* [RFC][python] Add support for convenience functions implemented in Python @ 2009-02-02 13:16 Thiago Jung Bauermann 2009-02-02 19:49 ` Eli Zaretskii 2009-02-04 1:07 ` Thiago Jung Bauermann 0 siblings, 2 replies; 8+ messages in thread From: Thiago Jung Bauermann @ 2009-02-02 13:16 UTC (permalink / raw) To: gdb-patches ml Hi, This patch introduces the concept of convenience functions, and allows them to be implemented in Python. Convenience functions are stored in convenience variables, and from the user POV behave just like inferior function calls, but they are executed by GDB instead. This patch assumes that the patch for commands in Python has been applied. Regtested on ppc-linux. WDYT? -- []'s Thiago Jung Bauermann IBM Linux Technology Center gdb/ 2009-02-02 Tom Tromey <tromey@redhat.com> * Makefile.in (SUBDIR_PYTHON_OBS): Add python-function.o. (SUBDIR_PYTHON_SRCS): Add python-function.c. (python-function.o): New target. * eval.c: Include "python/python.h" and <ctype.h>. (evaluate_subexp_standard): Handle values of type TYPE_CODE_INTERNAL_FUNCTION. * gdbtypes.h (type_code): Add TYPE_CODE_INTERNAL_FUNCTION. * parse.c (write_exp_string): Remove duplicate word in comment. * python/python-function.c: New file. * python/python-internal.h (gdbpy_initialize_functions): Add prototype. * python/python.c (_initialize_python): Call gdbpy_initialize_functions. * valprint.c (value_check_printable): Handle values of type TYPE_CODE_INTERNAL_FUNCTION. * value.c: Include "cli/cli-decode.h". (internal_function): New struct. (functionlist, internal_fn_type): New static variables. (lookup_only_internalvar, create_internalvar, lookup_internalvar): Add const qualifier to name argument. (set_internal_var): Fix typo in comment. (value_create_internal_function, value_internal_function_name, call_internal_function, function_command, function_destroyer, add_internal_function): New functions. (_initialize_values): Create `function' placeholder command. Initialize internal_fn_type. * value.h (lookup_only_internalvar, create_internalvar, lookup_internalvar): Add const qualifier to name argument. (internal_function_fn, add_internal_function, call_internal_function, value_internal_function_name): Add prototypes. gdb/doc/ 2008-02-02 Tom Tromey <tromey@redhat.com> * gdb.texinfo (Convenience Vars): Document convenience functions. (Functions In Python): New node. (Python API): Update. gdb/testsuite/ 2009-02-02 Thiago Jung Bauermann <bauerman@br.ibm.com> * gdb.python/python-function.exp: New file. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 7400702..5cb8fec 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -271,11 +271,13 @@ SUBDIR_TUI_CFLAGS= \ SUBDIR_PYTHON_OBS = \ python.o \ python-cmd.o \ + python-function.o \ python-utils.o \ python-value.o SUBDIR_PYTHON_SRCS = \ python/python.c \ python/python-cmd.c \ + python/python-function.c \ python/python-utils.c \ python/python-value.c SUBDIR_PYTHON_DEPS = @@ -1842,6 +1844,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c $(POSTCOMPILE) +python-function.o: $(srcdir)/python/python-function.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c + $(POSTCOMPILE) + python-utils.o: $(srcdir)/python/python-utils.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c $(POSTCOMPILE) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 45e3a91..aff4e4d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7354,6 +7354,20 @@ On HP-UX systems, if you refer to a function or variable name that begins with a dollar sign, @value{GDBN} searches for a user or system name first, before it searches for a convenience variable. +@cindex convenience functions +@value{GDBN} also supplies some @dfn{convenience functions}. These +have a syntax similar to convenience variables. A convenience +function can be used in an expression just like an ordinary function; +however, a convenience function is implemented internally to +@value{GDBN}. + +@table @code +@item help function +@kindex help function +@cindex show all convenience functions +Print a list of all convenience functions. +@end table + @node Registers @section Registers @@ -18065,6 +18079,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Exception Handling:: * Values From Inferior:: * Commands In Python:: Implementing new commands in Python. +* Functions In Python:: Writing new convenience functions. @end menu @node Basic Python @@ -18463,6 +18478,65 @@ registration of the command with @value{GDBN}. Depending on how the Python code is read into @value{GDBN}, you may need to import the @code{gdb} module explicitly. +@node Functions In Python +@subsubsection Writing new convenience functions + +@cindex writing convenience functions +@cindex convenience functions in python +@cindex python convenience functions +@tindex gdb.Function +@tindex Function +You can implement new convenience functions (@pxref{Convenience Vars}) +in Python. A convenience function is an instance of a subclass of the +class @code{gdb.Function}. + +@defmethod Function __init__ name +The initializer for @code{Function} registers the new function with +@value{GDBN}. The @var{name} argument is the name of the function. +This must be a string. The function is made visible to the user as a +convenience variable, of ``internal function'' type, whose name is the +same as @var{name}. + +The documentation for the new function is taken from the documentation +string for the new class. +@end defmethod + +@defmethod Function invoke @var{*args} +When a convenience function is evaluated, its arguments are converted +to instances of @code{gdb.Value}, and then the function's +@code{invoke} method is called. Note that @value{GDBN} does not +predetermine the arity of convenience functions. Instead, all +available arguments are passed to @code{invoke}, following the +standard Python calling convention. In particular, a convenience +function can have default values for parameters without ill effect. + +The return value of this method is used as its value in the enclosing +expression. If an ordinary Python value is returned, it is converted +to a @code{gdb.Value} following the usual rules. +@end defmethod + +The following code snippet shows how a trivial convenience function can +be implemented in Python: + +@smallexample +class Greet (gdb.Function): + """Return string to greet someone. +Takes a name as argument.""" + + def __init__ (self): + super (Greet, self).__init__ ("greet") + + def invoke (self, name): + return "Hello, %s!" % name.string () + +Greet () +@end smallexample + +The last line instantiates the class, and is necessary to trigger the +registration of the function with @value{GDBN}. Depending on how the +Python code is read into @value{GDBN}, you may need to import the +@code{gdb} module explicitly. + @node Interpreters @chapter Command Interpreters @cindex command interpreters diff --git a/gdb/eval.c b/gdb/eval.c index 038334b..70ed369 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -40,9 +40,12 @@ #include "regcache.h" #include "user-regs.h" #include "valprint.h" +#include "python/python.h" #include "gdb_assert.h" +#include <ctype.h> + /* This is defined in valops.c */ extern int overload_resolution; @@ -1512,6 +1515,9 @@ evaluate_subexp_standard (struct type *expect_type, else error (_("Expression of type other than \"Function returning ...\" used as function")); } + if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_INTERNAL_FUNCTION) + return call_internal_function (argvec[0], nargs, argvec + 1); + return call_function_by_hand (argvec[0], nargs, argvec + 1); /* pai: FIXME save value from call_function_by_hand, then adjust pc by adjust_fn_pc if +ve */ diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index e78d3cf..eaf906c 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -134,7 +134,10 @@ enum type_code TYPE_CODE_NAMESPACE, /* C++ namespace. */ - TYPE_CODE_DECFLOAT /* Decimal floating point. */ + TYPE_CODE_DECFLOAT, /* Decimal floating point. */ + + /* Internal function type. */ + TYPE_CODE_INTERNAL_FUNCTION }; /* For now allow source to use TYPE_CODE_CLASS for C++ classes, as an diff --git a/gdb/parse.c b/gdb/parse.c index eee1f8e..dc2ccb1 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -306,7 +306,7 @@ write_exp_elt_intern (struct internalvar *expelt) strings with embedded null bytes, as is required for some languages. Don't be fooled by the fact that the string is null byte terminated, - this is strictly for the convenience of debugging gdb itself. Gdb + this is strictly for the convenience of debugging gdb itself. Gdb does not depend up the string being null terminated, since the actual length is recorded in expression elements at each end of the string. The null byte is taken into consideration when computing how diff --git a/gdb/python/python-function.c b/gdb/python/python-function.c new file mode 100644 index 0000000..d92195f --- /dev/null +++ b/gdb/python/python-function.c @@ -0,0 +1,188 @@ +/* Convenience functions implemented in Python. + + Copyright (C) 2008, 2009 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 <http://www.gnu.org/licenses/>. */ + + +#include "defs.h" +#include "value.h" +#include "exceptions.h" +#include "python-internal.h" +#include "charset.h" +#include "gdbcmd.h" +#include "cli/cli-decode.h" +#include "completer.h" +#include "expression.h" + +static PyTypeObject fnpy_object_type; + +\f + +static PyObject * +convert_values_to_python (int argc, struct value **argv) +{ + int i; + PyObject *result = PyTuple_New (argc); + for (i = 0; i < argc; ++i) + { + PyObject *elt = value_to_value_object (argv[i]); + if (! elt) + { + Py_DECREF (result); + error (_("Could not convert value to Python object.")); + } + PyTuple_SetItem (result, i, elt); + } + return result; +} + +/* Call a Python function object's invoke method. */ + +static struct value * +fnpy_call (void *cookie, int argc, struct value **argv) +{ + int i; + struct value *value = NULL; + PyObject *result, *callable, *args; + volatile struct gdb_exception except; + struct cleanup *cleanup; + PyGILState_STATE state; + + state = PyGILState_Ensure (); + cleanup = make_cleanup_py_restore_gil (&state); + + args = convert_values_to_python (argc, argv); + + callable = PyObject_GetAttrString ((PyObject *) cookie, "invoke"); + if (! callable) + { + Py_DECREF (args); + error (_("No method named 'invoke' in object.")); + } + + result = PyObject_Call (callable, args, NULL); + Py_DECREF (callable); + Py_DECREF (args); + + if (!result) + { + gdbpy_print_stack (); + error(_("Error while executing Python code.")); + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + value = convert_value_from_python (result); + } + if (except.reason < 0) + { + Py_DECREF (result); + gdbpy_print_stack (); + throw_exception (except); + } + + Py_DECREF (result); + do_cleanups (cleanup); + + return value; +} + +/* Called when destroying a struct internal_function. */ + +static void +fnpy_destroy (void *cookie) +{ + PyGILState_STATE state; + + state = PyGILState_Ensure (); + Py_DECREF ((PyObject *) cookie); + PyGILState_Release (state); + +} + +/* Initializer for a Function object. It takes one argument, the name + of the function. */ + +static int +fnpy_init (PyObject *self, PyObject *args, PyObject *kwds) +{ + char *name, *docstring = NULL; + if (! PyArg_ParseTuple (args, "s", &name)) + return -1; + Py_INCREF (self); + + if (PyObject_HasAttrString (self, "__doc__")) + { + PyObject *ds_obj = PyObject_GetAttrString (self, "__doc__"); + if (ds_obj && gdbpy_is_string (ds_obj)) + /* Nothing ever frees this. */ + docstring = python_string_to_host_string (ds_obj); + } + if (! docstring) + docstring = _("This function is not documented."); + + add_internal_function (name, docstring, fnpy_call, self, fnpy_destroy); + return 0; +} + +/* Initialize internal function support. */ + +void +gdbpy_initialize_functions (void) +{ + fnpy_object_type.tp_new = PyType_GenericNew; + fnpy_object_type.tp_init = fnpy_init; + if (PyType_Ready (&fnpy_object_type) < 0) + return; + Py_INCREF (&fnpy_object_type); + PyModule_AddObject (gdb_module, "Function", (PyObject *) &fnpy_object_type); +} + +\f + +static PyTypeObject fnpy_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Function", /*tp_name*/ + sizeof (PyObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB function object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0 /* tp_methods */ +}; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 02dbfc4..463f08e 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -71,6 +71,7 @@ struct value *convert_value_from_python (PyObject *obj); void gdbpy_initialize_values (void); void gdbpy_initialize_commands (void); +void gdbpy_initialize_functions (void); struct cleanup *make_cleanup_py_decref (PyObject *py); struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state); diff --git a/gdb/python/python.c b/gdb/python/python.c index 4f97416..c6c3e2a 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -410,6 +410,7 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_values (); gdbpy_initialize_commands (); + gdbpy_initialize_functions (); PyRun_SimpleString ("import gdb"); diff --git a/gdb/testsuite/gdb.python/python-function.exp b/gdb/testsuite/gdb.python/python-function.exp new file mode 100644 index 0000000..3ffc5aa --- /dev/null +++ b/gdb/testsuite/gdb.python/python-function.exp @@ -0,0 +1,65 @@ +# Copyright (C) 2009 Free Software Foundation, Inc. + +# 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 <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing convenience functions to Python. + +if $tracelevel then { + strace $tracelevel +} + +# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}... +# Run a test named NAME, consisting of multiple lines of input. +# After each input line INPUT, search for result line RESULT. +# Succeed if all results are seen; fail otherwise. +proc gdb_py_test_multiple {name args} { + global gdb_prompt + foreach {input result} $args { + if {[gdb_test_multiple $input "$name - $input" { + -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" { + pass "$name - $input" + } + }]} { + return 1 + } + } + return 0 +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +gdb_test_multiple "python print 'hello, world!'" "verify python support" { + -re "not supported.*$gdb_prompt $" { + unsupported "python support is disabled" + return -1 + } + -re "$gdb_prompt $" {} +} + +gdb_py_test_multiple "input convenience function" \ + "python" "" \ + "class test_func (gdb.Function):" "" \ + " def __init__ (self):" "" \ + " super (test_func, self).__init__ (\"test_func\")" "" \ + " def invoke (self, arg):" "" \ + " return \"test_func output, arg = %s\" % arg.string ()" "" \ + "test_func ()" "" \ + "end" "" + +gdb_test "print \$test_func (\"ugh\")" "= \"test_func output, arg = ugh\"" "call function" diff --git a/gdb/valprint.c b/gdb/valprint.c index d13dc4c..99e5da7 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -287,6 +287,13 @@ value_check_printable (struct value *val, struct ui_file *stream) return 0; } + if (TYPE_CODE (value_type (val)) == TYPE_CODE_INTERNAL_FUNCTION) + { + fprintf_filtered (stream, _("<internal function %s>"), + value_internal_function_name (val)); + return 0; + } + return 1; } diff --git a/gdb/value.c b/gdb/value.c index f0c8463..521a50b 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -37,6 +37,7 @@ #include "dfp.h" #include "objfiles.h" #include "valprint.h" +#include "cli/cli-decode.h" #include "python/python.h" @@ -44,6 +45,27 @@ void _initialize_values (void); +/* Definition of a user function. */ +struct internal_function +{ + /* The name of the function. It is a bit odd to have this in the + function itself -- the user might use a differently-named + convenience variable to hold the function. */ + char *name; + + /* The handler. */ + internal_function_fn handler; + + /* User data for the handler. */ + void *cookie; + + /* Function called to destroy the cookie when the function object is + destroyed. */ + void (*destroyer) (void *cookie); +}; + +static struct cmd_list_element *functionlist; + struct value { /* Type of value; either not an lval, or one of the various @@ -194,6 +216,10 @@ struct value_history_chunk static struct value_history_chunk *value_history_chain; static int value_history_count; /* Abs number of last entry stored */ + +/* The type of internal functions. */ + +static struct type *internal_fn_type; \f /* List of all value objects currently allocated (except for those released by calls to release_value) @@ -814,7 +840,7 @@ init_if_undefined_command (char* args, int from_tty) the return value is NULL. */ struct internalvar * -lookup_only_internalvar (char *name) +lookup_only_internalvar (const char *name) { struct internalvar *var; @@ -830,7 +856,7 @@ lookup_only_internalvar (char *name) NAME should not normally include a dollar sign. */ struct internalvar * -create_internalvar (char *name) +create_internalvar (const char *name) { struct internalvar *var; var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); @@ -851,7 +877,7 @@ create_internalvar (char *name) one is created, with a void value. */ struct internalvar * -lookup_internalvar (char *name) +lookup_internalvar (const char *name) { struct internalvar *var; @@ -938,7 +964,7 @@ set_internalvar (struct internalvar *var, struct value *val) /* Begin code which must not call error(). If var->value points to something free'd, an error() obviously leaves a dangling pointer. - But we also get a danling pointer if var->value points to + But we also get a dangling pointer if var->value points to something in the value chain (i.e., before release_value is called), because after the error free_all_values will get called before long. */ @@ -955,6 +981,85 @@ internalvar_name (struct internalvar *var) return var->name; } +static struct value * +value_create_internal_function (const char *name, + internal_function_fn handler, + void *cookie, + void (*destroyer) (void *)) +{ + struct value *result = allocate_value (internal_fn_type); + gdb_byte *addr = value_contents_writeable (result); + struct internal_function **fnp = (struct internal_function **) addr; + /* The internal_function object is leaked here -- to make it truly + deletable, we would have to reference count it and add special + code to value_free and value_copy. The setup here is a bit odd + in general. It would be better to have a special case in + help_command. */ + struct internal_function *ifn = XNEW (struct internal_function); + ifn->name = xstrdup (name); + ifn->handler = handler; + ifn->cookie = cookie; + ifn->destroyer = destroyer; + *fnp = ifn; + return result; +} + +char * +value_internal_function_name (struct value *val) +{ + gdb_byte *addr = value_contents_writeable (val); + struct internal_function *ifn = * (struct internal_function **) addr; + return ifn->name; +} + +struct value * +call_internal_function (struct value *func, int argc, struct value **argv) +{ + gdb_byte *addr = value_contents_writeable (func); + struct internal_function *ifn = * (struct internal_function **) addr; + return (*ifn->handler) (ifn->cookie, argc, argv); +} + +/* The 'function' command. This does nothing -- it is just a + placeholder to let "help function NAME" work. This is also used as + the implementation of the sub-command that is created when + registering an internal function. */ +static void +function_command (char *command, int from_tty) +{ + /* Do nothing. */ +} + +/* Clean up if an internal function's command is destroyed. */ +static void +function_destroyer (struct cmd_list_element *self, void *ignore) +{ + xfree (self->name); + xfree (self->doc); +} + +/* Add a new internal function. NAME is the name of the function; DOC + is a documentation string describing the function. HANDLER is + called when the function is invoked. COOKIE is an arbitrary + pointer which is passed to HANDLER and is intended for "user data". + DESTROYER is invoked when the function is destroyed. */ +void +add_internal_function (const char *name, const char *doc, + internal_function_fn handler, + void *cookie, void (*destroyer) (void *)) +{ + struct cmd_list_element *cmd; + struct internalvar *var = lookup_internalvar (name); + struct value *fnval = value_create_internal_function (name, handler, cookie, + destroyer); + release_value (fnval); + set_internalvar (var, fnval); + + cmd = add_cmd (xstrdup (name), no_class, function_command, (char *) doc, + &functionlist); + cmd->destroyer = function_destroyer; +} + /* Update VALUE before discarding OBJFILE. COPIED_TYPES is used to prevent cycles / duplicates. */ @@ -1840,4 +1945,13 @@ init-if-undefined VARIABLE = EXPRESSION\n\ Set an internal VARIABLE to the result of the EXPRESSION if it does not\n\ exist or does not contain a value. The EXPRESSION is not evaluated if the\n\ VARIABLE is already initialized.")); + + add_prefix_cmd ("function", no_class, function_command, _("\ +Placeholder command for showing help on convenience functions."), + &functionlist, "function ", 0, &cmdlist); + + internal_fn_type = alloc_type (NULL); + TYPE_CODE (internal_fn_type) = TYPE_CODE_INTERNAL_FUNCTION; + TYPE_LENGTH (internal_fn_type) = sizeof (struct internal_function *); + TYPE_NAME (internal_fn_type) = "<internal function>"; } diff --git a/gdb/value.h b/gdb/value.h index 5dec547..73a47e0 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -476,11 +476,11 @@ extern void set_internalvar_component (struct internalvar *var, int bitpos, int bitsize, struct value *newvalue); -extern struct internalvar *lookup_only_internalvar (char *name); +extern struct internalvar *lookup_only_internalvar (const char *name); -extern struct internalvar *create_internalvar (char *name); +extern struct internalvar *create_internalvar (const char *name); -extern struct internalvar *lookup_internalvar (char *name); +extern struct internalvar *lookup_internalvar (const char *name); extern int value_equal (struct value *arg1, struct value *arg2); @@ -599,4 +599,20 @@ extern struct value *value_allocate_space_in_inferior (int); extern struct value *value_of_local (const char *name, int complain); extern struct value * value_subscripted_rvalue (struct value *array, struct value *idx, int lowerbound); + +/* User function handler. */ + +typedef struct value *(*internal_function_fn) (void *cookie, + int argc, + struct value **argv); + +void add_internal_function (const char *name, const char *doc, + internal_function_fn handler, + void *cookie, void (*destroyer) (void *)); + +struct value *call_internal_function (struct value *function, + int argc, struct value **argv); + +char *value_internal_function_name (struct value *); + #endif /* !defined (VALUE_H) */ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-02-02 13:16 [RFC][python] Add support for convenience functions implemented in Python Thiago Jung Bauermann @ 2009-02-02 19:49 ` Eli Zaretskii 2009-02-03 14:14 ` Thiago Jung Bauermann 2009-02-04 1:07 ` Thiago Jung Bauermann 1 sibling, 1 reply; 8+ messages in thread From: Eli Zaretskii @ 2009-02-02 19:49 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches > From: Thiago Jung Bauermann <bauerman@br.ibm.com> > Date: Mon, 02 Feb 2009 11:16:44 -0200 > > gdb/doc/ > 2008-02-02 Tom Tromey <tromey@redhat.com> > > * gdb.texinfo (Convenience Vars): Document convenience functions. > (Functions In Python): New node. > (Python API): Update. Approved, with comments: > +@value{GDBN}. The @var{name} argument is the name of the function. > +This must be a string. The function is made visible to the user as a > +convenience variable, of ``internal function'' type, whose name is the > +same as @var{name}. Suggest to rephrase as follows: The argument @var{name} is the name of the function, a string. The function will be visible to the user as a convenience variable of type @code{internal function}, whose name is the same as the given @var{name}. Thanks. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-02-02 19:49 ` Eli Zaretskii @ 2009-02-03 14:14 ` Thiago Jung Bauermann 0 siblings, 0 replies; 8+ messages in thread From: Thiago Jung Bauermann @ 2009-02-03 14:14 UTC (permalink / raw) To: Eli Zaretskii; +Cc: gdb-patches El lun, 02-02-2009 a las 21:47 +0200, Eli Zaretskii escribió: > > gdb/doc/ > > 2008-02-02 Tom Tromey <tromey@redhat.com> > > > > * gdb.texinfo (Convenience Vars): Document convenience functions. > > (Functions In Python): New node. > > (Python API): Update. > > Approved, with comments: Thanks! > > +@value{GDBN}. The @var{name} argument is the name of the function. > > +This must be a string. The function is made visible to the user as a > > +convenience variable, of ``internal function'' type, whose name is the > > +same as @var{name}. > > Suggest to rephrase as follows: > > The argument @var{name} is the name of the function, a string. The > function will be visible to the user as a convenience variable of > type @code{internal function}, whose name is the same as the given > @var{name}. Ok, adopted your wording. -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-02-02 13:16 [RFC][python] Add support for convenience functions implemented in Python Thiago Jung Bauermann 2009-02-02 19:49 ` Eli Zaretskii @ 2009-02-04 1:07 ` Thiago Jung Bauermann 2009-02-04 20:16 ` Tom Tromey 1 sibling, 1 reply; 8+ messages in thread From: Thiago Jung Bauermann @ 2009-02-04 1:07 UTC (permalink / raw) To: gdb-patches ml Hi, This is a new version of the patch, with two changes: - adopts the documentation change suggested by Eli, - fnpy_call now expects a Python exception from convert_value_from_python -- []'s Thiago Jung Bauermann IBM Linux Technology Center gdb/ 2009-02-03 Tom Tromey <tromey@redhat.com> * Makefile.in (SUBDIR_PYTHON_OBS): Add python-function.o. (SUBDIR_PYTHON_SRCS): Add python-function.c. (python-function.o): New target. * eval.c: Include "python/python.h" and <ctype.h>. (evaluate_subexp_standard): Handle values of type TYPE_CODE_INTERNAL_FUNCTION. * gdbtypes.h (type_code): Add TYPE_CODE_INTERNAL_FUNCTION. * parse.c (write_exp_string): Remove duplicate word in comment. * python/python-function.c: New file. * python/python-internal.h (gdbpy_initialize_functions): Add prototype. * python/python.c (_initialize_python): Call gdbpy_initialize_functions. * valprint.c (value_check_printable): Handle values of type TYPE_CODE_INTERNAL_FUNCTION. * value.c: Include "cli/cli-decode.h". (internal_function): New struct. (functionlist, internal_fn_type): New static variables. (lookup_only_internalvar, create_internalvar, lookup_internalvar): Add const qualifier to name argument. (set_internal_var): Fix typo in comment. (value_create_internal_function, value_internal_function_name, call_internal_function, function_command, function_destroyer, add_internal_function): New functions. (_initialize_values): Create `function' placeholder command. Initialize internal_fn_type. * value.h (lookup_only_internalvar, create_internalvar, lookup_internalvar): Add const qualifier to name argument. (internal_function_fn, add_internal_function, call_internal_function, value_internal_function_name): Add prototypes. gdb/doc/ 2008-02-03 Tom Tromey <tromey@redhat.com> * gdb.texinfo (Convenience Vars): Document convenience functions. (Functions In Python): New node. (Python API): Update. gdb/testsuite/ 2009-02-03 Thiago Jung Bauermann <bauerman@br.ibm.com> * gdb.python/python-function.exp: New file. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 7400702..5cb8fec 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -271,11 +271,13 @@ SUBDIR_TUI_CFLAGS= \ SUBDIR_PYTHON_OBS = \ python.o \ python-cmd.o \ + python-function.o \ python-utils.o \ python-value.o SUBDIR_PYTHON_SRCS = \ python/python.c \ python/python-cmd.c \ + python/python-function.c \ python/python-utils.c \ python/python-value.c SUBDIR_PYTHON_DEPS = @@ -1842,6 +1844,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c $(POSTCOMPILE) +python-function.o: $(srcdir)/python/python-function.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c + $(POSTCOMPILE) + python-utils.o: $(srcdir)/python/python-utils.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c $(POSTCOMPILE) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 45e3a91..0805bfd 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7354,6 +7354,20 @@ On HP-UX systems, if you refer to a function or variable name that begins with a dollar sign, @value{GDBN} searches for a user or system name first, before it searches for a convenience variable. +@cindex convenience functions +@value{GDBN} also supplies some @dfn{convenience functions}. These +have a syntax similar to convenience variables. A convenience +function can be used in an expression just like an ordinary function; +however, a convenience function is implemented internally to +@value{GDBN}. + +@table @code +@item help function +@kindex help function +@cindex show all convenience functions +Print a list of all convenience functions. +@end table + @node Registers @section Registers @@ -18065,6 +18079,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Exception Handling:: * Values From Inferior:: * Commands In Python:: Implementing new commands in Python. +* Functions In Python:: Writing new convenience functions. @end menu @node Basic Python @@ -18463,6 +18478,65 @@ registration of the command with @value{GDBN}. Depending on how the Python code is read into @value{GDBN}, you may need to import the @code{gdb} module explicitly. +@node Functions In Python +@subsubsection Writing new convenience functions + +@cindex writing convenience functions +@cindex convenience functions in python +@cindex python convenience functions +@tindex gdb.Function +@tindex Function +You can implement new convenience functions (@pxref{Convenience Vars}) +in Python. A convenience function is an instance of a subclass of the +class @code{gdb.Function}. + +@defmethod Function __init__ name +The initializer for @code{Function} registers the new function with +@value{GDBN}. The argument @var{name} is the name of the function, +a string. The function will be visible to the user as a convenience +variable of type @code{internal function}, whose name is the same as +the given @var{name}. + +The documentation for the new function is taken from the documentation +string for the new class. +@end defmethod + +@defmethod Function invoke @var{*args} +When a convenience function is evaluated, its arguments are converted +to instances of @code{gdb.Value}, and then the function's +@code{invoke} method is called. Note that @value{GDBN} does not +predetermine the arity of convenience functions. Instead, all +available arguments are passed to @code{invoke}, following the +standard Python calling convention. In particular, a convenience +function can have default values for parameters without ill effect. + +The return value of this method is used as its value in the enclosing +expression. If an ordinary Python value is returned, it is converted +to a @code{gdb.Value} following the usual rules. +@end defmethod + +The following code snippet shows how a trivial convenience function can +be implemented in Python: + +@smallexample +class Greet (gdb.Function): + """Return string to greet someone. +Takes a name as argument.""" + + def __init__ (self): + super (Greet, self).__init__ ("greet") + + def invoke (self, name): + return "Hello, %s!" % name.string () + +Greet () +@end smallexample + +The last line instantiates the class, and is necessary to trigger the +registration of the function with @value{GDBN}. Depending on how the +Python code is read into @value{GDBN}, you may need to import the +@code{gdb} module explicitly. + @node Interpreters @chapter Command Interpreters @cindex command interpreters diff --git a/gdb/eval.c b/gdb/eval.c index 038334b..70ed369 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -40,9 +40,12 @@ #include "regcache.h" #include "user-regs.h" #include "valprint.h" +#include "python/python.h" #include "gdb_assert.h" +#include <ctype.h> + /* This is defined in valops.c */ extern int overload_resolution; @@ -1512,6 +1515,9 @@ evaluate_subexp_standard (struct type *expect_type, else error (_("Expression of type other than \"Function returning ...\" used as function")); } + if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_INTERNAL_FUNCTION) + return call_internal_function (argvec[0], nargs, argvec + 1); + return call_function_by_hand (argvec[0], nargs, argvec + 1); /* pai: FIXME save value from call_function_by_hand, then adjust pc by adjust_fn_pc if +ve */ diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index e78d3cf..eaf906c 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -134,7 +134,10 @@ enum type_code TYPE_CODE_NAMESPACE, /* C++ namespace. */ - TYPE_CODE_DECFLOAT /* Decimal floating point. */ + TYPE_CODE_DECFLOAT, /* Decimal floating point. */ + + /* Internal function type. */ + TYPE_CODE_INTERNAL_FUNCTION }; /* For now allow source to use TYPE_CODE_CLASS for C++ classes, as an diff --git a/gdb/parse.c b/gdb/parse.c index eee1f8e..dc2ccb1 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -306,7 +306,7 @@ write_exp_elt_intern (struct internalvar *expelt) strings with embedded null bytes, as is required for some languages. Don't be fooled by the fact that the string is null byte terminated, - this is strictly for the convenience of debugging gdb itself. Gdb + this is strictly for the convenience of debugging gdb itself. Gdb does not depend up the string being null terminated, since the actual length is recorded in expression elements at each end of the string. The null byte is taken into consideration when computing how diff --git a/gdb/python/python-function.c b/gdb/python/python-function.c new file mode 100644 index 0000000..acf565c --- /dev/null +++ b/gdb/python/python-function.c @@ -0,0 +1,184 @@ +/* Convenience functions implemented in Python. + + Copyright (C) 2008, 2009 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 <http://www.gnu.org/licenses/>. */ + + +#include "defs.h" +#include "value.h" +#include "exceptions.h" +#include "python-internal.h" +#include "charset.h" +#include "gdbcmd.h" +#include "cli/cli-decode.h" +#include "completer.h" +#include "expression.h" + +static PyTypeObject fnpy_object_type; + +\f + +static PyObject * +convert_values_to_python (int argc, struct value **argv) +{ + int i; + PyObject *result = PyTuple_New (argc); + for (i = 0; i < argc; ++i) + { + PyObject *elt = value_to_value_object (argv[i]); + if (! elt) + { + Py_DECREF (result); + error (_("Could not convert value to Python object.")); + } + PyTuple_SetItem (result, i, elt); + } + return result; +} + +/* Call a Python function object's invoke method. */ + +static struct value * +fnpy_call (void *cookie, int argc, struct value **argv) +{ + int i; + struct value *value = NULL; + PyObject *result, *callable, *args; + struct cleanup *cleanup; + PyGILState_STATE state; + + state = PyGILState_Ensure (); + cleanup = make_cleanup_py_restore_gil (&state); + + args = convert_values_to_python (argc, argv); + + callable = PyObject_GetAttrString ((PyObject *) cookie, "invoke"); + if (! callable) + { + Py_DECREF (args); + error (_("No method named 'invoke' in object.")); + } + + result = PyObject_Call (callable, args, NULL); + Py_DECREF (callable); + Py_DECREF (args); + + if (!result) + { + gdbpy_print_stack (); + error(_("Error while executing Python code.")); + } + + value = convert_value_from_python (result); + if (value == NULL) + { + Py_DECREF (result); + gdbpy_print_stack (); + error(_("Error while executing Python code.")); + } + + Py_DECREF (result); + do_cleanups (cleanup); + + return value; +} + +/* Called when destroying a struct internal_function. */ + +static void +fnpy_destroy (void *cookie) +{ + PyGILState_STATE state; + + state = PyGILState_Ensure (); + Py_DECREF ((PyObject *) cookie); + PyGILState_Release (state); + +} + +/* Initializer for a Function object. It takes one argument, the name + of the function. */ + +static int +fnpy_init (PyObject *self, PyObject *args, PyObject *kwds) +{ + char *name, *docstring = NULL; + if (! PyArg_ParseTuple (args, "s", &name)) + return -1; + Py_INCREF (self); + + if (PyObject_HasAttrString (self, "__doc__")) + { + PyObject *ds_obj = PyObject_GetAttrString (self, "__doc__"); + if (ds_obj && gdbpy_is_string (ds_obj)) + /* Nothing ever frees this. */ + docstring = python_string_to_host_string (ds_obj); + } + if (! docstring) + docstring = _("This function is not documented."); + + add_internal_function (name, docstring, fnpy_call, self, fnpy_destroy); + return 0; +} + +/* Initialize internal function support. */ + +void +gdbpy_initialize_functions (void) +{ + fnpy_object_type.tp_new = PyType_GenericNew; + fnpy_object_type.tp_init = fnpy_init; + if (PyType_Ready (&fnpy_object_type) < 0) + return; + Py_INCREF (&fnpy_object_type); + PyModule_AddObject (gdb_module, "Function", (PyObject *) &fnpy_object_type); +} + +\f + +static PyTypeObject fnpy_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Function", /*tp_name*/ + sizeof (PyObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB function object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0 /* tp_methods */ +}; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 02dbfc4..463f08e 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -71,6 +71,7 @@ struct value *convert_value_from_python (PyObject *obj); void gdbpy_initialize_values (void); void gdbpy_initialize_commands (void); +void gdbpy_initialize_functions (void); struct cleanup *make_cleanup_py_decref (PyObject *py); struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state); diff --git a/gdb/python/python.c b/gdb/python/python.c index 4f97416..c6c3e2a 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -410,6 +410,7 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_values (); gdbpy_initialize_commands (); + gdbpy_initialize_functions (); PyRun_SimpleString ("import gdb"); diff --git a/gdb/testsuite/gdb.python/python-function.exp b/gdb/testsuite/gdb.python/python-function.exp new file mode 100644 index 0000000..3ffc5aa --- /dev/null +++ b/gdb/testsuite/gdb.python/python-function.exp @@ -0,0 +1,65 @@ +# Copyright (C) 2009 Free Software Foundation, Inc. + +# 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 <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing convenience functions to Python. + +if $tracelevel then { + strace $tracelevel +} + +# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}... +# Run a test named NAME, consisting of multiple lines of input. +# After each input line INPUT, search for result line RESULT. +# Succeed if all results are seen; fail otherwise. +proc gdb_py_test_multiple {name args} { + global gdb_prompt + foreach {input result} $args { + if {[gdb_test_multiple $input "$name - $input" { + -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" { + pass "$name - $input" + } + }]} { + return 1 + } + } + return 0 +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +gdb_test_multiple "python print 'hello, world!'" "verify python support" { + -re "not supported.*$gdb_prompt $" { + unsupported "python support is disabled" + return -1 + } + -re "$gdb_prompt $" {} +} + +gdb_py_test_multiple "input convenience function" \ + "python" "" \ + "class test_func (gdb.Function):" "" \ + " def __init__ (self):" "" \ + " super (test_func, self).__init__ (\"test_func\")" "" \ + " def invoke (self, arg):" "" \ + " return \"test_func output, arg = %s\" % arg.string ()" "" \ + "test_func ()" "" \ + "end" "" + +gdb_test "print \$test_func (\"ugh\")" "= \"test_func output, arg = ugh\"" "call function" diff --git a/gdb/valprint.c b/gdb/valprint.c index d13dc4c..99e5da7 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -287,6 +287,13 @@ value_check_printable (struct value *val, struct ui_file *stream) return 0; } + if (TYPE_CODE (value_type (val)) == TYPE_CODE_INTERNAL_FUNCTION) + { + fprintf_filtered (stream, _("<internal function %s>"), + value_internal_function_name (val)); + return 0; + } + return 1; } diff --git a/gdb/value.c b/gdb/value.c index f0c8463..521a50b 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -37,6 +37,7 @@ #include "dfp.h" #include "objfiles.h" #include "valprint.h" +#include "cli/cli-decode.h" #include "python/python.h" @@ -44,6 +45,27 @@ void _initialize_values (void); +/* Definition of a user function. */ +struct internal_function +{ + /* The name of the function. It is a bit odd to have this in the + function itself -- the user might use a differently-named + convenience variable to hold the function. */ + char *name; + + /* The handler. */ + internal_function_fn handler; + + /* User data for the handler. */ + void *cookie; + + /* Function called to destroy the cookie when the function object is + destroyed. */ + void (*destroyer) (void *cookie); +}; + +static struct cmd_list_element *functionlist; + struct value { /* Type of value; either not an lval, or one of the various @@ -194,6 +216,10 @@ struct value_history_chunk static struct value_history_chunk *value_history_chain; static int value_history_count; /* Abs number of last entry stored */ + +/* The type of internal functions. */ + +static struct type *internal_fn_type; \f /* List of all value objects currently allocated (except for those released by calls to release_value) @@ -814,7 +840,7 @@ init_if_undefined_command (char* args, int from_tty) the return value is NULL. */ struct internalvar * -lookup_only_internalvar (char *name) +lookup_only_internalvar (const char *name) { struct internalvar *var; @@ -830,7 +856,7 @@ lookup_only_internalvar (char *name) NAME should not normally include a dollar sign. */ struct internalvar * -create_internalvar (char *name) +create_internalvar (const char *name) { struct internalvar *var; var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); @@ -851,7 +877,7 @@ create_internalvar (char *name) one is created, with a void value. */ struct internalvar * -lookup_internalvar (char *name) +lookup_internalvar (const char *name) { struct internalvar *var; @@ -938,7 +964,7 @@ set_internalvar (struct internalvar *var, struct value *val) /* Begin code which must not call error(). If var->value points to something free'd, an error() obviously leaves a dangling pointer. - But we also get a danling pointer if var->value points to + But we also get a dangling pointer if var->value points to something in the value chain (i.e., before release_value is called), because after the error free_all_values will get called before long. */ @@ -955,6 +981,85 @@ internalvar_name (struct internalvar *var) return var->name; } +static struct value * +value_create_internal_function (const char *name, + internal_function_fn handler, + void *cookie, + void (*destroyer) (void *)) +{ + struct value *result = allocate_value (internal_fn_type); + gdb_byte *addr = value_contents_writeable (result); + struct internal_function **fnp = (struct internal_function **) addr; + /* The internal_function object is leaked here -- to make it truly + deletable, we would have to reference count it and add special + code to value_free and value_copy. The setup here is a bit odd + in general. It would be better to have a special case in + help_command. */ + struct internal_function *ifn = XNEW (struct internal_function); + ifn->name = xstrdup (name); + ifn->handler = handler; + ifn->cookie = cookie; + ifn->destroyer = destroyer; + *fnp = ifn; + return result; +} + +char * +value_internal_function_name (struct value *val) +{ + gdb_byte *addr = value_contents_writeable (val); + struct internal_function *ifn = * (struct internal_function **) addr; + return ifn->name; +} + +struct value * +call_internal_function (struct value *func, int argc, struct value **argv) +{ + gdb_byte *addr = value_contents_writeable (func); + struct internal_function *ifn = * (struct internal_function **) addr; + return (*ifn->handler) (ifn->cookie, argc, argv); +} + +/* The 'function' command. This does nothing -- it is just a + placeholder to let "help function NAME" work. This is also used as + the implementation of the sub-command that is created when + registering an internal function. */ +static void +function_command (char *command, int from_tty) +{ + /* Do nothing. */ +} + +/* Clean up if an internal function's command is destroyed. */ +static void +function_destroyer (struct cmd_list_element *self, void *ignore) +{ + xfree (self->name); + xfree (self->doc); +} + +/* Add a new internal function. NAME is the name of the function; DOC + is a documentation string describing the function. HANDLER is + called when the function is invoked. COOKIE is an arbitrary + pointer which is passed to HANDLER and is intended for "user data". + DESTROYER is invoked when the function is destroyed. */ +void +add_internal_function (const char *name, const char *doc, + internal_function_fn handler, + void *cookie, void (*destroyer) (void *)) +{ + struct cmd_list_element *cmd; + struct internalvar *var = lookup_internalvar (name); + struct value *fnval = value_create_internal_function (name, handler, cookie, + destroyer); + release_value (fnval); + set_internalvar (var, fnval); + + cmd = add_cmd (xstrdup (name), no_class, function_command, (char *) doc, + &functionlist); + cmd->destroyer = function_destroyer; +} + /* Update VALUE before discarding OBJFILE. COPIED_TYPES is used to prevent cycles / duplicates. */ @@ -1840,4 +1945,13 @@ init-if-undefined VARIABLE = EXPRESSION\n\ Set an internal VARIABLE to the result of the EXPRESSION if it does not\n\ exist or does not contain a value. The EXPRESSION is not evaluated if the\n\ VARIABLE is already initialized.")); + + add_prefix_cmd ("function", no_class, function_command, _("\ +Placeholder command for showing help on convenience functions."), + &functionlist, "function ", 0, &cmdlist); + + internal_fn_type = alloc_type (NULL); + TYPE_CODE (internal_fn_type) = TYPE_CODE_INTERNAL_FUNCTION; + TYPE_LENGTH (internal_fn_type) = sizeof (struct internal_function *); + TYPE_NAME (internal_fn_type) = "<internal function>"; } diff --git a/gdb/value.h b/gdb/value.h index 5dec547..73a47e0 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -476,11 +476,11 @@ extern void set_internalvar_component (struct internalvar *var, int bitpos, int bitsize, struct value *newvalue); -extern struct internalvar *lookup_only_internalvar (char *name); +extern struct internalvar *lookup_only_internalvar (const char *name); -extern struct internalvar *create_internalvar (char *name); +extern struct internalvar *create_internalvar (const char *name); -extern struct internalvar *lookup_internalvar (char *name); +extern struct internalvar *lookup_internalvar (const char *name); extern int value_equal (struct value *arg1, struct value *arg2); @@ -599,4 +599,20 @@ extern struct value *value_allocate_space_in_inferior (int); extern struct value *value_of_local (const char *name, int complain); extern struct value * value_subscripted_rvalue (struct value *array, struct value *idx, int lowerbound); + +/* User function handler. */ + +typedef struct value *(*internal_function_fn) (void *cookie, + int argc, + struct value **argv); + +void add_internal_function (const char *name, const char *doc, + internal_function_fn handler, + void *cookie, void (*destroyer) (void *)); + +struct value *call_internal_function (struct value *function, + int argc, struct value **argv); + +char *value_internal_function_name (struct value *); + #endif /* !defined (VALUE_H) */ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-02-04 1:07 ` Thiago Jung Bauermann @ 2009-02-04 20:16 ` Tom Tromey 2009-03-05 22:47 ` Thiago Jung Bauermann 0 siblings, 1 reply; 8+ messages in thread From: Tom Tromey @ 2009-02-04 20:16 UTC (permalink / raw) To: Thiago Jung Bauermann; +Cc: gdb-patches ml >>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes: Thiago> This is a new version of the patch [...] I have a couple little nits. Also I want to point out the memory allocation oddities in this patch ... I am not super concerned by them but I would be interested in other opinions, if there are any. Thiago> diff --git a/gdb/parse.c b/gdb/parse.c Thiago> index eee1f8e..dc2ccb1 100644 [...] Thiago> - this is strictly for the convenience of debugging gdb itself. Gdb Thiago> + this is strictly for the convenience of debugging gdb itself. This is fine but it seems unrelated to this patch. Thiago> + error(_("Error while executing Python code.")); Missing a space before the first "(". Thiago> + error(_("Error while executing Python code.")); Likewise. Thiago> +void Thiago> +gdbpy_initialize_functions (void) Thiago> +{ Thiago> + fnpy_object_type.tp_new = PyType_GenericNew; Thiago> + fnpy_object_type.tp_init = fnpy_init; Lately I am preferring to just put these directly into the global's definition. Thiago> +value_create_internal_function (const char *name, [...] Thiago> + /* The internal_function object is leaked here -- to make it truly Thiago> + deletable, we would have to reference count it and add special Thiago> + code to value_free and value_copy. The setup here is a bit odd Thiago> + in general. It would be better to have a special case in Thiago> + help_command. */ So, this is the primary memory leak. You can see it in action by defining a convenience function, then assigning something else to the variable. That is, if your function is $foo, invoking "set $foo = 0" will leak the function definition. I could implement the reference counting idea if this seems important to anybody. I tend to doubt this will happen much -- it isn't common to define a lot of convenience functions and it is even less common to delete them. Another idea would be to make them un-assignable. Tom ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-02-04 20:16 ` Tom Tromey @ 2009-03-05 22:47 ` Thiago Jung Bauermann 2009-03-18 2:25 ` Tom Tromey 0 siblings, 1 reply; 8+ messages in thread From: Thiago Jung Bauermann @ 2009-03-05 22:47 UTC (permalink / raw) To: tromey; +Cc: gdb-patches ml Hi Tromey, Thanks for the review. El mié, 04-02-2009 a las 13:15 -0700, Tom Tromey escribió: > Thiago> diff --git a/gdb/parse.c b/gdb/parse.c > Thiago> index eee1f8e..dc2ccb1 100644 > [...] > Thiago> - this is strictly for the convenience of debugging gdb itself. Gdb > Thiago> + this is strictly for the convenience of debugging gdb itself. > > This is fine but it seems unrelated to this patch. Kept. > Thiago> + error(_("Error while executing Python code.")); > > Missing a space before the first "(". Fixed. > Thiago> + error(_("Error while executing Python code.")); > > Likewise. Fixed. > Thiago> +void > Thiago> +gdbpy_initialize_functions (void) > Thiago> +{ > Thiago> + fnpy_object_type.tp_new = PyType_GenericNew; > Thiago> + fnpy_object_type.tp_init = fnpy_init; > > Lately I am preferring to just put these directly into the global's > definition. Changed. > Thiago> +value_create_internal_function (const char *name, > [...] > Thiago> + /* The internal_function object is leaked here -- to make it truly > Thiago> + deletable, we would have to reference count it and add special > Thiago> + code to value_free and value_copy. The setup here is a bit odd > Thiago> + in general. It would be better to have a special case in > Thiago> + help_command. */ > > So, this is the primary memory leak. You can see it in action by > defining a convenience function, then assigning something else to the > variable. That is, if your function is $foo, invoking "set $foo = 0" > will leak the function definition. > > I could implement the reference counting idea if this seems important > to anybody. I tend to doubt this will happen much -- it isn't common > to define a lot of convenience functions and it is even less common to > delete them. > > Another idea would be to make them un-assignable. This patch incorporates Tom's change to make them un-assignable. In his words, "it disallows deleting the canonical name of a convenience function". Eli has already approved the documentation part when I posted the first version of this patch. Introduces no regressions on x86_64-linux. -- []'s Thiago Jung Bauermann IBM Linux Technology Center gdb/ 2009-03-05 Tom Tromey <tromey@redhat.com> * Makefile.in (SUBDIR_PYTHON_OBS): Add python-function.o. (SUBDIR_PYTHON_SRCS): Add python-function.c. (python-function.o): New target. * eval.c: Include "python/python.h" and <ctype.h>. (evaluate_subexp_standard): Handle values of type TYPE_CODE_INTERNAL_FUNCTION. * gdbtypes.h (type_code): Add TYPE_CODE_INTERNAL_FUNCTION. * parse.c (write_exp_string): Remove duplicate word in comment. * python/python-function.c: New file. * python/python-internal.h (gdbpy_initialize_functions): Add prototype. * python/python.c (_initialize_python): Call gdbpy_initialize_functions. * valprint.c (value_check_printable): Handle values of type TYPE_CODE_INTERNAL_FUNCTION. * value.c: Include "cli/cli-decode.h". (internal_function): New struct. (functionlist, internal_fn_type): New static variables. (lookup_only_internalvar, lookup_internalvar): Add const qualifier to name argument. (create_internalvar): Likewise. Initialize new field. (set_internal_var): Fix typo in comment. Don't allow assignment to canonical variable. (value_create_internal_function, value_internal_function_name, call_internal_function, function_command, function_destroyer, add_internal_function): New functions. (_initialize_values): Create `function' placeholder command. Initialize internal_fn_type. * value.h (lookup_only_internalvar, create_internalvar, lookup_internalvar): Add const qualifier to name argument. (internal_function_fn, add_internal_function, call_internal_function, value_internal_function_name): Add prototypes. (struct internalvar) <canonical>: New field. gdb/doc/ 2008-03-05 Tom Tromey <tromey@redhat.com> * gdb.texinfo (Convenience Vars): Document convenience functions. (Functions In Python): New node. (Python API): Update. gdb/testsuite/ 2009-03-05 Thiago Jung Bauermann <bauerman@br.ibm.com> * gdb.python/python-function.exp: New file. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 74aa72e..6334f65 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -271,11 +271,13 @@ SUBDIR_TUI_CFLAGS= \ SUBDIR_PYTHON_OBS = \ python.o \ python-cmd.o \ + python-function.o \ python-utils.o \ python-value.o SUBDIR_PYTHON_SRCS = \ python/python.c \ python/python-cmd.c \ + python/python-function.c \ python/python-utils.c \ python/python-value.c SUBDIR_PYTHON_DEPS = @@ -1849,6 +1851,10 @@ python-cmd.o: $(srcdir)/python/python-cmd.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-cmd.c $(POSTCOMPILE) +python-function.o: $(srcdir)/python/python-function.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-function.c + $(POSTCOMPILE) + python-utils.o: $(srcdir)/python/python-utils.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/python-utils.c $(POSTCOMPILE) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 10e7388..67c7ea7 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7408,6 +7408,20 @@ On HP-UX systems, if you refer to a function or variable name that begins with a dollar sign, @value{GDBN} searches for a user or system name first, before it searches for a convenience variable. +@cindex convenience functions +@value{GDBN} also supplies some @dfn{convenience functions}. These +have a syntax similar to convenience variables. A convenience +function can be used in an expression just like an ordinary function; +however, a convenience function is implemented internally to +@value{GDBN}. + +@table @code +@item help function +@kindex help function +@cindex show all convenience functions +Print a list of all convenience functions. +@end table + @node Registers @section Registers @@ -18127,6 +18141,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Exception Handling:: * Values From Inferior:: * Commands In Python:: Implementing new commands in Python. +* Functions In Python:: Writing new convenience functions. @end menu @node Basic Python @@ -18563,6 +18578,65 @@ registration of the command with @value{GDBN}. Depending on how the Python code is read into @value{GDBN}, you may need to import the @code{gdb} module explicitly. +@node Functions In Python +@subsubsection Writing new convenience functions + +@cindex writing convenience functions +@cindex convenience functions in python +@cindex python convenience functions +@tindex gdb.Function +@tindex Function +You can implement new convenience functions (@pxref{Convenience Vars}) +in Python. A convenience function is an instance of a subclass of the +class @code{gdb.Function}. + +@defmethod Function __init__ name +The initializer for @code{Function} registers the new function with +@value{GDBN}. The argument @var{name} is the name of the function, +a string. The function will be visible to the user as a convenience +variable of type @code{internal function}, whose name is the same as +the given @var{name}. + +The documentation for the new function is taken from the documentation +string for the new class. +@end defmethod + +@defmethod Function invoke @var{*args} +When a convenience function is evaluated, its arguments are converted +to instances of @code{gdb.Value}, and then the function's +@code{invoke} method is called. Note that @value{GDBN} does not +predetermine the arity of convenience functions. Instead, all +available arguments are passed to @code{invoke}, following the +standard Python calling convention. In particular, a convenience +function can have default values for parameters without ill effect. + +The return value of this method is used as its value in the enclosing +expression. If an ordinary Python value is returned, it is converted +to a @code{gdb.Value} following the usual rules. +@end defmethod + +The following code snippet shows how a trivial convenience function can +be implemented in Python: + +@smallexample +class Greet (gdb.Function): + """Return string to greet someone. +Takes a name as argument.""" + + def __init__ (self): + super (Greet, self).__init__ ("greet") + + def invoke (self, name): + return "Hello, %s!" % name.string () + +Greet () +@end smallexample + +The last line instantiates the class, and is necessary to trigger the +registration of the function with @value{GDBN}. Depending on how the +Python code is read into @value{GDBN}, you may need to import the +@code{gdb} module explicitly. + @node Interpreters @chapter Command Interpreters @cindex command interpreters diff --git a/gdb/eval.c b/gdb/eval.c index 1d35571..4ff21b9 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -40,9 +40,12 @@ #include "regcache.h" #include "user-regs.h" #include "valprint.h" +#include "python/python.h" #include "gdb_assert.h" +#include <ctype.h> + /* This is defined in valops.c */ extern int overload_resolution; @@ -1512,6 +1515,9 @@ evaluate_subexp_standard (struct type *expect_type, else error (_("Expression of type other than \"Function returning ...\" used as function")); } + if (TYPE_CODE (value_type (argvec[0])) == TYPE_CODE_INTERNAL_FUNCTION) + return call_internal_function (argvec[0], nargs, argvec + 1); + return call_function_by_hand (argvec[0], nargs, argvec + 1); /* pai: FIXME save value from call_function_by_hand, then adjust pc by adjust_fn_pc if +ve */ diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index c90b6d7..816a208 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -134,7 +134,10 @@ enum type_code TYPE_CODE_NAMESPACE, /* C++ namespace. */ - TYPE_CODE_DECFLOAT /* Decimal floating point. */ + TYPE_CODE_DECFLOAT, /* Decimal floating point. */ + + /* Internal function type. */ + TYPE_CODE_INTERNAL_FUNCTION }; /* For now allow source to use TYPE_CODE_CLASS for C++ classes, as an diff --git a/gdb/parse.c b/gdb/parse.c index eee1f8e..dc2ccb1 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -306,7 +306,7 @@ write_exp_elt_intern (struct internalvar *expelt) strings with embedded null bytes, as is required for some languages. Don't be fooled by the fact that the string is null byte terminated, - this is strictly for the convenience of debugging gdb itself. Gdb + this is strictly for the convenience of debugging gdb itself. Gdb does not depend up the string being null terminated, since the actual length is recorded in expression elements at each end of the string. The null byte is taken into consideration when computing how diff --git a/gdb/python/python-function.c b/gdb/python/python-function.c new file mode 100644 index 0000000..4a85a33 --- /dev/null +++ b/gdb/python/python-function.c @@ -0,0 +1,180 @@ +/* Convenience functions implemented in Python. + + Copyright (C) 2008, 2009 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 <http://www.gnu.org/licenses/>. */ + + +#include "defs.h" +#include "value.h" +#include "exceptions.h" +#include "python-internal.h" +#include "charset.h" +#include "gdbcmd.h" +#include "cli/cli-decode.h" +#include "completer.h" +#include "expression.h" + +static PyTypeObject fnpy_object_type; + +\f + +static PyObject * +convert_values_to_python (int argc, struct value **argv) +{ + int i; + PyObject *result = PyTuple_New (argc); + for (i = 0; i < argc; ++i) + { + PyObject *elt = value_to_value_object (argv[i]); + if (! elt) + { + Py_DECREF (result); + error (_("Could not convert value to Python object.")); + } + PyTuple_SetItem (result, i, elt); + } + return result; +} + +/* Call a Python function object's invoke method. */ + +static struct value * +fnpy_call (void *cookie, int argc, struct value **argv) +{ + int i; + struct value *value = NULL; + PyObject *result, *callable, *args; + struct cleanup *cleanup; + PyGILState_STATE state; + + state = PyGILState_Ensure (); + cleanup = make_cleanup_py_restore_gil (&state); + + args = convert_values_to_python (argc, argv); + + callable = PyObject_GetAttrString ((PyObject *) cookie, "invoke"); + if (! callable) + { + Py_DECREF (args); + error (_("No method named 'invoke' in object.")); + } + + result = PyObject_Call (callable, args, NULL); + Py_DECREF (callable); + Py_DECREF (args); + + if (!result) + { + gdbpy_print_stack (); + error (_("Error while executing Python code.")); + } + + value = convert_value_from_python (result); + if (value == NULL) + { + Py_DECREF (result); + gdbpy_print_stack (); + error (_("Error while executing Python code.")); + } + + Py_DECREF (result); + do_cleanups (cleanup); + + return value; +} + +/* Initializer for a Function object. It takes one argument, the name + of the function. */ + +static int +fnpy_init (PyObject *self, PyObject *args, PyObject *kwds) +{ + char *name, *docstring = NULL; + if (! PyArg_ParseTuple (args, "s", &name)) + return -1; + Py_INCREF (self); + + if (PyObject_HasAttrString (self, "__doc__")) + { + PyObject *ds_obj = PyObject_GetAttrString (self, "__doc__"); + if (ds_obj && gdbpy_is_string (ds_obj)) + /* Nothing ever frees this. */ + docstring = python_string_to_host_string (ds_obj); + } + if (! docstring) + docstring = _("This function is not documented."); + + add_internal_function (name, docstring, fnpy_call, self); + return 0; +} + +/* Initialize internal function support. */ + +void +gdbpy_initialize_functions (void) +{ + if (PyType_Ready (&fnpy_object_type) < 0) + return; + + Py_INCREF (&fnpy_object_type); + PyModule_AddObject (gdb_module, "Function", (PyObject *) &fnpy_object_type); +} + +\f + +static PyTypeObject fnpy_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Function", /*tp_name*/ + sizeof (PyObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB function object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + fnpy_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew /* tp_new */ +}; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 02dbfc4..463f08e 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -71,6 +71,7 @@ struct value *convert_value_from_python (PyObject *obj); void gdbpy_initialize_values (void); void gdbpy_initialize_commands (void); +void gdbpy_initialize_functions (void); struct cleanup *make_cleanup_py_decref (PyObject *py); struct cleanup *make_cleanup_py_restore_gil (PyGILState_STATE *state); diff --git a/gdb/python/python.c b/gdb/python/python.c index b3a27d6..29f83bb 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -413,6 +413,7 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_values (); gdbpy_initialize_commands (); + gdbpy_initialize_functions (); PyRun_SimpleString ("import gdb"); diff --git a/gdb/testsuite/gdb.python/python-function.exp b/gdb/testsuite/gdb.python/python-function.exp new file mode 100644 index 0000000..3ffc5aa --- /dev/null +++ b/gdb/testsuite/gdb.python/python-function.exp @@ -0,0 +1,65 @@ +# Copyright (C) 2009 Free Software Foundation, Inc. + +# 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 <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing convenience functions to Python. + +if $tracelevel then { + strace $tracelevel +} + +# Usage: gdb_py_test_multiple NAME INPUT RESULT {INPUT RESULT}... +# Run a test named NAME, consisting of multiple lines of input. +# After each input line INPUT, search for result line RESULT. +# Succeed if all results are seen; fail otherwise. +proc gdb_py_test_multiple {name args} { + global gdb_prompt + foreach {input result} $args { + if {[gdb_test_multiple $input "$name - $input" { + -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" { + pass "$name - $input" + } + }]} { + return 1 + } + } + return 0 +} + +# Start with a fresh gdb. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +gdb_test_multiple "python print 'hello, world!'" "verify python support" { + -re "not supported.*$gdb_prompt $" { + unsupported "python support is disabled" + return -1 + } + -re "$gdb_prompt $" {} +} + +gdb_py_test_multiple "input convenience function" \ + "python" "" \ + "class test_func (gdb.Function):" "" \ + " def __init__ (self):" "" \ + " super (test_func, self).__init__ (\"test_func\")" "" \ + " def invoke (self, arg):" "" \ + " return \"test_func output, arg = %s\" % arg.string ()" "" \ + "test_func ()" "" \ + "end" "" + +gdb_test "print \$test_func (\"ugh\")" "= \"test_func output, arg = ugh\"" "call function" diff --git a/gdb/valprint.c b/gdb/valprint.c index b02e9df..53a928b 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -287,6 +287,13 @@ value_check_printable (struct value *val, struct ui_file *stream) return 0; } + if (TYPE_CODE (value_type (val)) == TYPE_CODE_INTERNAL_FUNCTION) + { + fprintf_filtered (stream, _("<internal function %s>"), + value_internal_function_name (val)); + return 0; + } + return 1; } diff --git a/gdb/value.c b/gdb/value.c index 4d4329e..9c08a41 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -37,6 +37,7 @@ #include "dfp.h" #include "objfiles.h" #include "valprint.h" +#include "cli/cli-decode.h" #include "python/python.h" @@ -44,6 +45,23 @@ void _initialize_values (void); +/* Definition of a user function. */ +struct internal_function +{ + /* The name of the function. It is a bit odd to have this in the + function itself -- the user might use a differently-named + convenience variable to hold the function. */ + char *name; + + /* The handler. */ + internal_function_fn handler; + + /* User data for the handler. */ + void *cookie; +}; + +static struct cmd_list_element *functionlist; + struct value { /* Type of value; either not an lval, or one of the various @@ -203,6 +221,10 @@ struct value_history_chunk static struct value_history_chunk *value_history_chain; static int value_history_count; /* Abs number of last entry stored */ + +/* The type of internal functions. */ + +static struct type *internal_fn_type; \f /* List of all value objects currently allocated (except for those released by calls to release_value) @@ -878,7 +900,7 @@ init_if_undefined_command (char* args, int from_tty) the return value is NULL. */ struct internalvar * -lookup_only_internalvar (char *name) +lookup_only_internalvar (const char *name) { struct internalvar *var; @@ -894,7 +916,7 @@ lookup_only_internalvar (char *name) NAME should not normally include a dollar sign. */ struct internalvar * -create_internalvar (char *name) +create_internalvar (const char *name) { struct internalvar *var; var = (struct internalvar *) xmalloc (sizeof (struct internalvar)); @@ -902,6 +924,7 @@ create_internalvar (char *name) var->value = allocate_value (builtin_type_void); var->endian = gdbarch_byte_order (current_gdbarch); var->make_value = NULL; + var->canonical = 0; release_value (var->value); var->next = internalvars; internalvars = var; @@ -934,7 +957,7 @@ create_internalvar_type_lazy (char *name, internalvar_make_value fun) one is created, with a void value. */ struct internalvar * -lookup_internalvar (char *name) +lookup_internalvar (const char *name) { struct internalvar *var; @@ -1031,6 +1054,9 @@ set_internalvar (struct internalvar *var, struct value *val) { struct value *newval; + if (var->canonical) + error (_("Cannot overwrite convenience function %s"), var->name); + newval = value_copy (val); newval->modifiable = 1; @@ -1042,7 +1068,7 @@ set_internalvar (struct internalvar *var, struct value *val) /* Begin code which must not call error(). If var->value points to something free'd, an error() obviously leaves a dangling pointer. - But we also get a danling pointer if var->value points to + But we also get a dangling pointer if var->value points to something in the value chain (i.e., before release_value is called), because after the error free_all_values will get called before long. */ @@ -1059,6 +1085,76 @@ internalvar_name (struct internalvar *var) return var->name; } +static struct value * +value_create_internal_function (const char *name, + internal_function_fn handler, + void *cookie) +{ + struct value *result = allocate_value (internal_fn_type); + gdb_byte *addr = value_contents_writeable (result); + struct internal_function **fnp = (struct internal_function **) addr; + struct internal_function *ifn = XNEW (struct internal_function); + ifn->name = xstrdup (name); + ifn->handler = handler; + ifn->cookie = cookie; + *fnp = ifn; + return result; +} + +char * +value_internal_function_name (struct value *val) +{ + gdb_byte *addr = value_contents_writeable (val); + struct internal_function *ifn = * (struct internal_function **) addr; + return ifn->name; +} + +struct value * +call_internal_function (struct value *func, int argc, struct value **argv) +{ + gdb_byte *addr = value_contents_writeable (func); + struct internal_function *ifn = * (struct internal_function **) addr; + return (*ifn->handler) (ifn->cookie, argc, argv); +} + +/* The 'function' command. This does nothing -- it is just a + placeholder to let "help function NAME" work. This is also used as + the implementation of the sub-command that is created when + registering an internal function. */ +static void +function_command (char *command, int from_tty) +{ + /* Do nothing. */ +} + +/* Clean up if an internal function's command is destroyed. */ +static void +function_destroyer (struct cmd_list_element *self, void *ignore) +{ + xfree (self->name); + xfree (self->doc); +} + +/* Add a new internal function. NAME is the name of the function; DOC + is a documentation string describing the function. HANDLER is + called when the function is invoked. COOKIE is an arbitrary + pointer which is passed to HANDLER and is intended for "user + data". */ +void +add_internal_function (const char *name, const char *doc, + internal_function_fn handler, void *cookie) +{ + struct cmd_list_element *cmd; + struct internalvar *var = lookup_internalvar (name); + struct value *fnval = value_create_internal_function (name, handler, cookie); + set_internalvar (var, fnval); + var->canonical = 1; + + cmd = add_cmd (xstrdup (name), no_class, function_command, (char *) doc, + &functionlist); + cmd->destroyer = function_destroyer; +} + /* Update VALUE before discarding OBJFILE. COPIED_TYPES is used to prevent cycles / duplicates. */ @@ -1944,4 +2040,13 @@ init-if-undefined VARIABLE = EXPRESSION\n\ Set an internal VARIABLE to the result of the EXPRESSION if it does not\n\ exist or does not contain a value. The EXPRESSION is not evaluated if the\n\ VARIABLE is already initialized.")); + + add_prefix_cmd ("function", no_class, function_command, _("\ +Placeholder command for showing help on convenience functions."), + &functionlist, "function ", 0, &cmdlist); + + internal_fn_type = alloc_type (NULL); + TYPE_CODE (internal_fn_type) = TYPE_CODE_INTERNAL_FUNCTION; + TYPE_LENGTH (internal_fn_type) = sizeof (struct internal_function *); + TYPE_NAME (internal_fn_type) = "<internal function>"; } diff --git a/gdb/value.h b/gdb/value.h index aa43365..b45f863 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -314,6 +314,9 @@ struct internalvar struct value *value; internalvar_make_value make_value; int endian; + /* True if this internalvar is the canonical name for a convenience + function. */ + int canonical; }; \f @@ -533,14 +536,14 @@ extern void set_internalvar_component (struct internalvar *var, int bitpos, int bitsize, struct value *newvalue); -extern struct internalvar *lookup_only_internalvar (char *name); +extern struct internalvar *lookup_only_internalvar (const char *name); -extern struct internalvar *create_internalvar (char *name); +extern struct internalvar *create_internalvar (const char *name); extern struct internalvar * create_internalvar_type_lazy (char *name, internalvar_make_value fun); -extern struct internalvar *lookup_internalvar (char *name); +extern struct internalvar *lookup_internalvar (const char *name); extern int value_equal (struct value *arg1, struct value *arg2); @@ -659,4 +662,20 @@ extern struct value *value_allocate_space_in_inferior (int); extern struct value *value_of_local (const char *name, int complain); extern struct value * value_subscripted_rvalue (struct value *array, struct value *idx, int lowerbound); + +/* User function handler. */ + +typedef struct value *(*internal_function_fn) (void *cookie, + int argc, + struct value **argv); + +void add_internal_function (const char *name, const char *doc, + internal_function_fn handler, + void *cookie); + +struct value *call_internal_function (struct value *function, + int argc, struct value **argv); + +char *value_internal_function_name (struct value *); + #endif /* !defined (VALUE_H) */ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-03-05 22:47 ` Thiago Jung Bauermann @ 2009-03-18 2:25 ` Tom Tromey 2009-03-21 3:16 ` Thiago Jung Bauermann 0 siblings, 1 reply; 8+ messages in thread From: Tom Tromey @ 2009-03-18 2:25 UTC (permalink / raw) To: gdb-patches >>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes: Thiago> This patch incorporates Tom's change to make them un-assignable. In his Thiago> words, "it disallows deleting the canonical name of a convenience Thiago> function". Thiago> Eli has already approved the documentation part when I posted the first Thiago> version of this patch. Thiago> Introduces no regressions on x86_64-linux. This is ok, please check it in. Thanks again for all your work on these Python patches. Sorry for the delay on this response. Tom ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC][python] Add support for convenience functions implemented in Python 2009-03-18 2:25 ` Tom Tromey @ 2009-03-21 3:16 ` Thiago Jung Bauermann 0 siblings, 0 replies; 8+ messages in thread From: Thiago Jung Bauermann @ 2009-03-21 3:16 UTC (permalink / raw) To: tromey; +Cc: gdb-patches El mar, 17-03-2009 a las 19:31 -0600, Tom Tromey escribió: > >>>>> "Thiago" == Thiago Jung Bauermann <bauerman@br.ibm.com> writes: > Thiago> Introduces no regressions on x86_64-linux. > > This is ok, please check it in. Checked in. Yay! > Thanks again for all your work on these Python patches. > Sorry for the delay on this response. No problem, thanks for your review of the Python patches. -- []'s Thiago Jung Bauermann IBM Linux Technology Center ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2009-03-21 3:05 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2009-02-02 13:16 [RFC][python] Add support for convenience functions implemented in Python Thiago Jung Bauermann 2009-02-02 19:49 ` Eli Zaretskii 2009-02-03 14:14 ` Thiago Jung Bauermann 2009-02-04 1:07 ` Thiago Jung Bauermann 2009-02-04 20:16 ` Tom Tromey 2009-03-05 22:47 ` Thiago Jung Bauermann 2009-03-18 2:25 ` Tom Tromey 2009-03-21 3:16 ` Thiago Jung Bauermann
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox