From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 7703 invoked by alias); 24 Dec 2013 18:49:26 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 7690 invoked by uid 89); 24 Dec 2013 18:49:26 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.7 required=5.0 tests=AWL,BAYES_40,FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,MSGID_FROM_MTA_HEADER,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-pa0-f48.google.com Received: from mail-pa0-f48.google.com (HELO mail-pa0-f48.google.com) (209.85.220.48) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 24 Dec 2013 18:49:20 +0000 Received: by mail-pa0-f48.google.com with SMTP id rd3so6746333pab.7 for ; Tue, 24 Dec 2013 10:49:18 -0800 (PST) X-Received: by 10.67.1.70 with SMTP id be6mr34417184pad.9.1387910958756; Tue, 24 Dec 2013 10:49:18 -0800 (PST) Received: from sspiff.org (173-13-178-53-sfba.hfc.comcastbusiness.net. [173.13.178.53]) by mx.google.com with ESMTPSA id hz10sm43386751pbc.36.2013.12.24.10.49.15 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 24 Dec 2013 10:49:17 -0800 (PST) Message-ID: <52b9d72d.eac2440a.58aa.ffff8230@mx.google.com> Received: by sspiff.org (sSMTP sendmail emulation); Tue, 24 Dec 2013 10:48:52 -0800 Date: Tue, 24 Dec 2013 18:49:00 -0000 From: Doug Evans To: gdb-patches@sourceware.org Subject: [PATCH v3 02/15] extension language API for GDB: extension.[ch] X-IsSubscribed: yes X-SW-Source: 2013-12/txt/msg00908.txt.bz2 This patch adds extension.h, extension.c and extension-priv.h. extension.h provides the public API. The enums that were in python.h have been moved here and any py_,PY_ part of the name has been replaced to be non-python-specific. extension-priv.h provides the "private" API. This is what Python exports to GDB. extension.c defines extension_language_gdb for GDB's own extension/scripting language, and provides the functions GDB calls to call into an extension language. Changes from v2: - new enum ext_lang_rc - "methods" apply_type_printers, apply_val_printer, before_prompt updated to use it - function breakpoint_has_ext_lang_cond renamed to get_breakpoint_cond_ext_lang Changes from v1: - I renamed the files from scripting.[ch] per suggestion - more comments - more consistent enum prefixes [I'm still leaving off prefixes of values carried over as is from Python. A later pass can add them.] 2013-12-24 Doug Evans * Makefile.in (SFILES): Add extension.c. (HFILES_NO_SRCDIR): Add extension.h, extension-priv.h (COMMON_OBS): Add extension.o. * extension.h: New file. * extension-priv.h: New file. * extension.c: New file. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index be30dfd..838f27a 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -733,7 +733,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \ dwarf2-frame-tailcall.c \ elfread.c environ.c eval.c event-loop.c event-top.c \ - exceptions.c expprint.c \ + exceptions.c expprint.c extension.c \ f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \ findcmd.c findvar.c frame.c frame-base.c frame-unwind.c \ gdbarch.c arch-utils.c gdb_bfd.c gdb_obstack.c \ @@ -820,6 +820,7 @@ tui/tui-windata.h tui/tui-data.h tui/tui-win.h tui/tui-stack.h \ tui/tui-winsource.h tui/tui-regs.h tui/tui-io.h tui/tui-layout.h \ tui/tui-source.h sol2-tdep.h gregset.h sh-tdep.h sh64-tdep.h \ expression.h score-tdep.h gdb_select.h ser-tcp.h \ +extension.h extension-priv.h \ build-id.h buildsym.h valprint.h \ typeprint.h mi/mi-getopt.h mi/mi-parse.h mi/mi-console.h \ mi/mi-out.h mi/mi-main.h mi/mi-common.h mi/mi-cmds.h linux-nat.h \ @@ -906,6 +907,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ infcmd.o infrun.o \ expprint.o environ.o stack.o thread.o \ exceptions.o \ + extension.o \ filesystem.o \ filestuff.o \ inf-child.o \ diff --git a/gdb/extension.h b/gdb/extension.h new file mode 100644 index 0000000..b80c5ff --- /dev/null +++ b/gdb/extension.h @@ -0,0 +1,214 @@ +/* Interface between gdb and its extension languages. + + Copyright (C) 2013 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef EXTENSION_H +#define EXTENSION_H + +#include "mi/mi-cmds.h" /* For PRINT_NO_VALUES, etc. */ + +struct breakpoint; +struct command_line; +struct frame_info; +struct language_defn; +struct objfile; +struct extension_language_defn; +struct type; +struct ui_file; +struct ui_out; +struct value; +struct value_print_options; + +/* A function to load and process a script file. + The file has been opened and is ready to be read from the beginning. + Any exceptions are not caught, and are passed to the caller. */ +typedef void script_sourcer_func (const struct extension_language_defn *, + FILE *stream, const char *filename); + +/* A function to load and process a script for an objfile. + The file has been opened and is ready to be read from the beginning. + Any exceptions are not caught, and are passed to the caller. */ +typedef void objfile_script_sourcer_func + (const struct extension_language_defn *, + struct objfile *, FILE *stream, const char *filename); + +/* Enum of each extension(/scripting) language. */ + +enum extension_language + { + EXT_LANG_NONE, + EXT_LANG_GDB, + EXT_LANG_PYTHON + }; + +/* Extension language frame-filter status return values. */ + +enum ext_lang_bt_status + { + /* Return when an error has occurred in processing frame filters, + or when printing the stack. */ + EXT_LANG_BT_ERROR = -1, + + /* Return from internal routines to indicate that the function + succeeded. */ + EXT_LANG_BT_OK = 1, + + /* Return when the frame filter process is complete, and all + operations have succeeded. */ + EXT_LANG_BT_COMPLETED = 2, + + /* Return when the frame filter process is complete, but there + were no filter registered and enabled to process. */ + EXT_LANG_BT_NO_FILTERS = 3 + }; + +/* Flags to pass to apply_extlang_frame_filter. */ + +enum frame_filter_flags + { + /* Set this flag if frame level is to be printed. */ + PRINT_LEVEL = 1, + + /* Set this flag if frame information is to be printed. */ + PRINT_FRAME_INFO = 2, + + /* Set this flag if frame arguments are to be printed. */ + PRINT_ARGS = 4, + + /* Set this flag if frame locals are to be printed. */ + PRINT_LOCALS = 8, + }; + +/* A choice of the different frame argument printing strategies that + can occur in different cases of frame filter instantiation. */ + +enum ext_lang_frame_args + { + /* Print no values for arguments when invoked from the MI. */ + NO_VALUES = PRINT_NO_VALUES, + + MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES, + + /* Print only simple values (what MI defines as "simple") for + arguments when invoked from the MI. */ + MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES, + + /* Print only scalar values for arguments when invoked from the CLI. */ + CLI_SCALAR_VALUES, + + /* Print all values for arguments when invoked from the CLI. */ + CLI_ALL_VALUES + }; + +/* The possible results of + extension_language_ops.breakpoint_cond_says_stop. */ + +enum ext_lang_bp_stop + { + /* No "stop" condition is set. */ + EXT_LANG_BP_STOP_UNSET, + + /* A "stop" condition is set, and it says "don't stop". */ + EXT_LANG_BP_STOP_NO, + + /* A "stop" condition is set, and it says "stop". */ + EXT_LANG_BP_STOP_YES + }; + +/* Table of type printers associated with the global typedef table. */ + +struct ext_lang_type_printers +{ + /* Type-printers from Python. */ + void *py_type_printers; +}; + +/* The interface for gdb's own extension(/scripting) language. */ +extern const struct extension_language_defn extension_language_gdb; + +extern const struct extension_language_defn *get_ext_lang_defn + (enum extension_language lang); + +extern const struct extension_language_defn *get_ext_lang_of_file + (const char *file); + +extern int ext_lang_present_p (const struct extension_language_defn *); + +extern int ext_lang_initialized_p (const struct extension_language_defn *); + +extern void throw_ext_lang_unsupported + (const struct extension_language_defn *); + +/* Accessors for "public" attributes of the extension language definition. */ + +extern enum extension_language ext_lang_kind + (const struct extension_language_defn *); + +extern const char *ext_lang_name (const struct extension_language_defn *); + +extern const char *ext_lang_capitalized_name + (const struct extension_language_defn *); + +extern const char *ext_lang_suffix (const struct extension_language_defn *); + +extern const char *ext_lang_auto_load_suffix + (const struct extension_language_defn *); + +extern script_sourcer_func *ext_lang_script_sourcer + (const struct extension_language_defn *); + +extern objfile_script_sourcer_func *ext_lang_objfile_script_sourcer + (const struct extension_language_defn *); + +extern int ext_lang_auto_load_enabled (const struct extension_language_defn *); + +/* Wrappers for each extension language API function that iterate over all + extension languages. */ + +extern void finish_ext_lang_initialization (void); + +extern void eval_ext_lang_from_control_command (struct command_line *cmd); + +extern void auto_load_ext_lang_scripts_for_objfile (struct objfile *); + +extern struct ext_lang_type_printers *start_ext_lang_type_printers (void); + +extern char *apply_ext_lang_type_printers (struct ext_lang_type_printers *, + struct type *); + +extern void free_ext_lang_type_printers (struct ext_lang_type_printers *); + +extern int apply_ext_lang_val_pretty_printer + (struct type *type, const gdb_byte *valaddr, + int embedded_offset, CORE_ADDR address, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options, + const struct language_defn *language); + +extern enum ext_lang_bt_status apply_ext_lang_frame_filter + (struct frame_info *frame, int flags, enum ext_lang_frame_args args_type, + struct ui_out *out, int frame_low, int frame_high); + +extern void preserve_ext_lang_values (struct objfile *, htab_t copied_types); + +extern const struct extension_language_defn *get_breakpoint_cond_ext_lang + (struct breakpoint *b, enum extension_language skip_lang); + +extern int breakpoint_ext_lang_cond_says_stop (struct breakpoint *); + +#endif /* EXTENSION_H */ diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h new file mode 100644 index 0000000..5455161 --- /dev/null +++ b/gdb/extension-priv.h @@ -0,0 +1,249 @@ +/* Private implementation details of interface between gdb and its + extension languages. + + Copyright (C) 2013 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef EXTENSION_PRIV_H +#define EXTENSION_PRIV_H + +#include "extension.h" + +/* The return code for some API calls. */ + +enum ext_lang_rc + { + /* The operation completed successfully. */ + EXT_LANG_RC_OK, + + /* The operation was not performed (e.g., no pretty-printer). */ + EXT_LANG_RC_NOP, + + /* There was an error (e.g., Python error while printing a value). + When an error occurs no further extension languages are tried. + This is to preserve existing behaviour, and because it's convenient + for Python developers. + Note: This is different than encountering a memory error trying to read + a value for pretty-printing. Here we're referring to, e.g., programming + errors that trigger an exception in the extension language. */ + EXT_LANG_RC_ERROR + }; + +/* High level description of an extension/scripting language. + An entry for each is compiled into GDB regardless of whether the support + is present. This is done so that we can issue meaningful errors if the + support is not compiled in. */ + +struct extension_language_defn +{ + /* Enum of the extension language. */ + enum extension_language language; + + /* The name of the extension language, lowercase. E.g., python. */ + const char *name; + + /* The capitalized name of the extension language. + For python this is "Python". For gdb this is "GDB". */ + const char *capitalized_name; + + /* The file suffix of this extension language. E.g., ".py". */ + const char *suffix; + + /* The suffix of per-objfile scripts to auto-load. + E.g., When the program loads libfoo.so, look for libfoo.so-gdb.py. */ + const char *auto_load_suffix; + + /* We support embedding external extension language code in GDB's own + scripting language. We do this by having a special command that begins + the extension language snippet, and terminate it with "end". + This specifies the control type used to implement this. */ + enum command_control_type cli_control_type; + + /* A pointer to the "methods" to load scripts in this language, + or NULL if the support is not compiled into GDB. */ + const struct extension_language_script_ops *script_ops; + + /* Either a pointer to the "methods" of the extension language interface + or NULL if the support is not compiled into GDB. + This is also NULL for GDB's own scripting language which is relatively + primitive, and doesn't provide these features. */ + const struct extension_language_ops *ops; +}; + +/* The interface for loading scripts from external extension languages, + as well as GDB's own scripting language. + All of these methods are required to be implemented. */ + +struct extension_language_script_ops +{ + /* Load a script. This is called, e.g., via the "source" command. + If there's an error while processing the script this function may, + but is not required to, throw an error. */ + script_sourcer_func *script_sourcer; + + /* Load a script attached to an objfile. + If there's an error while processing the script this function may, + but is not required to, throw an error. */ + objfile_script_sourcer_func *objfile_script_sourcer; + + /* Return non-zero if auto-loading scripts in this extension language + is enabled. */ + int (*auto_load_enabled) (const struct extension_language_defn *); +}; + +/* The interface for making calls from GDB to an external extension + language. This is for non-script-loading related functionality, like + pretty-printing, etc. The reason these are separated out is GDB's own + scripting language makes use of extension_language_script_opts, but it + makes no use of these. There is no (current) intention to split + extension_language_ops up any further. + All of these methods are optional and may be NULL, except where + otherwise indicated. */ + +struct extension_language_ops +{ + /* Called at the end of gdb initialization to give the extension language + an opportunity to finish up. This is useful for things like adding + new commands where one has to wait until gdb itself is initialized. */ + void (*finish_initialization) (const struct extension_language_defn *); + + /* Return non-zero if the extension language successfully initialized. + This method is required. */ + int (*initialized) (const struct extension_language_defn *); + + /* Process a sequence of commands embedded in GDB's own scripting language. + E.g., + python + print 42 + end */ + void (*eval_from_control_command) (const struct extension_language_defn *, + struct command_line *); + + /* Type-printing support: + start_type_printers, apply_type_printers, free_type_printers. + These methods are optional and may be NULL, but if one of them is + implemented then they all must be. */ + + /* Called before printing a type. */ + void (*start_type_printers) (const struct extension_language_defn *, + struct ext_lang_type_printers *); + + /* Try to pretty-print TYPE. If successful the pretty-printed type is + stored in *PRETTIED_TYPE, and the caller must free it. + Returns EXT_LANG_RC_OK upon success, EXT_LANG_RC_NOP if the type + is not recognized, and EXT_LANG_RC_ERROR if an error was encountered. + This function has a bit of a funny name, since it actually applies + recognizers, but this seemed clearer given the start_type_printers + and free_type_printers functions. */ + enum ext_lang_rc (*apply_type_printers) + (const struct extension_language_defn *, + const struct ext_lang_type_printers *, + struct type *, char **prettied_type); + + /* Called after a type has been printed to give the type pretty-printer + mechanism an opportunity to clean up. */ + void (*free_type_printers) (const struct extension_language_defn *, + struct ext_lang_type_printers *); + + /* Try to pretty-print a value of type TYPE located at VALADDR + + EMBEDDED_OFFSET, which came from the inferior at address ADDRESS + + EMBEDDED_OFFSET, onto stdio stream STREAM according to OPTIONS. + VAL is the whole object that came from ADDRESS. VALADDR must point to + the head of VAL's contents buffer. + Returns EXT_LANG_RC_OK upon success, EXT_LANG_RC_NOP if the value + is not recognized, and EXT_LANG_RC_ERROR if an error was encountered. */ + enum ext_lang_rc (*apply_val_pretty_printer) + (const struct extension_language_defn *, + struct type *type, const gdb_byte *valaddr, + int embedded_offset, CORE_ADDR address, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options, + const struct language_defn *language); + + /* GDB access to the "frame filter" feature. + FRAME is the source frame to start frame-filter invocation. FLAGS is an + integer holding the flags for printing. The following elements of + the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS: + PRINT_LEVEL is a flag indicating whether to print the frame's + relative level in the output. PRINT_FRAME_INFO is a flag that + indicates whether this function should print the frame + information, PRINT_ARGS is a flag that indicates whether to print + frame arguments, and PRINT_LOCALS, likewise, with frame local + variables. ARGS_TYPE is an enumerator describing the argument + format, OUT is the output stream to print. FRAME_LOW is the + beginning of the slice of frames to print, and FRAME_HIGH is the + upper limit of the frames to count. Returns SCR_BT_ERROR on error, + or SCR_BT_COMPLETED on success. */ + enum ext_lang_bt_status (*apply_frame_filter) + (const struct extension_language_defn *, + struct frame_info *frame, int flags, enum ext_lang_frame_args args_type, + struct ui_out *out, int frame_low, int frame_high); + + /* Update values held by the extension language when OBJFILE is discarded. + New global types must be created for every such value, which must then be + updated to use the new types. + This function typically just iterates over all appropriate values and + calls preserve_one_value for each one. + COPIED_TYPES is used to prevent cycles / duplicates and is passed to + preserve_one_value. */ + void (*preserve_values) (const struct extension_language_defn *, + struct objfile *objfile, htab_t copied_types); + + /* Return non-zero if there is a stop condition for the breakpoint. + This is used to implement the restriction that a breakpoint may have + at most one condition. */ + int (*breakpoint_has_cond) (const struct extension_language_defn *, + struct breakpoint *); + + /* Return a value of enum ext_lang_bp_stop indicating if there is a stop + condition for the breakpoint, and if so whether the program should + stop. This is called when the program has stopped at the specified + breakpoint. + While breakpoints can have at most one condition, this is called for + every extension language, even if another extension language has a + "stop" method: other kinds of breakpoints may be implemented using + this method, e.g., "finish breakpoints" in Python. */ + enum ext_lang_bp_stop (*breakpoint_cond_says_stop) + (const struct extension_language_defn *, struct breakpoint *); + + /* The next three are used to connect gdb's SIGINT handling with the + extension language's. + These need not be implemented, but if one of them is implemented + then they all must be. */ + + /* Clear the SIGINT indicator. */ + void (*clear_quit_flag) (const struct extension_language_defn *); + + /* Set the SIGINT indicator. */ + void (*set_quit_flag) (const struct extension_language_defn *); + + /* Return non-zero if a SIGINT has occurred. + This is expected to also clear the indicator. */ + int (*check_quit_flag) (const struct extension_language_defn *); + + /* Called before gdb prints its prompt, giving extension languages an + opportunity to change it with set_prompt. + Returns EXT_LANG_RC_OK if the prompt was changed, EXT_LANG_RC_NOP if + the prompt was not changed, and EXT_LANG_RC_ERROR if an error was + encountered. + Extension languages are called in order, and once the prompt is + changed or an error occurs no further languages are called. */ + enum ext_lang_rc (*before_prompt) (const struct extension_language_defn *, + const char *current_gdb_prompt); +}; + +#endif /* EXTENSION_PRIV_H */ diff --git a/gdb/extension.c b/gdb/extension.c new file mode 100644 index 0000000..36537c6 --- /dev/null +++ b/gdb/extension.c @@ -0,0 +1,753 @@ +/* Interface between gdb and its extension languages. + + Copyright (C) 2013 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Note: With few exceptions, external functions and variables in this file + have "ext_lang" in the name, and no other symbol in gdb does. */ + +#include "defs.h" +#include "auto-load.h" +#include "breakpoint.h" +#include "extension.h" +#include "extension-priv.h" +#include "observer.h" +#include "cli/cli-script.h" +#include "python/python.h" + +/* Iterate over all external extension languages, regardless of whether the + support has been compiled in or not. + This does not include GDB's own scripting language. */ + +#define ALL_EXTENSION_LANGUAGES(i, extlang) \ + for (/*int*/ i = 0, extlang = extension_languages[0]; \ + extlang != NULL; \ + extlang = extension_languages[++i]) + +/* Iterate over all external extension languages that are supported. + This does not include GDB's own scripting language. */ + +#define ALL_ENABLED_EXTENSION_LANGUAGES(i, extlang) \ + for (/*int*/ i = 0, extlang = extension_languages[0]; \ + extlang != NULL; \ + extlang = extension_languages[++i]) \ + if (extlang->ops != NULL) + +static script_sourcer_func source_gdb_script; +static objfile_script_sourcer_func source_gdb_objfile_script; + +/* GDB's own scripting language. + This exists, in part, to support auto-loading ${prog}-gdb.gdb scripts. */ + +static const struct extension_language_script_ops + extension_language_gdb_script_ops = +{ + source_gdb_script, + source_gdb_objfile_script, + auto_load_gdb_scripts_enabled +}; + +const struct extension_language_defn extension_language_gdb = +{ + EXT_LANG_GDB, + "gdb", + "GDB", + + /* We fall back to interpreting a script as a GDB script if it doesn't + match the other scripting languages, but for consistency's sake + give it a formal suffix. */ + ".gdb", + "-gdb.gdb", + + /* cli_control_type: This is never used: GDB's own scripting language + has a variety of control types (if, while, etc.). */ + commands_control, + + &extension_language_gdb_script_ops, + + /* The rest of the extension language interface isn't supported by GDB's own + extension/scripting language. */ + NULL +}; + +/* NULL-terminated table of all external (non-native) extension languages. + + The order of appearance in the table is important. + When multiple extension languages provide the same feature, for example + a pretty-printer for a particular type, which one gets used? + The algorithm employed here is "the first one wins". For example, in + the case of pretty-printers this means the first one to provide a + pretty-printed value is the one that is used. This algorithm is employed + throughout. */ + +static const struct extension_language_defn * const extension_languages[] = +{ + /* To preserve existing behaviour, python should always appear first. */ + &extension_language_python, + NULL +}; + +/* Return a pointer to the struct extension_language_defn object of + extension language LANG. + This always returns a non-NULL pointer, even if support for the language + is not compiled into this copy of GDB. */ + +const struct extension_language_defn * +get_ext_lang_defn (enum extension_language lang) +{ + int i; + const struct extension_language_defn *extlang; + + gdb_assert (lang != EXT_LANG_NONE); + + if (lang == EXT_LANG_GDB) + return &extension_language_gdb; + + ALL_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->language == lang) + return extlang; + } + + gdb_assert_not_reached ("unable to find extension_language_defn"); +} + +/* Return TRUE if FILE has extension EXTENSION. */ + +static int +has_extension (const char *file, const char *extension) +{ + int file_len = strlen (file); + int extension_len = strlen (extension); + + return (file_len > extension_len + && strcmp (&file[file_len - extension_len], extension) == 0); +} + +/* Return the extension language of FILE, or NULL if + the extension language of FILE is not recognized. + This is done by looking at the file's suffix. */ + +const struct extension_language_defn * +get_ext_lang_of_file (const char *file) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_EXTENSION_LANGUAGES (i, extlang) + { + if (has_extension (file, extlang->suffix)) + return extlang; + } + + return NULL; +} + +/* Return non-zero if support for the specified extension language + is compiled in. */ + +int +ext_lang_present_p (const struct extension_language_defn *extlang) +{ + return extlang->script_ops != NULL; +} + +/* Return non-zero if the specified extension language has successfully + initialized. */ + +int +ext_lang_initialized_p (const struct extension_language_defn *extlang) +{ + if (extlang->ops != NULL) + { + /* This method is required. */ + gdb_assert (extlang->ops->initialized != NULL); + return extlang->ops->initialized (extlang); + } + + return 0; +} + +/* Throw an error indicating EXTLANG is not supported in this copy of GDB. */ + +void +throw_ext_lang_unsupported (const struct extension_language_defn *extlang) +{ + error (_("Scripting in the \"%s\" language is not supported" + " in this copy of GDB."), + ext_lang_capitalized_name (extlang)); +} + +/* Methods for GDB's own extension/scripting language. */ + +/* The extension_language_script_ops.script_sourcer "method". */ + +static void +source_gdb_script (const struct extension_language_defn *extlang, + FILE *stream, const char *file) +{ + script_from_file (stream, file); +} + +/* The extension_language_script_ops.objfile_script_sourcer "method". */ + +static void +source_gdb_objfile_script (const struct extension_language_defn *extlang, + struct objfile *objfile, + FILE *stream, const char *file) +{ + script_from_file (stream, file); +} + +/* Accessors for "public" attributes of struct extension_language. */ + +/* Return the "name" field of EXTLANG. */ + +const char * +ext_lang_name (const struct extension_language_defn *extlang) +{ + return extlang->name; +} + +/* Return the "capitalized_name" field of EXTLANG. */ + +const char * +ext_lang_capitalized_name (const struct extension_language_defn *extlang) +{ + return extlang->capitalized_name; +} + +/* Return the "suffix" field of EXTLANG. */ + +const char * +ext_lang_suffix (const struct extension_language_defn *extlang) +{ + return extlang->suffix; +} + +/* Return the "auto_load_suffix" field of EXTLANG. */ + +const char * +ext_lang_auto_load_suffix (const struct extension_language_defn *extlang) +{ + return extlang->auto_load_suffix; +} + +/* extension_language_script_ops wrappers. */ + +/* Return the script "sourcer" function for EXTLANG. + This is the function that loads and processes a script. + If support for this language isn't compiled in, NULL is returned. */ + +script_sourcer_func * +ext_lang_script_sourcer (const struct extension_language_defn *extlang) +{ + if (extlang->script_ops == NULL) + return NULL; + + /* The extension language is required to implement this function. */ + gdb_assert (extlang->script_ops->script_sourcer != NULL); + + return extlang->script_ops->script_sourcer; +} + +/* Return the objfile script "sourcer" function for EXTLANG. + This is the function that loads and processes a script for a particular + objfile. + If support for this language isn't compiled in, NULL is returned. */ + +objfile_script_sourcer_func * +ext_lang_objfile_script_sourcer (const struct extension_language_defn *extlang) +{ + if (extlang->script_ops == NULL) + return NULL; + + /* The extension language is required to implement this function. */ + gdb_assert (extlang->script_ops->objfile_script_sourcer != NULL); + + return extlang->script_ops->objfile_script_sourcer; +} + +/* Return non-zero if auto-loading of EXTLANG scripts is enabled. + Zero is returned if support for this language isn't compiled in. */ + +int +ext_lang_auto_load_enabled (const struct extension_language_defn *extlang) +{ + if (extlang->script_ops == NULL) + return 0; + + /* The extension language is required to implement this function. */ + gdb_assert (extlang->script_ops->auto_load_enabled != NULL); + + return extlang->script_ops->auto_load_enabled (extlang); +} + +/* Functions that iterate over all extension languages. + These only iterate over external extension languages, not including + GDB's own extension/scripting language, unless otherwise indicated. */ + +/* Wrapper to call the extension_language_ops.finish_initialization "method" + for each compiled-in extension language. */ + +void +finish_ext_lang_initialization (void) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->finish_initialization != NULL) + extlang->ops->finish_initialization (extlang); + } +} + +/* Invoke the appropriate extension_language_ops.eval_from_control_command + method to perform CMD, which is a list of commands in an extension language. + + This function is what implements, for example: + + python + print 42 + end + + in a GDB script. */ + +void +eval_ext_lang_from_control_command (struct command_line *cmd) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->cli_control_type == cmd->control_type) + { + if (extlang->ops->eval_from_control_command != NULL) + { + extlang->ops->eval_from_control_command (extlang, cmd); + return; + } + /* The requested extension language is not supported in this GDB. */ + throw_ext_lang_unsupported (extlang); + } + } + + gdb_assert_not_reached ("unknown extension language in command_line"); +} + +/* Search for and load scripts for OBJFILE written in extension languages. + This includes GDB's own scripting language. + + This function is what implements the loading of OBJFILE-gdb.py and + OBJFILE-gdb.gdb. */ + +void +auto_load_ext_lang_scripts_for_objfile (struct objfile *objfile) +{ + int i; + const struct extension_language_defn *extlang; + + extlang = &extension_language_gdb; + if (ext_lang_auto_load_enabled (extlang)) + auto_load_objfile_script (objfile, extlang); + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (ext_lang_auto_load_enabled (extlang)) + auto_load_objfile_script (objfile, extlang); + } +} + +/* Interface to type pretty-printers implemented in an extension language. */ + +/* Call this at the start when preparing to pretty-print a type. + The result is a pointer to an opaque object (to the caller) to be passed + to apply_ext_lang_type_printers and free_ext_lang_type_printers. + + We don't know in advance which extension language will provide a + pretty-printer for the type, so all are initialized. */ + +struct ext_lang_type_printers * +start_ext_lang_type_printers (void) +{ + struct ext_lang_type_printers *printers + = XZALLOC (struct ext_lang_type_printers); + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->start_type_printers != NULL) + extlang->ops->start_type_printers (extlang, printers); + } + + return printers; +} + +/* Iteratively try the type pretty-printers specified by PRINTERS + according to the standard search order (specified by extension_languages), + returning the result of the first one that succeeds. + If there was an error, or if no printer succeeds, then NULL is returned. */ + +char * +apply_ext_lang_type_printers (struct ext_lang_type_printers *printers, + struct type *type) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + char *result = NULL; + enum ext_lang_rc rc; + + if (extlang->ops->apply_type_printers == NULL) + continue; + rc = extlang->ops->apply_type_printers (extlang, printers, type, + &result); + switch (rc) + { + case EXT_LANG_RC_OK: + gdb_assert (result != NULL); + return result; + case EXT_LANG_RC_ERROR: + return NULL; + case EXT_LANG_RC_NOP: + break; + default: + gdb_assert_not_reached ("bad return from apply_type_printers"); + } + } + + return NULL; +} + +/* Call this after pretty-printing a type to release all memory held + by PRINTERS. */ + +void +free_ext_lang_type_printers (struct ext_lang_type_printers *printers) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->free_type_printers != NULL) + extlang->ops->free_type_printers (extlang, printers); + } + + xfree (printers); +} + +/* Try to pretty-print a value of type TYPE located at VALADDR + + EMBEDDED_OFFSET, which came from the inferior at address ADDRESS + + EMBEDDED_OFFSET, onto stdio stream STREAM according to OPTIONS. + VAL is the whole object that came from ADDRESS. VALADDR must point to + the head of VAL's contents buffer. + Returns non-zero if the value was successfully pretty-printed. + + Extension languages are tried in the order specified by + extension_languages. The first one to provide a pretty-printed + value "wins". + + If an error is encountered in a pretty-printer, no further extension + languages are tried. + Note: This is different than encountering a memory error trying to read a + value for pretty-printing. Here we're referring to, e.g., programming + errors that trigger an exception in the extension language. */ + +int +apply_ext_lang_val_pretty_printer (struct type *type, const gdb_byte *valaddr, + int embedded_offset, CORE_ADDR address, + struct ui_file *stream, int recurse, + const struct value *val, + const struct value_print_options *options, + const struct language_defn *language) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + enum ext_lang_rc rc; + + if (extlang->ops->apply_val_pretty_printer == NULL) + continue; + rc = extlang->ops->apply_val_pretty_printer (extlang, type, valaddr, + embedded_offset, address, + stream, recurse, val, + options, language); + switch (rc) + { + case EXT_LANG_RC_OK: + return 1; + case EXT_LANG_RC_ERROR: + return 0; + case EXT_LANG_RC_NOP: + break; + default: + gdb_assert_not_reached ("bad return from apply_val_pretty_printer"); + } + } + + return 0; +} + +/* GDB access to the "frame filter" feature. + FRAME is the source frame to start frame-filter invocation. FLAGS is an + integer holding the flags for printing. The following elements of + the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS: + PRINT_LEVEL is a flag indicating whether to print the frame's + relative level in the output. PRINT_FRAME_INFO is a flag that + indicates whether this function should print the frame + information, PRINT_ARGS is a flag that indicates whether to print + frame arguments, and PRINT_LOCALS, likewise, with frame local + variables. ARGS_TYPE is an enumerator describing the argument + format, OUT is the output stream to print. FRAME_LOW is the + beginning of the slice of frames to print, and FRAME_HIGH is the + upper limit of the frames to count. Returns EXT_LANG_BT_ERROR on error, + or EXT_LANG_BT_COMPLETED on success. + + Extension languages are tried in the order specified by + extension_languages. The first one to provide a filter "wins". + If there is an error (EXT_LANG_BT_ERROR) it is reported immediately + rather than trying filters in other extension languages. */ + +enum ext_lang_bt_status +apply_ext_lang_frame_filter (struct frame_info *frame, int flags, + enum ext_lang_frame_args args_type, + struct ui_out *out, + int frame_low, int frame_high) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + enum ext_lang_bt_status status; + + if (extlang->ops->apply_frame_filter == NULL) + continue; + status = extlang->ops->apply_frame_filter (extlang, frame, flags, + args_type, out, + frame_low, frame_high); + /* We use the filters from the first extension language that has + applicable filters. Also, an error is reported immediately + rather than continue trying. */ + if (status != EXT_LANG_BT_NO_FILTERS) + return status; + } + + return EXT_LANG_BT_NO_FILTERS; +} + +/* Update values held by the extension language when OBJFILE is discarded. + New global types must be created for every such value, which must then be + updated to use the new types. + The function typically just iterates over all appropriate values and + calls preserve_one_value for each one. + COPIED_TYPES is used to prevent cycles / duplicates and is passed to + preserve_one_value. */ + +void +preserve_ext_lang_values (struct objfile *objfile, htab_t copied_types) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->preserve_values != NULL) + extlang->ops->preserve_values (extlang, objfile, copied_types); + } +} + +/* If there is a stop condition implemented in an extension language for + breakpoint B, return a pointer to the extension language's definition. + Otherwise return NULL. + If SKIP_LANG is not EXT_LANG_NONE, skip checking this language. + This is for the case where we're setting a new condition: Only one + condition is allowed, so when setting a condition for any particular + extension language, we need to check if any other extension language + already has a condition set. */ + +const struct extension_language_defn * +get_breakpoint_cond_ext_lang (struct breakpoint *b, + enum extension_language skip_lang) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->language != skip_lang + && extlang->ops->breakpoint_has_cond != NULL + && extlang->ops->breakpoint_has_cond (extlang, b)) + return extlang; + } + + return NULL; +} + +/* Return whether a stop condition for breakpoint B says to stop. + True is also returned if there is no stop condition for B. */ + +int +breakpoint_ext_lang_cond_says_stop (struct breakpoint *b) +{ + int i; + const struct extension_language_defn *extlang; + enum ext_lang_bp_stop stop = EXT_LANG_BP_STOP_UNSET; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + /* There is a rule that a breakpoint can have at most one of any of a + CLI or extension language condition. However, Python hacks in "finish + breakpoints" on top of the "stop" check, so we have to call this for + every language, even if we could first determine whether a "stop" + method exists. */ + if (extlang->ops->breakpoint_cond_says_stop != NULL) + { + enum ext_lang_bp_stop this_stop + = extlang->ops->breakpoint_cond_says_stop (extlang, b); + + if (this_stop != EXT_LANG_BP_STOP_UNSET) + { + /* Even though we have to check every extension language, only + one of them can return yes/no (because only one of them + can have a "stop" condition). */ + gdb_assert (stop == EXT_LANG_BP_STOP_UNSET); + stop = this_stop; + } + } + } + + return stop == EXT_LANG_BP_STOP_NO ? 0 : 1; +} + +/* ^C/SIGINT support. + This requires cooperation with the extension languages so the support + is defined here. + The prototypes for these are in defs.h. */ + +/* Nonzero means a quit has been requested. + This flag tracks quit requests but it's only used if the extension language + doesn't provide the necessary support. */ +static int quit_flag; + +/* Clear the quit flag. */ + +void +clear_quit_flag (void) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->clear_quit_flag != NULL) + extlang->ops->clear_quit_flag (extlang); + } + + quit_flag = 0; +} + +/* Set the quit flag. */ + +void +set_quit_flag (void) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->set_quit_flag != NULL) + extlang->ops->set_quit_flag (extlang); + } + + quit_flag = 1; +} + +/* Return true if the quit flag has been set, false otherwise. + Extension languages may need their own control over whether SIGINT has + been seen. */ + +int +check_quit_flag (void) +{ + int i, result = 0; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + if (extlang->ops->check_quit_flag != NULL) + if (extlang->ops->check_quit_flag (extlang) != 0) + result = 1; + } + + /* This is written in a particular way to avoid races. */ + if (quit_flag) + { + quit_flag = 0; + result = 1; + } + + return result; +} + +/* Called via an observer before gdb prints its prompt. + Iterate over the extension languages giving them a chance to + change the prompt. The first one to change the prompt wins, + and no further languages are tried. */ + +static void +ext_lang_before_prompt (const char *current_gdb_prompt) +{ + int i; + const struct extension_language_defn *extlang; + + ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang) + { + enum ext_lang_rc rc; + + if (extlang->ops->before_prompt == NULL) + continue; + rc = extlang->ops->before_prompt (extlang, current_gdb_prompt); + switch (rc) + { + case EXT_LANG_RC_OK: + case EXT_LANG_RC_ERROR: + return; + case EXT_LANG_RC_NOP: + break; + default: + gdb_assert_not_reached ("bad return from before_prompt"); + } + } +} + +extern initialize_file_ftype _initialize_extension; + +void +_initialize_extension (void) +{ + observer_attach_before_prompt (ext_lang_before_prompt); +}