* [RFC] Debug Methods in GDB Python
@ 2013-01-07 21:22 Siva Chandra
2013-01-29 1:51 ` Siva Chandra
0 siblings, 1 reply; 18+ messages in thread
From: Siva Chandra @ 2013-01-07 21:22 UTC (permalink / raw)
To: gdb-patches; +Cc: Doug Evans
[-- Attachment #1: Type: text/plain, Size: 2713 bytes --]
Hi all,
This is a followup to the thread on debug operators here (last
message): http://sourceware.org/ml/gdb-patches/2013-01/msg00005.html
I am starting another thread as I now have, what I feel is, a fairly
independent patch which implements a larger feature (of which C++
operator methods are a part), and some of the un-implemented features
I have mentioned in my last post have been implemented. The patch is
attached. Few points about the patch:
1. The patch is incomplete in the sense that tests and documentation
are missing. Also, ways to enable and disable single or group of debug
methods is missing. At this point, I am looking for feedback on my
direction and the way I am intercepting the normal GDB flow to lookup
methods defined in Python. I will work on completing the patch once
there is an agreement on these basics.
2. I did not intend to provide a way for the user to define new or
override static methods via Python.
3. I did not intend to provide a way for the user to define new
virtual methods in Python. The user can however override existing
virtual and non-virtual methods. The user can also define new
non-virtual methods in Python.
4. Doug suggested that I use the C++ infrastructure in GDB as much as
possible. I feel I have done so.
5. The Changelog for the patch (which is also raw) is as follows:
2013-01-07 Siva Chandra Reddy <sivachandra@google.com>
* Makefile.in: Add entries for new files
* data-directory/Makefile.in: Add entry for new Python file
* eval.c: Invoke methods defined in extension methods.
* ext-function.c: Support for working with functions/methods
defined in an extension language.
* ext-function.h: Support for working with functions/methods
defined in an extension language.
* python/lib/gdb/debugmethods.py: Python side of the support
for debug methods in Python.
* python/py-debugmethods.c: C side of the support for debug
methods in Python.
* python/py-objfile.c: Add 'debug_methods' attribute to
gdb.Objfile.
* python/python-internal.h: Add new function
gdb.enable_debug_methods to the Python module 'gdb'.
* python/python.c: Add new function gdb.enable_debug_methods to
the Python module 'gdb'.
* python/python.h: Add declarations of new functions.
* valarith.c: Invoke operators defined in extension languages.
* valops.c: Lookup methods defined in extension languages.
* value.h: New signature for 'find_overload_match'.
6. The toy program I used for testing, and the corresponding auto load
script are also attached for illustration.
Thanks and regards,
Siva Chandra
[-- Attachment #2: dm_patch_v1.txt --]
[-- Type: text/plain, Size: 51916 bytes --]
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b065d41..7150781 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -277,6 +277,7 @@ SUBDIR_PYTHON_OBS = \
py-breakpoint.o \
py-cmd.o \
py-continueevent.o \
+ py-debugmethods.o \
py-event.o \
py-evtregistry.o \
py-evts.o \
@@ -310,6 +311,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-breakpoint.c \
python/py-cmd.c \
python/py-continueevent.c \
+ python/py-debugmethods.c \
python/py-event.c \
python/py-evtregistry.c \
python/py-evts.c \
@@ -710,7 +712,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 ext-function.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 gdbtypes.c gnu-v2-abi.c gnu-v3-abi.c \
@@ -808,7 +810,7 @@ doublest.h regset.h hppa-tdep.h ppc-linux-tdep.h rs6000-tdep.h \
common/gdb_locale.h common/gdb_dirent.h arch-utils.h trad-frame.h gnu-nat.h \
language.h nbsd-tdep.h solib-svr4.h \
macroexp.h ui-file.h regcache.h gdb_string.h tracepoint.h i386-tdep.h \
-inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \
+inf-child.h p-lang.h event-top.h ext-function.h gdbtypes.h user-regs.h \
regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \
config/i386/nm-fbsd.h \
config/nm-nto.h config/sparc/nm-sol2.h config/nm-linux.h \
@@ -922,7 +924,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
inferior.o osdata.o gdb_usleep.o record.o gcore.o \
gdb_vecs.o jit.o progspace.o skip.o probe.o \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
- format.o registry.o
+ format.o registry.o ext-function.o
TSOBS = inflow.o
@@ -2065,6 +2067,10 @@ py-continueevent.o: $(srcdir)/python/py-continueevent.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c
$(POSTCOMPILE)
+py-debugmethods.o: $(srcdir)/python/py-debugmethods.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-debugmethods.c
+ $(POSTCOMPILE)
+
py-event.o: $(srcdir)/python/py-event.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c
$(POSTCOMPILE)
diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index d98ac77..04e5e52 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -56,6 +56,7 @@ PYTHON_FILES = \
gdb/types.py \
gdb/printing.py \
gdb/prompt.py \
+ gdb/debugmethods.py \
gdb/command/__init__.py \
gdb/command/type_printers.py \
gdb/command/pretty_printers.py \
diff --git a/gdb/eval.c b/gdb/eval.c
index c9630df..4586c92 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -22,6 +22,7 @@
#include "symtab.h"
#include "gdbtypes.h"
#include "value.h"
+#include "ext-function.h"
#include "expression.h"
#include "target.h"
#include "frame.h"
@@ -1593,7 +1594,7 @@ evaluate_subexp_standard (struct type *expect_type,
0, /* strict match */
NULL, NULL, /* pass NULL symbol since
symbol is unknown */
- NULL, &symp, NULL, 0);
+ NULL, &symp, NULL, NULL, 0);
/* Now fix the expression being evaluated. */
exp->elts[save_pos1 + 2].symbol = symp;
@@ -1623,12 +1624,13 @@ evaluate_subexp_standard (struct type *expect_type,
/* Language is C++, do some overload resolution before
evaluation. */
struct value *valp = NULL;
+ struct ext_fn_descriptor *ext_fnp = NULL;
(void) find_overload_match (&argvec[1], nargs, tstr,
METHOD, /* method */
0, /* strict match */
&arg2, /* the object */
- NULL, &valp, NULL,
+ NULL, &valp, NULL, &ext_fnp,
&static_memfuncp, 0);
if (op == OP_SCOPE && !static_memfuncp)
@@ -1638,9 +1640,19 @@ evaluate_subexp_standard (struct type *expect_type,
"`this' pointer"),
function_name);
}
- argvec[1] = arg2; /* the ``this'' pointer */
- argvec[0] = valp; /* Use the method found after overload
- resolution. */
+
+ if (valp)
+ {
+ argvec[1] = arg2; /* the ``this'' pointer */
+ argvec[0] = valp; /* Use the method found after overload
+ resolution. */
+ }
+ if (ext_fnp)
+ {
+ if (ext_fnp->is_method)
+ return ext_fn_invoke_method (ext_fnp, arg2, argvec + 2,
+ nargs - 1);
+ }
}
else
/* Non-C++ case -- or no overload resolution. */
@@ -1700,7 +1712,7 @@ evaluate_subexp_standard (struct type *expect_type,
NON_METHOD, /* not method */
0, /* strict match */
NULL, function, /* the function */
- NULL, &symp, NULL, no_adl);
+ NULL, &symp, NULL, NULL, no_adl);
if (op == OP_VAR_VALUE)
{
diff --git a/gdb/ext-function.c b/gdb/ext-function.c
new file mode 100644
index 0000000..c3f2216
--- /dev/null
+++ b/gdb/ext-function.c
@@ -0,0 +1,170 @@
+/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "ext-function.h"
+#include "python/python.h"
+
+static ULONGEST enabled_ext_languages = EXT_LANG_PYTHON;
+
+struct ext_fn_descriptor *
+new_ext_function (void)
+{
+ struct ext_fn_descriptor *ext_fn =
+ (struct ext_fn_descriptor *) xzalloc (sizeof (struct ext_fn_descriptor));
+
+ ext_fn->next = NULL;
+ ext_fn->is_method = 0;
+ ext_fn->lang = EXT_LANG_UNKNOWN;
+ ext_fn->ext_object = NULL;
+
+ return ext_fn;
+}
+
+void
+ext_fn_list_free (struct ext_fn_descriptor *list)
+{
+ while (list)
+ {
+ struct ext_fn_descriptor *item = list;
+
+ list = list->next;
+
+ if (item->is_method)
+ {
+ if (item->lang == EXT_LANG_PYTHON)
+ py_free_ext_object (item->ext_object);
+ }
+
+ xfree (item);
+ }
+}
+
+struct ext_fn_descriptor *
+ext_fn_list_extend (struct ext_fn_descriptor *l1, struct ext_fn_descriptor *l2)
+{
+ struct ext_fn_descriptor *item = l1;
+
+ if (!l1)
+ return l2;
+
+ while (item->next)
+ item = item->next;
+
+ item->next = l2;
+
+ return l1;
+}
+
+struct ext_fn_descriptor *
+ext_fn_list_prepend (struct ext_fn_descriptor *list,
+ struct ext_fn_descriptor *item)
+{
+ struct ext_fn_descriptor *last_item;
+ if (!list)
+ return item;
+
+ if (!item)
+ return list;
+
+ item->next = list;
+
+ return item;
+}
+
+struct ext_fn_descriptor *
+ext_fn_list_remove (struct ext_fn_descriptor *list,
+ struct ext_fn_descriptor *item)
+{
+ struct ext_fn_descriptor *prev_item = NULL, *head = list;
+
+ while (list)
+ {
+ if (list == item)
+ {
+ if (prev_item)
+ {
+ prev_item->next = list->next;
+ item->next = NULL;
+ }
+ else
+ {
+ head = list->next;
+ item->next = NULL;
+ }
+
+ break;
+ }
+
+ prev_item = list;
+ list = list->next;
+ }
+
+ return head;
+}
+
+struct ext_fn_descriptor *
+ext_fn_list_get (struct ext_fn_descriptor *list, int index)
+{
+ int i = 0;
+
+ while (list)
+ {
+ if (i == index)
+ return list;
+
+ i++;
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+struct ext_fn_descriptor *
+get_matching_ext_methods (struct type *type, const char *method)
+{
+ struct ext_fn_descriptor *python_methods = NULL;
+
+ if (enabled_ext_languages && EXT_LANG_PYTHON)
+ python_methods = py_debugmethod_name_match (type, method);
+
+ return python_methods;
+}
+
+struct type **
+ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs)
+{
+ if ((enabled_ext_languages && EXT_LANG_PYTHON)
+ && (ext_fn->lang == EXT_LANG_PYTHON))
+ return py_ext_fn_get_argtypes (ext_fn, nargs);
+
+ *nargs = 0;
+ return NULL;
+}
+
+struct value *
+ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj,
+ struct value **args, int nargs)
+{
+ if ((enabled_ext_languages && EXT_LANG_PYTHON)
+ && (ext_fn->lang == EXT_LANG_PYTHON))
+ return py_ext_fn_invoke_method (ext_fn, obj, args, nargs);
+
+ return NULL;
+}
diff --git a/gdb/ext-function.h b/gdb/ext-function.h
new file mode 100644
index 0000000..7f87cb7
--- /dev/null
+++ b/gdb/ext-function.h
@@ -0,0 +1,71 @@
+/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */
+
+#if !defined (EXT_FUNCTION_H)
+#define EXT_FUNCTION_H
+
+struct value;
+struct type;
+
+enum ext_lang
+ {
+ EXT_LANG_UNKNOWN = 0,
+ /* Values for extension languages are powers of 2. */
+ EXT_LANG_GUILE = 1,
+ EXT_LANG_PYTHON = 2
+ /* Next extension language should have value of 4. */
+ };
+
+struct ext_fn_descriptor
+ {
+ enum ext_lang lang;
+
+ int is_method;
+
+ void *ext_object;
+
+ struct ext_fn_descriptor *next;
+ };
+
+extern struct value *ext_fn_invoke_method (struct ext_fn_descriptor *,
+ struct value *,
+ struct value **, int nargs);
+
+extern struct ext_fn_descriptor *ext_fn_list_remove (
+ struct ext_fn_descriptor *list, struct ext_fn_descriptor *item);
+
+extern struct ext_fn_descriptor *ext_fn_list_get (
+ struct ext_fn_descriptor *list, int);
+
+extern struct ext_fn_descriptor *
+ext_fn_list_extend (struct ext_fn_descriptor *, struct ext_fn_descriptor *);
+
+extern struct ext_fn_descriptor *ext_fn_list_prepend (
+ struct ext_fn_descriptor *list, struct ext_fn_descriptor *item);
+
+extern void ext_fn_list_free (struct ext_fn_descriptor *list);
+
+extern struct ext_fn_descriptor *new_ext_function (void);
+
+extern struct ext_fn_descriptor *get_matching_ext_methods (struct type *,
+ const char *);
+
+extern struct type **ext_fn_get_argtypes (struct ext_fn_descriptor *, int *);
+
+#endif /* EXT_FUNCTION_H */
diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py
new file mode 100644
index 0000000..9d0c706
--- /dev/null
+++ b/gdb/python/lib/gdb/debugmethods.py
@@ -0,0 +1,113 @@
+# Python side of the support for debug methods.
+# Copyright (C) 2013 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/>.
+
+"""Utilities for adding debug methods"""
+
+import gdb
+import re
+
+DEFAULT_DEBUG_METHOD_GROUP = 'DEFAULT_DEBUG_METHOD_GROUP'
+
+
+class DebugMethod(object):
+
+ def __init__(self, name, group=DEFAULT_DEBUG_METHOD_GROUP):
+ self._name = name
+ self.group = group
+ self.enabled = True
+
+ @property
+ def name(self):
+ return self._name
+
+ def match_class(self, class_name):
+ gdb.GdbError('ERROR: Invoking abstract method \'match_class\'.')
+
+ def match_method(self, method_name):
+ gdb.GdbError('ERROR: Invoking abstract method \'match_method\'.')
+
+ def get_argtypes(self, class_type, method_name):
+ gdb.GdbError('ERROR: Invoking abstract method \'get_argtypes\'.')
+
+ def invoke(self, obj, *args):
+ gdb.GdbError('ERROR: Invoking abstract method \'invoke\'.')
+
+
+class SimpleDebugMethod(DebugMethod):
+ def __init__(self, name, group, method_function, class_matcher,
+ method_matcher, *argtypes):
+ DebugMethod.__init__(self, name, group)
+ self._method_function = method_function
+ self._class_matcher = class_matcher
+ self._method_matcher = method_matcher
+ self._argtypes = argtypes
+
+ def match_class(self, class_type):
+ if re.match(self._class_matcher, str(class_type.unqualified().tag)):
+ return True
+ else:
+ return False
+
+ def match_method(self, method_name):
+ if re.match(self._method_matcher, method_name):
+ return True
+ else:
+ return False
+
+ def get_argtypes(self, class_type, method_name):
+ return self._argtypes
+
+ def invoke(self, obj, *args):
+ return self._method_function(obj, *args)
+
+
+def get_matching_methods(class_type, method_name):
+ match_list = []
+ for objfile in gdb.objfiles():
+ for debug_method in objfile.debug_methods:
+ if (debug_method.enabled and
+ debug_method.match_class(class_type) and
+ debug_method.match_method(method_name)):
+ match_list.append(debug_method)
+ return match_list
+
+
+def get_method_argtypes(debug_method, class_type, method_name):
+ return debug_method.get_argtypes(class_type, method_name)
+
+
+def invoke_debug_method(debug_method, obj, args):
+ return debug_method.invoke(obj, *args)
+
+
+def register_debug_methods(objfile, debug_methods):
+ existing_method_list = []
+ for new_method in debug_methods:
+ for old_method in objfile.debug_methods:
+ if (new_method.group == old_method.group and
+ new_method.name == old_method.name):
+ print ('WARNING: Replacing debug method with name "%s" in '
+ 'group "%s".' % (old_method.name, old_method.group))
+ existing_method_list.append(old_method)
+ for old_method in existing_method_list:
+ objfile.debug_methods.remove(old_method)
+ objfile.debug_methods.extend(debug_methods)
+
+
+# Python debug methods feature will be enabled when a user imports this
+# module.
+gdb.enable_debug_methods(get_matching_methods, get_method_argtypes,
+ invoke_debug_method)
diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c
new file mode 100644
index 0000000..d160695
--- /dev/null
+++ b/gdb/python/py-debugmethods.c
@@ -0,0 +1,212 @@
+/* Support for debug methods in Python.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "ext-function.h"
+#include "value.h"
+#include "language.h"
+#include "python-internal.h"
+#include "python.h"
+
+struct py_ext_object
+{
+ PyObject *object;
+ PyObject *match_py_obj_type;
+ const char *match_method;
+};
+
+PyObject *match_methods_callable = NULL;
+PyObject *get_argtypes_callable = NULL;
+PyObject *invoke_method_callable = NULL;
+
+PyObject *
+gdbpy_enable_debug_methods (PyObject *self, PyObject *args)
+{
+ PyObject *match_methods_temp, *get_argtypes_temp, *invoke_method_temp;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple (args, "OOO", &match_methods_temp, &get_argtypes_temp,
+ &invoke_method_temp))
+ return result;
+
+ if (PyCallable_Check (match_methods_temp)
+ && PyCallable_Check (get_argtypes_temp)
+ && PyCallable_Check (invoke_method_temp))
+ {
+ Py_XDECREF (match_methods_callable);
+ Py_XDECREF (get_argtypes_callable);
+ Py_XDECREF (invoke_method_callable);
+
+ Py_INCREF (match_methods_temp);
+ Py_INCREF (get_argtypes_temp);
+ Py_INCREF (invoke_method_temp);
+
+ match_methods_callable = match_methods_temp;
+ get_argtypes_callable = get_argtypes_temp;
+ invoke_method_callable = invoke_method_temp;
+
+ Py_INCREF (Py_None);
+ result = Py_None;
+ }
+
+ return result;
+}
+
+static struct ext_fn_descriptor *
+new_python_ext_method (PyObject *item, PyObject *py_obj_type,
+ const char *method)
+{
+ struct ext_fn_descriptor *ext_fn = new_ext_function ();
+ struct py_ext_object *ext_object;
+
+ ext_object = (struct py_ext_object *) xzalloc (sizeof (struct py_ext_object));
+ ext_object->object = item;
+ ext_object->match_py_obj_type = py_obj_type;
+ ext_object->match_method = method;
+
+ ext_fn->lang = EXT_LANG_PYTHON;
+ ext_fn->is_method = 1;
+ ext_fn->ext_object = (void *) ext_object;
+
+ return ext_fn;
+}
+
+void
+py_free_ext_object (void *ext_object)
+{
+ struct py_ext_object *o = (struct py_ext_object *) ext_object;
+
+ Py_XDECREF (o->object);
+ xfree (o);
+}
+
+struct ext_fn_descriptor *
+py_debugmethod_name_match (struct type *obj_type, const char *method_name)
+{
+ PyObject *py_type, *py_debugmethod_list = NULL, *list_iter, *item;
+ struct cleanup *cleanups;
+ struct ext_fn_descriptor *method_list = NULL;
+
+ if (!obj_type)
+ return NULL;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ py_type = type_to_type_object (obj_type);
+ py_debugmethod_list = PyObject_CallFunction (match_methods_callable,
+ "Os",
+ py_type, method_name);
+
+ list_iter = PyObject_GetIter (py_debugmethod_list);
+ while ((item = PyIter_Next (list_iter)))
+ {
+ struct ext_fn_descriptor *ext_fn;
+
+ ext_fn = new_python_ext_method (item, py_type, method_name);
+ method_list = ext_fn_list_prepend (method_list, ext_fn);
+ }
+
+ Py_DECREF (list_iter);
+ Py_DECREF (py_debugmethod_list);
+
+ do_cleanups (cleanups);
+ return method_list;
+}
+
+struct type **
+py_ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs)
+{
+ if (ext_fn->is_method)
+ {
+ struct py_ext_object *ext_object =
+ (struct py_ext_object *) ext_fn->ext_object;
+ PyObject *py_argtype_list, *list_iter, *item;
+ struct cleanup *cleanups;
+ struct type **type_array, *obj_type;
+ int i = 1;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+ py_argtype_list = PyObject_CallFunction (get_argtypes_callable,
+ "OOs",
+ ext_object->object,
+ ext_object->match_py_obj_type,
+ ext_object->match_method);
+
+ list_iter = PyObject_GetIter (py_argtype_list);
+ *nargs = PySequence_Size (py_argtype_list) + 1; /* Include 'this' */
+ type_array = (struct type **) xmalloc (*nargs * sizeof (struct type *));
+ while ((item = PyIter_Next (list_iter)))
+ {
+ type_array[i] = type_object_to_type (item);
+ i++;
+ Py_XDECREF (item);
+ }
+
+ /* Add the type of 'this' as the first argument. */
+ obj_type = type_object_to_type (ext_object->match_py_obj_type);
+ type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL);
+
+ Py_XDECREF (list_iter);
+ Py_XDECREF (py_argtype_list);
+ do_cleanups (cleanups);
+
+ return type_array;
+ }
+
+ *nargs = 0;
+ return NULL;
+}
+
+struct value *
+py_ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj,
+ struct value **args, int nargs)
+{
+ int i;
+ struct cleanup *cleanups;
+ PyObject *py_obj, *py_arg_tuple, *py_result, *debug_method;
+ struct value *result = NULL;
+
+ if (!ext_fn->is_method)
+ return NULL;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ debug_method = ((struct py_ext_object *) ext_fn->ext_object)->object;
+ py_obj = value_to_value_object (obj);
+ py_arg_tuple = PyTuple_New (nargs);
+ for (i = 0; i < nargs; i++)
+ {
+ PyObject *py_arg_value = value_to_value_object (args[i]);
+
+ PyTuple_SetItem (py_arg_tuple, i, py_arg_value);
+ }
+
+ py_result = PyObject_CallFunction (invoke_method_callable, "OOO",
+ debug_method, py_obj, py_arg_tuple);
+
+ result = convert_value_from_python (py_result);
+
+ Py_XDECREF (py_obj);
+ Py_XDECREF (py_arg_tuple);
+ Py_XDECREF (py_result);
+ do_cleanups (cleanups);
+
+ return result;
+}
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index db51f50..c2bef3e 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -35,6 +35,9 @@ typedef struct
/* The type-printer list. */
PyObject *type_printers;
+
+ /* The debug method list. */
+ PyObject *debug_methods;
} objfile_object;
static PyTypeObject objfile_object_type;
@@ -62,6 +65,7 @@ objfpy_dealloc (PyObject *o)
Py_XDECREF (self->printers);
Py_XDECREF (self->type_printers);
+ Py_XDECREF (self->debug_methods);
Py_TYPE (self)->tp_free (self);
}
@@ -87,6 +91,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debug_methods = PyList_New (0);
+ if (!self->debug_methods)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
}
return (PyObject *) self;
}
@@ -140,6 +151,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
+/* Get the 'debug_methods' attribute. */
+
+static PyObject *
+objfpy_get_debug_methods (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->debug_methods);
+ return self->debug_methods;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -232,6 +254,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->debug_methods = PyList_New (0);
+ if (!object->debug_methods)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
set_objfile_data (objfile, objfpy_objfile_data_key, object);
}
}
@@ -272,6 +301,8 @@ static PyGetSetDef objfile_getset[] =
"Pretty printers.", NULL },
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
"Type printers.", NULL },
+ { "debug_methods", objfpy_get_debug_methods, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 7d52c06..b457a6c 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -231,6 +231,7 @@ PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
PyObject *gdbpy_parameter (PyObject *self, PyObject *args);
PyObject *gdbpy_parameter_value (enum var_types type, void *var);
+PyObject *gdbpy_enable_debug_methods (PyObject *self, PyObject *args);
char *gdbpy_parse_command_name (const char *name,
struct cmd_list_element ***base_list,
struct cmd_list_element **start_list);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b0f71a2..43a8b9c 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1825,6 +1825,9 @@ Return the selected inferior object." },
{ "inferiors", gdbpy_inferiors, METH_NOARGS,
"inferiors () -> (gdb.Inferior, ...).\n\
Return a tuple containing all inferiors." },
+ { "enable_debug_methods", gdbpy_enable_debug_methods, METH_VARARGS,
+ "inferiors (meth_match_function, args_match_function) -> None.\n\
+Enables Python debug methods feature." },
{NULL, NULL, 0, NULL}
};
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 24e3077..d4adafe 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -49,6 +49,17 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
+struct ext_fn_descriptor *py_debugmethod_name_match (struct type *,
+ const char *);
+
+void py_free_ext_object (void *ext_object);
+
+struct type **py_ext_fn_get_argtypes (struct ext_fn_descriptor *, int *);
+
+struct value *py_ext_fn_invoke_method (struct ext_fn_descriptor *,
+ struct value *,
+ struct value **, int nargs);
+
void *start_type_printers (void);
char *apply_type_printers (void *, struct type *type);
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 074cf36..7f53de7 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -30,6 +30,7 @@
#include <math.h>
#include "infcall.h"
#include "exceptions.h"
+#include "ext-function.h"
/* Define whether or not the C operator '/' truncates towards zero for
differently signed operands (truncation direction is undefined in C). */
@@ -285,21 +286,27 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1)
explicitly, and perform correct overload resolution in all of the above
situations or combinations thereof. */
-static struct value *
+static void
value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
+ struct value **src_fn,
+ struct ext_fn_descriptor **ext_fn,
int *static_memfuncp)
{
struct symbol *symp = NULL;
struct value *valp = NULL;
+ struct ext_fn_descriptor *ext_fnp = NULL;
find_overload_match (args, nargs, operator, BOTH /* could be method */,
0 /* strict match */, &args[0], /* objp */
NULL /* pass NULL symbol since symbol is unknown */,
- &valp, &symp, static_memfuncp, 0);
+ &valp, &symp, &ext_fnp, static_memfuncp, 0);
if (valp)
- return valp;
+ {
+ *src_fn = valp;
+ return;
+ }
if (symp)
{
@@ -307,7 +314,14 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
expect a reference as its first argument
rather the explicit structure. */
args[0] = value_ind (args[0]);
- return value_of_variable (symp, 0);
+ *src_fn = value_of_variable (symp, 0);
+ return;
+ }
+
+ if (ext_fnp)
+ {
+ *ext_fn = ext_fnp;
+ return;
}
error (_("Could not find %s."), operator);
@@ -316,19 +330,22 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
/* Lookup user defined operator NAME. Return a value representing the
function, otherwise return NULL. */
-static struct value *
+static void
value_user_defined_op (struct value **argp, struct value **args, char *name,
- int *static_memfuncp, int nargs)
+ int *static_memfuncp, int nargs,
+ struct value **src_fn, struct ext_fn_descriptor **ext_fn)
{
struct value *result = NULL;
if (current_language->la_language == language_cplus)
- result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp);
+ value_user_defined_cpp_op (args, nargs, name, src_fn, ext_fn,
+ static_memfuncp);
else
- result = value_struct_elt (argp, args, name, static_memfuncp,
- "structure");
-
- return result;
+ {
+ result = value_struct_elt (argp, args, name, static_memfuncp,
+ "structure");
+ *src_fn = result;
+ }
}
/* We know either arg1 or arg2 is a structure, so try to find the right
@@ -345,6 +362,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
enum exp_opcode otherop, enum noside noside)
{
struct value **argvec;
+ struct ext_fn_descriptor *ext_fn = NULL;
char *ptr;
char tstr[13];
int static_memfuncp;
@@ -359,6 +377,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
error (_("Can't do that binary op on that type")); /* FIXME be explicit */
argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+ argvec[0] = NULL;
argvec[1] = value_addr (arg1);
argvec[2] = arg2;
argvec[3] = 0;
@@ -471,8 +490,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
error (_("Invalid binary operation specified."));
}
- argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
- &static_memfuncp, 2);
+ value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, 2,
+ &argvec[0], &ext_fn);
if (argvec[0])
{
@@ -492,6 +511,11 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
return call_function_by_hand (argvec[0], 2 - static_memfuncp,
argvec + 1);
}
+ if (ext_fn)
+ {
+ if (ext_fn->is_method)
+ return ext_fn_invoke_method (ext_fn, arg1, &arg2, 1);
+ }
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
#ifdef lint
@@ -510,6 +534,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
{
struct gdbarch *gdbarch = get_type_arch (value_type (arg1));
struct value **argvec;
+ struct ext_fn_descriptor *ext_fn;
char *ptr, *mangle_ptr;
char tstr[13], mangle_tstr[13];
int static_memfuncp, nargs;
@@ -523,6 +548,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
error (_("Can't do that unary op on that type")); /* FIXME be explicit */
argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+ argvec[0] = NULL;
argvec[1] = value_addr (arg1);
argvec[2] = 0;
@@ -575,8 +601,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
error (_("Invalid unary operation specified."));
}
- argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
- &static_memfuncp, nargs);
+ value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, nargs,
+ &argvec[0], &ext_fn);
if (argvec[0])
{
@@ -595,6 +621,11 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
return value_zero (return_type, VALUE_LVAL (arg1));
}
return call_function_by_hand (argvec[0], nargs, argvec + 1);
+ }
+ if (ext_fn)
+ {
+ if (ext_fn->is_method)
+ return ext_fn_invoke_method (ext_fn, arg1, NULL, 0);
}
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index e3d36a1..2cc67d9 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -45,6 +45,7 @@
#include "objfiles.h"
#include "symtab.h"
#include "exceptions.h"
+#include "ext-function.h"
extern unsigned int overload_debug;
/* Local functions. */
@@ -73,8 +74,8 @@ int find_oload_champ_namespace_loop (struct value **, int,
const int no_adl);
static int find_oload_champ (struct value **, int, int, int,
- struct fn_field *, struct symbol **,
- struct badness_vector **);
+ struct fn_field *, struct ext_fn_descriptor *,
+ struct symbol **, struct badness_vector **);
static int oload_method_static (int, struct fn_field *, int);
@@ -101,9 +102,10 @@ static CORE_ADDR allocate_space_in_inferior (int);
static struct value *cast_into_complex (struct type *, struct value *);
-static struct fn_field *find_method_list (struct value **, const char *,
- int, struct type *, int *,
- struct type **, int *);
+static void find_method_list (struct value **, const char *,
+ int, struct type *, struct fn_field **, int *,
+ struct ext_fn_descriptor **,
+ struct type **, int *);
void _initialize_valops (void);
@@ -2433,40 +2435,53 @@ value_struct_elt (struct value **argp, struct value **args,
method is found.
BOFFSET is the offset of the base subobject where the method is found. */
-static struct fn_field *
+static void
find_method_list (struct value **argp, const char *method,
- int offset, struct type *type, int *num_fns,
+ int offset, struct type *type,
+ struct fn_field **fn_list, int *num_fns,
+ struct ext_fn_descriptor **ext_fn_list,
struct type **basetype, int *boffset)
{
int i;
- struct fn_field *f;
- CHECK_TYPEDEF (type);
+ struct fn_field *f = NULL;
+ struct ext_fn_descriptor *ef = NULL;
- *num_fns = 0;
+ CHECK_TYPEDEF (type);
/* First check in object itself. */
- for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
- {
- /* pai: FIXME What about operators and type conversions? */
- const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
- if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0))
- {
- int len = TYPE_FN_FIELDLIST_LENGTH (type, i);
- struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
-
- *num_fns = len;
- *basetype = type;
- *boffset = offset;
-
- /* Resolve any stub methods. */
- check_stub_method_group (type, i);
+ if (fn_list && !(*fn_list))
+ for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
+ {
+ /* pai: FIXME What about operators and type conversions? */
+ const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
+
+ if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0))
+ {
+ int len = TYPE_FN_FIELDLIST_LENGTH (type, i);
+ f = TYPE_FN_FIELDLIST1 (type, i);
+ *fn_list = f;
+
+ *num_fns = len;
+ *basetype = type;
+ *boffset = offset;
+
+ /* Resolve any stub methods. */
+ check_stub_method_group (type, i);
+
+ break;
+ }
+ }
- return f;
- }
+ if (ext_fn_list)
+ {
+ ef = get_matching_ext_methods (type, method);
+ *ext_fn_list = ext_fn_list_extend (*ext_fn_list, ef);
}
- /* Not found in object, check in base subobjects. */
+ /* If source methods are not found in current class, look for them in the
+ base classes. We have to go through the base classes to gather extension
+ methods anyway. */
for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
{
int base_offset;
@@ -2483,13 +2498,11 @@ find_method_list (struct value **argp, const char *method,
{
base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8;
}
- f = find_method_list (argp, method, base_offset + offset,
- TYPE_BASECLASS (type, i), num_fns,
- basetype, boffset);
- if (f)
- return f;
+
+ find_method_list (argp, method, base_offset + offset,
+ TYPE_BASECLASS (type, i), fn_list, num_fns,
+ ext_fn_list, basetype, boffset);
}
- return NULL;
}
/* Return the list of overloaded methods of a specified name.
@@ -2502,9 +2515,11 @@ find_method_list (struct value **argp, const char *method,
method.
BOFFSET is the offset of the base subobject which defines the method. */
-static struct fn_field *
+static void
value_find_oload_method_list (struct value **argp, const char *method,
- int offset, int *num_fns,
+ int offset, struct fn_field **fn_list,
+ int *num_fns,
+ struct ext_fn_descriptor **ext_fn_list,
struct type **basetype, int *boffset)
{
struct type *t;
@@ -2526,8 +2541,32 @@ value_find_oload_method_list (struct value **argp, const char *method,
error (_("Attempt to extract a component of a "
"value that is not a struct or union"));
- return find_method_list (argp, method, 0, t, num_fns,
- basetype, boffset);
+ /* Clear the lists. */
+ if (fn_list)
+ {
+ *fn_list = NULL;
+ *num_fns = 0;
+ }
+ if (ext_fn_list)
+ *ext_fn_list = NULL;
+
+ find_method_list (argp, method, 0, t, fn_list, num_fns, ext_fn_list,
+ basetype, boffset);
+}
+
+static struct type *
+value_has_indirect_dynamic_type (struct value *obj)
+{
+ struct type *stype, *dtype, *dtype_ind;
+
+ stype = check_typedef (TYPE_TARGET_TYPE (value_type (obj)));
+ dtype_ind = value_rtti_indirect_type (obj, NULL, NULL, NULL);
+ dtype = dtype_ind ? check_typedef (TYPE_TARGET_TYPE (dtype_ind)) : stype;
+
+ if (class_types_same_p (stype, dtype))
+ return NULL;
+ else
+ return dtype_ind;
}
/* Given an array of arguments (ARGS) (which includes an
@@ -2576,7 +2615,8 @@ int
find_overload_match (struct value **args, int nargs,
const char *name, enum oload_search_type method,
int lax, struct value **objp, struct symbol *fsym,
- struct value **valp, struct symbol **symp,
+ struct value **valp, struct symbol **symp,
+ struct ext_fn_descriptor **ext_fn,
int *staticp, const int no_adl)
{
struct value *obj = (objp ? *objp : NULL);
@@ -2584,16 +2624,24 @@ find_overload_match (struct value **args, int nargs,
/* Index of best overloaded function. */
int func_oload_champ = -1;
int method_oload_champ = -1;
+ int src_method_oload_champ = -1;
+ int src_method_oload_champ_bkp = -1;
+ int ext_method_oload_champ = -1;
+ int src_and_ext_equal = 0;
/* The measure for the current best match. */
struct badness_vector *method_badness = NULL;
struct badness_vector *func_badness = NULL;
+ struct badness_vector *ext_method_badness = NULL;
+ struct badness_vector *src_method_badness = NULL;
struct value *temp = obj;
/* For methods, the list of overloaded methods. */
struct fn_field *fns_ptr = NULL;
/* For non-methods, the list of overloaded function symbols. */
struct symbol **oload_syms = NULL;
+ /* For extension functions, the list of extension function descriptors. */
+ struct ext_fn_descriptor *ext_fn_list = NULL;
/* Number of overloaded instances being considered. */
int num_fns = 0;
struct type *basetype = NULL;
@@ -2605,6 +2653,8 @@ find_overload_match (struct value **args, int nargs,
const char *func_name = NULL;
enum oload_classification match_quality;
enum oload_classification method_match_quality = INCOMPATIBLE;
+ enum oload_classification src_method_match_quality = INCOMPATIBLE;
+ enum oload_classification ext_method_match_quality = INCOMPATIBLE;
enum oload_classification func_match_quality = INCOMPATIBLE;
/* Get the list of overloaded methods or functions. */
@@ -2633,12 +2683,13 @@ find_overload_match (struct value **args, int nargs,
}
/* Retrieve the list of methods with the name NAME. */
- fns_ptr = value_find_oload_method_list (&temp, name,
- 0, &num_fns,
- &basetype, &boffset);
+ value_find_oload_method_list (&temp, name, 0, &fns_ptr, &num_fns,
+ ext_fn ? &ext_fn_list : NULL,
+ &basetype, &boffset);
/* If this is a method only search, and no methods were found
the search has faild. */
- if (method == METHOD && (!fns_ptr || !num_fns))
+ if (method == METHOD && (!fns_ptr || !num_fns)
+ && (ext_fn && !ext_fn_list))
error (_("Couldn't find method %s%s%s"),
obj_type_name,
(obj_type_name && *obj_type_name) ? "::" : "",
@@ -2649,18 +2700,81 @@ find_overload_match (struct value **args, int nargs,
if (fns_ptr)
{
gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL);
- method_oload_champ = find_oload_champ (args, nargs, method,
- num_fns, fns_ptr,
- oload_syms, &method_badness);
-
- method_match_quality =
- classify_oload_match (method_badness, nargs,
- oload_method_static (method, fns_ptr,
- method_oload_champ));
-
- make_cleanup (xfree, method_badness);
+ src_method_oload_champ = find_oload_champ (args, nargs, method,
+ num_fns, fns_ptr, NULL,
+ oload_syms,
+ &src_method_badness);
+
+ src_method_match_quality =
+ classify_oload_match (
+ src_method_badness, nargs,
+ oload_method_static (method, fns_ptr,
+ src_method_oload_champ));
+
+ make_cleanup (xfree, src_method_badness);
}
+ if (ext_fn_list)
+ {
+ ext_method_oload_champ = find_oload_champ (args, nargs, method,
+ 0, NULL, ext_fn_list,
+ NULL, &ext_method_badness);
+ ext_method_match_quality = classify_oload_match (ext_method_badness,
+ nargs, 0);
+ make_cleanup (xfree, ext_method_badness);
+ }
+
+ if (src_method_oload_champ >= 0 && ext_method_oload_champ >= 0)
+ {
+ switch (compare_badness (ext_method_badness, src_method_badness))
+ {
+ case 0: /* Src method and ext method are equally good. */
+ src_and_ext_equal = 1;
+ case 1: /* Src method and ext method are incompatible */
+ /* if ext method match is not standard, then let source method
+ win. */
+ if (ext_method_match_quality != STANDARD)
+ {
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ ext_method_oload_champ = -1;
+ method_match_quality = src_method_match_quality;
+ break;
+ }
+ case 2: /* Ext method is champion. */
+ method_oload_champ = ext_method_oload_champ;
+ method_badness = ext_method_badness;
+ src_method_oload_champ_bkp = src_method_oload_champ;
+ src_method_oload_champ = -1;
+ method_match_quality = ext_method_match_quality;
+ break;
+ case 3: /* Src method is champion. */
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ ext_method_oload_champ = -1;
+ method_match_quality = src_method_match_quality;
+ break;
+ default:
+ error (_("Internal error: unexpected overload comparison "
+ "result"));
+ break;
+ }
+ }
+ else
+ {
+ if (src_method_oload_champ >= 0)
+ {
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ method_match_quality = src_method_match_quality;
+ }
+ if (ext_method_oload_champ >= 0)
+ {
+ method_oload_champ = ext_method_oload_champ;
+ method_badness = ext_method_badness;
+ method_match_quality = ext_method_match_quality;
+ }
+ }
}
if (method == NON_METHOD || method == BOTH)
@@ -2803,21 +2917,6 @@ find_overload_match (struct value **args, int nargs,
func_name);
}
- if (staticp != NULL)
- *staticp = oload_method_static (method, fns_ptr, method_oload_champ);
-
- if (method_oload_champ >= 0)
- {
- if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ))
- *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ,
- basetype, boffset);
- else
- *valp = value_fn_field (&temp, fns_ptr, method_oload_champ,
- basetype, boffset);
- }
- else
- *symp = oload_syms[func_oload_champ];
-
if (objp)
{
struct type *temp_type = check_typedef (value_type (temp));
@@ -2827,10 +2926,66 @@ find_overload_match (struct value **args, int nargs,
&& (TYPE_CODE (objtype) == TYPE_CODE_PTR
|| TYPE_CODE (objtype) == TYPE_CODE_REF))
{
- temp = value_addr (temp);
+ *objp = value_addr (temp);
}
- *objp = temp;
+ else
+ *objp = temp;
+ }
+
+ if (staticp != NULL)
+ *staticp = oload_method_static (method, fns_ptr, method_oload_champ);
+
+ if (method_oload_champ >= 0)
+ {
+ if (src_method_oload_champ >= 0)
+ {
+ if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ))
+ {
+ struct type *dtype;
+
+ dtype = value_has_indirect_dynamic_type (args[0]);
+ if (dtype)
+ {
+ args[0] = value_cast (dtype, args[0]);
+ do_cleanups (all_cleanups);
+ return find_overload_match (args, nargs, name, method, lax,
+ objp, fsym, valp, symp, ext_fn,
+ staticp, no_adl);
+ }
+ else
+ *valp = value_virtual_fn_field (&temp, fns_ptr,
+ method_oload_champ,
+ basetype, boffset);
+ }
+ else
+ *valp = value_fn_field (&temp, fns_ptr, method_oload_champ,
+ basetype, boffset);
+ }
+ else
+ {
+ if (src_and_ext_equal
+ && TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, src_method_oload_champ_bkp))
+ {
+ struct type *dtype;
+
+ dtype = value_has_indirect_dynamic_type (args[0]);
+ if (dtype)
+ {
+ args[0] = value_cast (dtype, args[0]);
+ do_cleanups (all_cleanups);
+ return find_overload_match (args, nargs, name, method, lax,
+ objp, fsym, valp, symp, ext_fn,
+ staticp, no_adl);
+ }
+ }
+
+ *ext_fn = ext_fn_list_get (ext_fn_list, ext_method_oload_champ);
+ ext_fn_list = ext_fn_list_remove (ext_fn_list, *ext_fn);
+ ext_fn_list_free (ext_fn_list);
+ }
}
+ else
+ *symp = oload_syms[func_oload_champ];
do_cleanups (all_cleanups);
@@ -2965,7 +3120,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
++num_fns;
new_oload_champ = find_oload_champ (args, nargs, 0, num_fns,
- NULL, new_oload_syms,
+ NULL, NULL, new_oload_syms,
&new_oload_champ_bv);
/* Case 1: We found a good match. Free earlier matches (if any),
@@ -3013,6 +3168,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
static int
find_oload_champ (struct value **args, int nargs, int method,
int num_fns, struct fn_field *fns_ptr,
+ struct ext_fn_descriptor *ext_fn_list,
struct symbol **oload_syms,
struct badness_vector **oload_champ_bv)
{
@@ -3028,16 +3184,24 @@ find_oload_champ (struct value **args, int nargs, int method,
*oload_champ_bv = NULL;
/* Consider each candidate in turn. */
- for (ix = 0; ix < num_fns; ix++)
+ for (ix = 0; (ix < num_fns) || ext_fn_list; ix++)
{
int jj;
- int static_offset = oload_method_static (method, fns_ptr, ix);
+ int static_offset = 0;
int nparms;
struct type **parm_types;
+ struct ext_fn_descriptor *ext_fn = ext_fn_list;
+
+ if (ext_fn_list)
+ ext_fn_list = ext_fn_list->next;
if (method)
{
- nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
+ if (fns_ptr)
+ {
+ static_offset = oload_method_static (method, fns_ptr, ix);
+ nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
+ }
}
else
{
@@ -3046,13 +3210,20 @@ find_oload_champ (struct value **args, int nargs, int method,
}
/* Prepare array of parameter types. */
- parm_types = (struct type **)
- xmalloc (nparms * (sizeof (struct type *)));
- for (jj = 0; jj < nparms; jj++)
- parm_types[jj] = (method
- ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
- : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
- jj));
+ if (fns_ptr || oload_syms)
+ {
+ parm_types = (struct type **)
+ xmalloc (nparms * (sizeof (struct type *)));
+ for (jj = 0; jj < nparms; jj++)
+ parm_types[jj] = (method
+ ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
+ : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
+ jj));
+ }
+ else
+ {
+ parm_types = ext_fn_get_argtypes (ext_fn, &nparms);
+ }
/* Compare parameter types to supplied argument types. Skip
THIS for static methods. */
diff --git a/gdb/value.h b/gdb/value.h
index 67f1d04..08991f0 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -31,6 +31,7 @@ struct type;
struct ui_file;
struct language_defn;
struct value_print_options;
+struct ext_fn_descriptor;
/* The structure which defines the type of a value. It should never
be possible for a program lval value to survive over a call to the
@@ -648,6 +649,7 @@ extern int find_overload_match (struct value **args, int nargs,
enum oload_search_type method, int lax,
struct value **objp, struct symbol *fsym,
struct value **valp, struct symbol **symp,
+ struct ext_fn_descriptor **ext_fn,
int *staticp, const int no_adl);
extern struct value *value_field (struct value *arg1, int fieldno);
[-- Attachment #3: dm.cc --]
[-- Type: application/octet-stream, Size: 1251 bytes --]
#include <iostream>
using namespace std;
namespace dop
{
class A
{
public:
int a;
int array[10];
virtual ~A();
int operator+(const A &obj);
virtual int operator-(const A &obj);
virtual int geta();
};
A::~A()
{
}
int A::operator+(const A &obj)
{
cout << "From C++, A + A" << endl;
return a + obj.a;
}
int A::operator-(const A &obj)
{
cout << "From C++, A - A" << endl;
return a - obj.a;
}
int A::geta(void)
{
return a;
}
class B : public A
{
public:
virtual int operator*(const B &obj);
};
int
B::operator*(const B &obj)
{
cout << "From C++, B * B" << endl;
return a * obj.a;
}
typedef B Bt;
typedef Bt Btt;
class C : public Bt
{
public:
virtual ~C();
};
C::~C() { }
class D : public B
{
public:
virtual int operator*(const B &obj);
};
int
D::operator*(const B &obj)
{
cout << "From C++, D * D" << endl;
return a * obj.a;
}
}
using namespace dop;
int func(const A &a)
{
return a.a;
}
int main(void)
{
A a1, a2;
a1.a = 5;
a2.a = 10;
C c1;
c1.a = 20;
B b1;
b1.a = 30;
D d1;
d1.a = 50;
Bt bt;
bt.a = 40;
A &ref_c = c1;
B &ref_d = d1;
Btt btt;
btt.a = -5;
for (int i = 0; i < 10; i++)
{
a1.array[i] = a2.array[i] = i;
}
return ref_c - ref_d;
}
[-- Attachment #4: dm-gdb.py --]
[-- Type: application/octet-stream, Size: 2401 bytes --]
from gdb.debugmethods import SimpleDebugMethod
def A_plus_A(obj, opr):
print 'From Python (A_plus_A)'
return obj['a'] + opr['a']
def plus_plus_A(obj):
return obj['a'] + 1
def C_minus_C(obj, opr):
print 'From Python (C_minus_C)'
return obj['a'] - opr['a']
def B_star_B(obj, opr):
print 'From Python (B_star_B)'
return obj['a'] * opr['a']
def A_geta(obj):
print 'From Python (A_geta)'
return obj['a']
def A_getarray(obj, index):
print 'From Python (A_getarray)'
return obj['array'][index]
type_A = gdb.parse_and_eval('(dop::A *) 0').type.target()
type_B = gdb.parse_and_eval('(dop::B *) 0').type.target()
type_C = gdb.parse_and_eval('(dop::C *) 0').type.target()
type_int = gdb.parse_and_eval('(int *) 0').type.target()
dm_list = [
SimpleDebugMethod('A_plus_A',
'DEFAULT_DEBUG_METHOD_GROUP',
A_plus_A,
'^dop::A$',
'operator\+',
# This is a replacement, hence match the arg type
# exactly!
type_A.const().reference()),
SimpleDebugMethod('plus_plus_A',
'DEFAULT_DEBUG_METHOD_GROUP',
plus_plus_A,
'^dop::A$',
'operator\+\+'),
SimpleDebugMethod('C_minus_C',
'DEFAULT_DEBUG_METHOD_GROUP',
C_minus_C,
'^dop::C$',
'operator\-',
type_C),
SimpleDebugMethod('B_star_B',
'DEFAULT_DEBUG_METHOD_GROUP',
B_star_B,
'^dop::B$',
'operator\*',
# This is a replacement, hence match the arg type
# exactly!
type_B.const().reference()),
SimpleDebugMethod('A_geta',
'DEFAULT_DEBUG_METHOD_GROUP',
A_geta,
'^dop::A$',
'^geta$'),
SimpleDebugMethod('A_getarray',
'DEFAULT_DEBUG_METHOD_GROUP',
A_getarray,
'^dop::A$',
'^getarray$',
type_int),
]
gdb.debugmethods.register_debug_methods(gdb.current_objfile(), dm_list)
debug_methods = gdb.current_objfile().debug_methods
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [RFC] Debug Methods in GDB Python 2013-01-07 21:22 [RFC] Debug Methods in GDB Python Siva Chandra @ 2013-01-29 1:51 ` Siva Chandra 2013-02-25 23:02 ` Siva Chandra 2013-05-10 19:33 ` Tom Tromey 0 siblings, 2 replies; 18+ messages in thread From: Siva Chandra @ 2013-01-29 1:51 UTC (permalink / raw) To: gdb-patches [-- Attachment #1: Type: text/plain, Size: 2356 bytes --] Attached is a new version of the patch which I have now regression tested. Except for tests and docs, I think it is complete wrt the feature set I have in mind. Hence, the caveats from the previous mail still apply. 2013-01-01 Siva Chandra Reddy <sivachandra@google.com> * Makefile.in: Add entries for new files * data-directory/Makefile.in: Add entery for new Python file * eval.c: Use new 'find_overload_match' signature. * ext-function.c: Support for working with functions/methods defined in an extension language. * ext-function.h: Support for working with functions/methods defined in an extension language. * python/lib/gdb/debugmethods.py: Python side of the support for debug methods in Python. * python/py-debugmethods.c: C side of the support for debug methods in Python. * python/py-objfile.c: Add 'debug_methods' attribute to gdb.Objfile. * python/python-internal.h: Add new function gdb.enable_debug_methods to the Python module 'gdb'. * python/python.c: Add new function gdb.enable_debug_methods to the Python module 'gdb'. * python/python.h: Add declarations of new functions. * valarith.c: Use methods defined in extension languages. * valops.c: Use methods defined in extension languages. * value.h: New signature for 'find_overload_match'. On Mon, Jan 7, 2013 at 1:21 PM, Siva Chandra <sivachandra@google.com> wrote: > 1. The patch is incomplete in the sense that tests and documentation > are missing. Also, ways to enable and disable single or group of debug > methods is missing. At this point, I am looking for feedback on my > direction and the way I am intercepting the normal GDB flow to lookup > methods defined in Python. I will work on completing the patch once > there is an agreement on these basics. > > 2. I did not intend to provide a way for the user to define new or > override static methods via Python. > > 3. I did not intend to provide a way for the user to define new > virtual methods in Python. The user can however override existing > virtual and non-virtual methods. The user can also define new > non-virtual methods in Python. > > 4. Doug suggested that I use the C++ infrastructure in GDB as much as > possible. I feel I have done so. [-- Attachment #2: dm_patch_v2.txt --] [-- Type: text/plain, Size: 52409 bytes --] diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 68d545e..c41bf8c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -278,6 +278,7 @@ SUBDIR_PYTHON_OBS = \ py-breakpoint.o \ py-cmd.o \ py-continueevent.o \ + py-debugmethods.o \ py-event.o \ py-evtregistry.o \ py-evts.o \ @@ -312,6 +313,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-breakpoint.c \ python/py-cmd.c \ python/py-continueevent.c \ + python/py-debugmethods.c \ python/py-event.c \ python/py-evtregistry.c \ python/py-evts.c \ @@ -712,7 +714,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 ext-function.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 \ @@ -811,7 +813,7 @@ doublest.h regset.h hppa-tdep.h ppc-linux-tdep.h rs6000-tdep.h \ common/gdb_locale.h common/gdb_dirent.h arch-utils.h trad-frame.h gnu-nat.h \ language.h nbsd-tdep.h solib-svr4.h \ macroexp.h ui-file.h regcache.h gdb_string.h tracepoint.h i386-tdep.h \ -inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \ +inf-child.h p-lang.h event-top.h ext-function.h gdbtypes.h user-regs.h \ regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \ config/i386/nm-fbsd.h \ config/nm-nto.h config/sparc/nm-sol2.h config/nm-linux.h \ @@ -927,7 +929,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ inferior.o osdata.o gdb_usleep.o record.o gcore.o \ gdb_vecs.o jit.o progspace.o skip.o probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ - format.o registry.o + format.o registry.o ext-function.o TSOBS = inflow.o @@ -2077,6 +2079,10 @@ py-continueevent.o: $(srcdir)/python/py-continueevent.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c $(POSTCOMPILE) +py-debugmethods.o: $(srcdir)/python/py-debugmethods.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-debugmethods.c + $(POSTCOMPILE) + py-event.o: $(srcdir)/python/py-event.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c $(POSTCOMPILE) diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index d98ac77..04e5e52 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -56,6 +56,7 @@ PYTHON_FILES = \ gdb/types.py \ gdb/printing.py \ gdb/prompt.py \ + gdb/debugmethods.py \ gdb/command/__init__.py \ gdb/command/type_printers.py \ gdb/command/pretty_printers.py \ diff --git a/gdb/eval.c b/gdb/eval.c index f4b39cb..3c2c67f 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -22,6 +22,7 @@ #include "symtab.h" #include "gdbtypes.h" #include "value.h" +#include "ext-function.h" #include "expression.h" #include "target.h" #include "frame.h" @@ -1592,7 +1593,7 @@ evaluate_subexp_standard (struct type *expect_type, NON_METHOD, /* not method */ NULL, NULL, /* pass NULL symbol since symbol is unknown */ - NULL, &symp, NULL, 0); + NULL, &symp, NULL, NULL, 0); /* Now fix the expression being evaluated. */ exp->elts[save_pos1 + 2].symbol = symp; @@ -1622,11 +1623,12 @@ evaluate_subexp_standard (struct type *expect_type, /* Language is C++, do some overload resolution before evaluation. */ struct value *valp = NULL; + struct ext_fn_descriptor *ext_fnp = NULL; (void) find_overload_match (&argvec[1], nargs, tstr, METHOD, /* method */ &arg2, /* the object */ - NULL, &valp, NULL, + NULL, &valp, NULL, &ext_fnp, &static_memfuncp, 0); if (op == OP_SCOPE && !static_memfuncp) @@ -1636,9 +1638,19 @@ evaluate_subexp_standard (struct type *expect_type, "`this' pointer"), function_name); } - argvec[1] = arg2; /* the ``this'' pointer */ - argvec[0] = valp; /* Use the method found after overload - resolution. */ + + if (valp) + { + argvec[1] = arg2; /* the ``this'' pointer */ + argvec[0] = valp; /* Use the method found after overload + resolution. */ + } + if (ext_fnp) + { + if (ext_fnp->is_method) + return ext_fn_invoke_method (ext_fnp, arg2, argvec + 2, + nargs - 1); + } } else /* Non-C++ case -- or no overload resolution. */ @@ -1697,7 +1709,7 @@ evaluate_subexp_standard (struct type *expect_type, NULL, /* no need for name */ NON_METHOD, /* not method */ NULL, function, /* the function */ - NULL, &symp, NULL, no_adl); + NULL, &symp, NULL, NULL, no_adl); if (op == OP_VAR_VALUE) { diff --git a/gdb/ext-function.c b/gdb/ext-function.c new file mode 100644 index 0000000..b0e975f --- /dev/null +++ b/gdb/ext-function.c @@ -0,0 +1,166 @@ +/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "ext-function.h" +#include "python/python.h" + +static ULONGEST enabled_ext_languages = EXT_LANG_PYTHON; + +struct ext_fn_descriptor * +new_ext_function (void) +{ + struct ext_fn_descriptor *ext_fn = + (struct ext_fn_descriptor *) xzalloc (sizeof (struct ext_fn_descriptor)); + + ext_fn->next = NULL; + ext_fn->is_method = 0; + ext_fn->lang = EXT_LANG_UNKNOWN; + ext_fn->ext_object = NULL; + + return ext_fn; +} + +void +ext_fn_list_free (void *l) +{ + struct ext_fn_descriptor *list = (struct ext_fn_descriptor *) l; + + while (list) + { + struct ext_fn_descriptor *item = list; + + list = list->next; + + if (item->is_method) + { + if (item->lang == EXT_LANG_PYTHON) + py_free_ext_object (item->ext_object); + } + + xfree (item); + } +} + +struct ext_fn_descriptor * +ext_fn_list_extend (struct ext_fn_descriptor *l1, struct ext_fn_descriptor *l2) +{ + struct ext_fn_descriptor *item = l1; + + if (!l1) + return l2; + + if (!l2) + return l1; + + while (item->next) + item = item->next; + + item->next = l2; + + return l1; +} + +struct ext_fn_descriptor * +ext_fn_list_prepend (struct ext_fn_descriptor *list, + struct ext_fn_descriptor *item) +{ + struct ext_fn_descriptor *last_item; + if (!list) + return item; + + if (!item) + return list; + + item->next = list; + + return item; +} + +struct ext_fn_descriptor * +ext_fn_clone (struct ext_fn_descriptor *ext_fn) +{ + struct ext_fn_descriptor *new_ext_fn = new_ext_function (); + + new_ext_fn->lang = ext_fn->lang; + new_ext_fn->is_method = ext_fn->is_method; +#ifdef HAVE_PYTHON + if ((enabled_ext_languages && EXT_LANG_PYTHON) + && (ext_fn->lang == EXT_LANG_PYTHON)) + new_ext_fn->ext_object = py_clone_ext_object (ext_fn->ext_object); +#endif + + return new_ext_fn; +} + +struct ext_fn_descriptor * +ext_fn_list_get (struct ext_fn_descriptor *list, int index) +{ + int i = 0; + + while (list) + { + if (i == index) + return list; + + i++; + list = list->next; + } + + return NULL; +} + +struct ext_fn_descriptor * +get_matching_ext_methods (struct type *type, const char *method) +{ + struct ext_fn_descriptor *python_methods = NULL; + +#ifdef HAVE_PYTHON + if (enabled_ext_languages && EXT_LANG_PYTHON) + python_methods = py_debugmethod_name_match (type, method); +#endif /* HAVE_PYTHON */ + + return python_methods; +} + +struct type ** +ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs) +{ +#ifdef HAVE_PYTHON + if ((enabled_ext_languages && EXT_LANG_PYTHON) + && (ext_fn->lang == EXT_LANG_PYTHON)) + return py_ext_fn_get_argtypes (ext_fn, nargs); +#endif /* HAVE_PYTHON */ + + *nargs = 0; + return NULL; +} + +struct value * +ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj, + struct value **args, int nargs) +{ +#ifdef HAVE_PYTHON + if ((enabled_ext_languages && EXT_LANG_PYTHON) + && (ext_fn->lang == EXT_LANG_PYTHON)) + return py_ext_fn_invoke_method (ext_fn, obj, args, nargs); +#endif /* HAVE_PYTHON */ + + return NULL; +} diff --git a/gdb/ext-function.h b/gdb/ext-function.h new file mode 100644 index 0000000..0116962 --- /dev/null +++ b/gdb/ext-function.h @@ -0,0 +1,70 @@ +/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */ + +#if !defined (EXT_FUNCTION_H) +#define EXT_FUNCTION_H + +struct value; +struct type; + +enum ext_lang + { + EXT_LANG_UNKNOWN = 0, + /* Values for extension languages are powers of 2. */ + EXT_LANG_GUILE = 1, + EXT_LANG_PYTHON = 2 + /* Next extension language should have value of 4. */ + }; + +struct ext_fn_descriptor + { + enum ext_lang lang; + + int is_method; + + void *ext_object; + + struct ext_fn_descriptor *next; + }; + +extern struct value *ext_fn_invoke_method (struct ext_fn_descriptor *, + struct value *, + struct value **, int nargs); + +extern struct ext_fn_descriptor *ext_fn_clone (struct ext_fn_descriptor *); + +extern struct ext_fn_descriptor *ext_fn_list_get ( + struct ext_fn_descriptor *list, int); + +extern struct ext_fn_descriptor * +ext_fn_list_extend (struct ext_fn_descriptor *, struct ext_fn_descriptor *); + +extern struct ext_fn_descriptor *ext_fn_list_prepend ( + struct ext_fn_descriptor *list, struct ext_fn_descriptor *item); + +extern void ext_fn_list_free (void *list); + +extern struct ext_fn_descriptor *new_ext_function (void); + +extern struct ext_fn_descriptor *get_matching_ext_methods (struct type *, + const char *); + +extern struct type **ext_fn_get_argtypes (struct ext_fn_descriptor *, int *); + +#endif /* EXT_FUNCTION_H */ diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py new file mode 100644 index 0000000..9d0c706 --- /dev/null +++ b/gdb/python/lib/gdb/debugmethods.py @@ -0,0 +1,113 @@ +# Python side of the support for debug methods. +# Copyright (C) 2013 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/>. + +"""Utilities for adding debug methods""" + +import gdb +import re + +DEFAULT_DEBUG_METHOD_GROUP = 'DEFAULT_DEBUG_METHOD_GROUP' + + +class DebugMethod(object): + + def __init__(self, name, group=DEFAULT_DEBUG_METHOD_GROUP): + self._name = name + self.group = group + self.enabled = True + + @property + def name(self): + return self._name + + def match_class(self, class_name): + gdb.GdbError('ERROR: Invoking abstract method \'match_class\'.') + + def match_method(self, method_name): + gdb.GdbError('ERROR: Invoking abstract method \'match_method\'.') + + def get_argtypes(self, class_type, method_name): + gdb.GdbError('ERROR: Invoking abstract method \'get_argtypes\'.') + + def invoke(self, obj, *args): + gdb.GdbError('ERROR: Invoking abstract method \'invoke\'.') + + +class SimpleDebugMethod(DebugMethod): + def __init__(self, name, group, method_function, class_matcher, + method_matcher, *argtypes): + DebugMethod.__init__(self, name, group) + self._method_function = method_function + self._class_matcher = class_matcher + self._method_matcher = method_matcher + self._argtypes = argtypes + + def match_class(self, class_type): + if re.match(self._class_matcher, str(class_type.unqualified().tag)): + return True + else: + return False + + def match_method(self, method_name): + if re.match(self._method_matcher, method_name): + return True + else: + return False + + def get_argtypes(self, class_type, method_name): + return self._argtypes + + def invoke(self, obj, *args): + return self._method_function(obj, *args) + + +def get_matching_methods(class_type, method_name): + match_list = [] + for objfile in gdb.objfiles(): + for debug_method in objfile.debug_methods: + if (debug_method.enabled and + debug_method.match_class(class_type) and + debug_method.match_method(method_name)): + match_list.append(debug_method) + return match_list + + +def get_method_argtypes(debug_method, class_type, method_name): + return debug_method.get_argtypes(class_type, method_name) + + +def invoke_debug_method(debug_method, obj, args): + return debug_method.invoke(obj, *args) + + +def register_debug_methods(objfile, debug_methods): + existing_method_list = [] + for new_method in debug_methods: + for old_method in objfile.debug_methods: + if (new_method.group == old_method.group and + new_method.name == old_method.name): + print ('WARNING: Replacing debug method with name "%s" in ' + 'group "%s".' % (old_method.name, old_method.group)) + existing_method_list.append(old_method) + for old_method in existing_method_list: + objfile.debug_methods.remove(old_method) + objfile.debug_methods.extend(debug_methods) + + +# Python debug methods feature will be enabled when a user imports this +# module. +gdb.enable_debug_methods(get_matching_methods, get_method_argtypes, + invoke_debug_method) diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c new file mode 100644 index 0000000..cfdb0b5 --- /dev/null +++ b/gdb/python/py-debugmethods.c @@ -0,0 +1,231 @@ +/* Support for debug methods in Python. + + 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "arch-utils.h" +#include "ext-function.h" +#include "value.h" +#include "language.h" +#include "python-internal.h" +#include "python.h" + +struct py_ext_object +{ + PyObject *object; + PyObject *match_py_obj_type; + const char *match_method; +}; + +PyObject *match_methods_callable = NULL; +PyObject *get_argtypes_callable = NULL; +PyObject *invoke_method_callable = NULL; + +PyObject * +gdbpy_enable_debug_methods (PyObject *self, PyObject *args) +{ + PyObject *match_methods_temp, *get_argtypes_temp, *invoke_method_temp; + PyObject *result = NULL; + + if (!PyArg_ParseTuple (args, "OOO", &match_methods_temp, &get_argtypes_temp, + &invoke_method_temp)) + return result; + + if (PyCallable_Check (match_methods_temp) + && PyCallable_Check (get_argtypes_temp) + && PyCallable_Check (invoke_method_temp)) + { + Py_XDECREF (match_methods_callable); + Py_XDECREF (get_argtypes_callable); + Py_XDECREF (invoke_method_callable); + + Py_INCREF (match_methods_temp); + Py_INCREF (get_argtypes_temp); + Py_INCREF (invoke_method_temp); + + match_methods_callable = match_methods_temp; + get_argtypes_callable = get_argtypes_temp; + invoke_method_callable = invoke_method_temp; + + Py_INCREF (Py_None); + result = Py_None; + } + + return result; +} + +static struct ext_fn_descriptor * +new_python_ext_method (PyObject *item, PyObject *py_obj_type, + const char *method) +{ + struct ext_fn_descriptor *ext_fn = new_ext_function (); + struct py_ext_object *ext_object; + + ext_object = (struct py_ext_object *) xzalloc (sizeof (struct py_ext_object)); + ext_object->object = item; + ext_object->match_py_obj_type = py_obj_type; + ext_object->match_method = method; + + ext_fn->lang = EXT_LANG_PYTHON; + ext_fn->is_method = 1; + ext_fn->ext_object = (void *) ext_object; + + return ext_fn; +} + +void +py_free_ext_object (void *ext_object) +{ + struct py_ext_object *o = (struct py_ext_object *) ext_object; + + Py_XDECREF (o->object); + Py_XDECREF (o->match_py_obj_type); + xfree (o); +} + +void * +py_clone_ext_object (void *obj) +{ + struct py_ext_object *new_obj, *old_obj; + + old_obj = (struct py_ext_object *) obj; + + new_obj = (struct py_ext_object *) xzalloc (sizeof (struct py_ext_object)); + new_obj->object = old_obj->object; + new_obj->match_py_obj_type = old_obj->match_py_obj_type; + new_obj->match_method = old_obj->match_method; + + Py_XINCREF (new_obj->object); + Py_XINCREF (new_obj->match_py_obj_type); + + return (void *) new_obj; +} + +struct ext_fn_descriptor * +py_debugmethod_name_match (struct type *obj_type, const char *method_name) +{ + PyObject *py_type, *py_debugmethod_list = NULL, *list_iter, *item; + struct cleanup *cleanups; + struct ext_fn_descriptor *method_list = NULL; + + if (!obj_type || !match_methods_callable) + return NULL; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + py_type = type_to_type_object (obj_type); + py_debugmethod_list = PyObject_CallFunction (match_methods_callable, + "Os", + py_type, method_name); + + list_iter = PyObject_GetIter (py_debugmethod_list); + while ((item = PyIter_Next (list_iter))) + { + struct ext_fn_descriptor *ext_fn; + + ext_fn = new_python_ext_method (item, py_type, method_name); + method_list = ext_fn_list_prepend (method_list, ext_fn); + } + + Py_DECREF (list_iter); + Py_DECREF (py_debugmethod_list); + + do_cleanups (cleanups); + return method_list; +} + +struct type ** +py_ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs) +{ + if (ext_fn->is_method && get_argtypes_callable) + { + struct py_ext_object *ext_object = + (struct py_ext_object *) ext_fn->ext_object; + PyObject *py_argtype_list, *list_iter, *item; + struct cleanup *cleanups; + struct type **type_array, *obj_type; + int i = 1; + + cleanups = ensure_python_env (get_current_arch (), current_language); + py_argtype_list = PyObject_CallFunction (get_argtypes_callable, + "OOs", + ext_object->object, + ext_object->match_py_obj_type, + ext_object->match_method); + + list_iter = PyObject_GetIter (py_argtype_list); + *nargs = PySequence_Size (py_argtype_list) + 1; /* Include 'this' */ + type_array = (struct type **) xmalloc (*nargs * sizeof (struct type *)); + while ((item = PyIter_Next (list_iter))) + { + type_array[i] = type_object_to_type (item); + i++; + Py_XDECREF (item); + } + + /* Add the type of 'this' as the first argument. */ + obj_type = type_object_to_type (ext_object->match_py_obj_type); + type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL); + + Py_XDECREF (list_iter); + Py_XDECREF (py_argtype_list); + do_cleanups (cleanups); + + return type_array; + } + + *nargs = 0; + return NULL; +} + +struct value * +py_ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj, + struct value **args, int nargs) +{ + int i; + struct cleanup *cleanups; + PyObject *py_obj, *py_arg_tuple, *py_result, *debug_method; + struct value *result = NULL; + + if (!ext_fn->is_method || !invoke_method_callable) + return NULL; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + debug_method = ((struct py_ext_object *) ext_fn->ext_object)->object; + py_obj = value_to_value_object (obj); + py_arg_tuple = PyTuple_New (nargs); + for (i = 0; i < nargs; i++) + { + PyObject *py_arg_value = value_to_value_object (args[i]); + + PyTuple_SetItem (py_arg_tuple, i, py_arg_value); + } + + py_result = PyObject_CallFunction (invoke_method_callable, "OOO", + debug_method, py_obj, py_arg_tuple); + + result = convert_value_from_python (py_result); + + Py_XDECREF (py_obj); + Py_XDECREF (py_arg_tuple); + Py_XDECREF (py_result); + do_cleanups (cleanups); + + return result; +} diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index db51f50..c2bef3e 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -35,6 +35,9 @@ typedef struct /* The type-printer list. */ PyObject *type_printers; + + /* The debug method list. */ + PyObject *debug_methods; } objfile_object; static PyTypeObject objfile_object_type; @@ -62,6 +65,7 @@ objfpy_dealloc (PyObject *o) Py_XDECREF (self->printers); Py_XDECREF (self->type_printers); + Py_XDECREF (self->debug_methods); Py_TYPE (self)->tp_free (self); } @@ -87,6 +91,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) Py_DECREF (self); return NULL; } + + self->debug_methods = PyList_New (0); + if (!self->debug_methods) + { + Py_DECREF (self); + return NULL; + } } return (PyObject *) self; } @@ -140,6 +151,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore) return self->type_printers; } +/* Get the 'debug_methods' attribute. */ + +static PyObject * +objfpy_get_debug_methods (PyObject *o, void *ignore) +{ + objfile_object *self = (objfile_object *) o; + + Py_INCREF (self->debug_methods); + return self->debug_methods; +} + /* Set the 'type_printers' attribute. */ static int @@ -232,6 +254,13 @@ objfile_to_objfile_object (struct objfile *objfile) return NULL; } + object->debug_methods = PyList_New (0); + if (!object->debug_methods) + { + Py_DECREF (object); + return NULL; + } + set_objfile_data (objfile, objfpy_objfile_data_key, object); } } @@ -272,6 +301,8 @@ static PyGetSetDef objfile_getset[] = "Pretty printers.", NULL }, { "type_printers", objfpy_get_type_printers, objfpy_set_type_printers, "Type printers.", NULL }, + { "debug_methods", objfpy_get_debug_methods, NULL, + "Debug methods.", NULL }, { NULL } }; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 8dff1d7..0f15085 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -231,6 +231,7 @@ PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args); PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args); PyObject *gdbpy_parameter (PyObject *self, PyObject *args); PyObject *gdbpy_parameter_value (enum var_types type, void *var); +PyObject *gdbpy_enable_debug_methods (PyObject *self, PyObject *args); char *gdbpy_parse_command_name (const char *name, struct cmd_list_element ***base_list, struct cmd_list_element **start_list); diff --git a/gdb/python/python.c b/gdb/python/python.c index 53ddee9..cfc5034 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1826,6 +1826,9 @@ Return the selected inferior object." }, { "inferiors", gdbpy_inferiors, METH_NOARGS, "inferiors () -> (gdb.Inferior, ...).\n\ Return a tuple containing all inferiors." }, + { "enable_debug_methods", gdbpy_enable_debug_methods, METH_VARARGS, + "inferiors (meth_match_function, args_match_function) -> None.\n\ +Enables Python debug methods feature." }, {NULL, NULL, 0, NULL} }; diff --git a/gdb/python/python.h b/gdb/python/python.h index 24e3077..ae25dbc 100644 --- a/gdb/python/python.h +++ b/gdb/python/python.h @@ -49,6 +49,19 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj); int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj); +struct ext_fn_descriptor *py_debugmethod_name_match (struct type *, + const char *); + +void py_free_ext_object (void *ext_object); + +void *py_clone_ext_object (void *ext_object); + +struct type **py_ext_fn_get_argtypes (struct ext_fn_descriptor *, int *); + +struct value *py_ext_fn_invoke_method (struct ext_fn_descriptor *, + struct value *, + struct value **, int nargs); + void *start_type_printers (void); char *apply_type_printers (void *, struct type *type); diff --git a/gdb/valarith.c b/gdb/valarith.c index bdc98ff..7de1ae2 100644 --- a/gdb/valarith.c +++ b/gdb/valarith.c @@ -30,6 +30,7 @@ #include <math.h> #include "infcall.h" #include "exceptions.h" +#include "ext-function.h" /* Define whether or not the C operator '/' truncates towards zero for differently signed operands (truncation direction is undefined in C). */ @@ -285,21 +286,27 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1) explicitly, and perform correct overload resolution in all of the above situations or combinations thereof. */ -static struct value * +static void value_user_defined_cpp_op (struct value **args, int nargs, char *operator, + struct value **src_fn, + struct ext_fn_descriptor **ext_fn, int *static_memfuncp) { struct symbol *symp = NULL; struct value *valp = NULL; + struct ext_fn_descriptor *ext_fnp = NULL; find_overload_match (args, nargs, operator, BOTH /* could be method */, &args[0] /* objp */, NULL /* pass NULL symbol since symbol is unknown */, - &valp, &symp, static_memfuncp, 0); + &valp, &symp, &ext_fnp, static_memfuncp, 0); if (valp) - return valp; + { + *src_fn = valp; + return; + } if (symp) { @@ -307,7 +314,14 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator, expect a reference as its first argument rather the explicit structure. */ args[0] = value_ind (args[0]); - return value_of_variable (symp, 0); + *src_fn = value_of_variable (symp, 0); + return; + } + + if (ext_fnp) + { + *ext_fn = ext_fnp; + return; } error (_("Could not find %s."), operator); @@ -316,19 +330,22 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator, /* Lookup user defined operator NAME. Return a value representing the function, otherwise return NULL. */ -static struct value * +static void value_user_defined_op (struct value **argp, struct value **args, char *name, - int *static_memfuncp, int nargs) + int *static_memfuncp, int nargs, + struct value **src_fn, struct ext_fn_descriptor **ext_fn) { struct value *result = NULL; if (current_language->la_language == language_cplus) - result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp); + value_user_defined_cpp_op (args, nargs, name, src_fn, ext_fn, + static_memfuncp); else - result = value_struct_elt (argp, args, name, static_memfuncp, - "structure"); - - return result; + { + result = value_struct_elt (argp, args, name, static_memfuncp, + "structure"); + *src_fn = result; + } } /* We know either arg1 or arg2 is a structure, so try to find the right @@ -345,6 +362,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, enum exp_opcode otherop, enum noside noside) { struct value **argvec; + struct ext_fn_descriptor *ext_fn = NULL; char *ptr; char tstr[13]; int static_memfuncp; @@ -359,6 +377,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Can't do that binary op on that type")); /* FIXME be explicit */ argvec = (struct value **) alloca (sizeof (struct value *) * 4); + argvec[0] = NULL; argvec[1] = value_addr (arg1); argvec[2] = arg2; argvec[3] = 0; @@ -471,8 +490,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Invalid binary operation specified.")); } - argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, - &static_memfuncp, 2); + value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, 2, + &argvec[0], &ext_fn); if (argvec[0]) { @@ -492,6 +511,11 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1); } + if (ext_fn) + { + if (ext_fn->is_method) + return ext_fn_invoke_method (ext_fn, arg1, &arg2, 1); + } throw_error (NOT_FOUND_ERROR, _("member function %s not found"), tstr); #ifdef lint @@ -510,6 +534,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) { struct gdbarch *gdbarch = get_type_arch (value_type (arg1)); struct value **argvec; + struct ext_fn_descriptor *ext_fn; char *ptr, *mangle_ptr; char tstr[13], mangle_tstr[13]; int static_memfuncp, nargs; @@ -523,6 +548,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Can't do that unary op on that type")); /* FIXME be explicit */ argvec = (struct value **) alloca (sizeof (struct value *) * 4); + argvec[0] = NULL; argvec[1] = value_addr (arg1); argvec[2] = 0; @@ -575,8 +601,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Invalid unary operation specified.")); } - argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, - &static_memfuncp, nargs); + value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, nargs, + &argvec[0], &ext_fn); if (argvec[0]) { @@ -595,6 +621,11 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) return value_zero (return_type, VALUE_LVAL (arg1)); } return call_function_by_hand (argvec[0], nargs, argvec + 1); + } + if (ext_fn) + { + if (ext_fn->is_method) + return ext_fn_invoke_method (ext_fn, arg1, NULL, 0); } throw_error (NOT_FOUND_ERROR, _("member function %s not found"), tstr); diff --git a/gdb/valops.c b/gdb/valops.c index 5b2abf4..0538df4 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -45,6 +45,7 @@ #include "objfiles.h" #include "symtab.h" #include "exceptions.h" +#include "ext-function.h" extern unsigned int overload_debug; /* Local functions. */ @@ -73,8 +74,8 @@ int find_oload_champ_namespace_loop (struct value **, int, const int no_adl); static int find_oload_champ (struct value **, int, int, int, - struct fn_field *, struct symbol **, - struct badness_vector **); + struct fn_field *, struct ext_fn_descriptor *, + struct symbol **, struct badness_vector **); static int oload_method_static (int, struct fn_field *, int); @@ -101,9 +102,10 @@ static CORE_ADDR allocate_space_in_inferior (int); static struct value *cast_into_complex (struct type *, struct value *); -static struct fn_field *find_method_list (struct value **, const char *, - int, struct type *, int *, - struct type **, int *); +static void find_method_list (struct value **, const char *, + int, struct type *, struct fn_field **, int *, + struct ext_fn_descriptor **, + struct type **, int *); void _initialize_valops (void); @@ -2417,40 +2419,53 @@ value_struct_elt (struct value **argp, struct value **args, method is found. BOFFSET is the offset of the base subobject where the method is found. */ -static struct fn_field * +static void find_method_list (struct value **argp, const char *method, - int offset, struct type *type, int *num_fns, + int offset, struct type *type, + struct fn_field **fn_list, int *num_fns, + struct ext_fn_descriptor **ext_fn_list, struct type **basetype, int *boffset) { int i; - struct fn_field *f; - CHECK_TYPEDEF (type); + struct fn_field *f = NULL; + struct ext_fn_descriptor *ef = NULL; - *num_fns = 0; + CHECK_TYPEDEF (type); /* First check in object itself. */ - for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) - { - /* pai: FIXME What about operators and type conversions? */ - const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); - if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) - { - int len = TYPE_FN_FIELDLIST_LENGTH (type, i); - struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); - - *num_fns = len; - *basetype = type; - *boffset = offset; - - /* Resolve any stub methods. */ - check_stub_method_group (type, i); + if (fn_list && !(*fn_list)) + for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) + { + /* pai: FIXME What about operators and type conversions? */ + const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); + + if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) + { + int len = TYPE_FN_FIELDLIST_LENGTH (type, i); + f = TYPE_FN_FIELDLIST1 (type, i); + *fn_list = f; + + *num_fns = len; + *basetype = type; + *boffset = offset; + + /* Resolve any stub methods. */ + check_stub_method_group (type, i); + + break; + } + } - return f; - } + if (ext_fn_list) + { + ef = get_matching_ext_methods (type, method); + *ext_fn_list = ext_fn_list_extend (*ext_fn_list, ef); } - /* Not found in object, check in base subobjects. */ + /* If source methods are not found in current class, look for them in the + base classes. We have to go through the base classes to gather extension + methods anyway. */ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--) { int base_offset; @@ -2467,13 +2482,11 @@ find_method_list (struct value **argp, const char *method, { base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8; } - f = find_method_list (argp, method, base_offset + offset, - TYPE_BASECLASS (type, i), num_fns, - basetype, boffset); - if (f) - return f; + + find_method_list (argp, method, base_offset + offset, + TYPE_BASECLASS (type, i), fn_list, num_fns, + ext_fn_list, basetype, boffset); } - return NULL; } /* Return the list of overloaded methods of a specified name. @@ -2486,9 +2499,11 @@ find_method_list (struct value **argp, const char *method, method. BOFFSET is the offset of the base subobject which defines the method. */ -static struct fn_field * +static void value_find_oload_method_list (struct value **argp, const char *method, - int offset, int *num_fns, + int offset, struct fn_field **fn_list, + int *num_fns, + struct ext_fn_descriptor **ext_fn_list, struct type **basetype, int *boffset) { struct type *t; @@ -2510,8 +2525,32 @@ value_find_oload_method_list (struct value **argp, const char *method, error (_("Attempt to extract a component of a " "value that is not a struct or union")); - return find_method_list (argp, method, 0, t, num_fns, - basetype, boffset); + /* Clear the lists. */ + if (fn_list) + { + *fn_list = NULL; + *num_fns = 0; + } + if (ext_fn_list) + *ext_fn_list = NULL; + + find_method_list (argp, method, 0, t, fn_list, num_fns, ext_fn_list, + basetype, boffset); +} + +static struct type * +value_has_indirect_dynamic_type (struct value *obj) +{ + struct type *stype, *dtype, *dtype_ind; + + stype = check_typedef (TYPE_TARGET_TYPE (value_type (obj))); + dtype_ind = value_rtti_indirect_type (obj, NULL, NULL, NULL); + dtype = dtype_ind ? check_typedef (TYPE_TARGET_TYPE (dtype_ind)) : stype; + + if (class_types_same_p (stype, dtype)) + return NULL; + else + return dtype_ind; } /* Given an array of arguments (ARGS) (which includes an @@ -2559,6 +2598,7 @@ find_overload_match (struct value **args, int nargs, const char *name, enum oload_search_type method, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, + struct ext_fn_descriptor **ext_fn, int *staticp, const int no_adl) { struct value *obj = (objp ? *objp : NULL); @@ -2566,16 +2606,24 @@ find_overload_match (struct value **args, int nargs, /* Index of best overloaded function. */ int func_oload_champ = -1; int method_oload_champ = -1; + int src_method_oload_champ = -1; + int src_method_oload_champ_bkp = -1; + int ext_method_oload_champ = -1; + int src_and_ext_equal = 0; /* The measure for the current best match. */ struct badness_vector *method_badness = NULL; struct badness_vector *func_badness = NULL; + struct badness_vector *ext_method_badness = NULL; + struct badness_vector *src_method_badness = NULL; struct value *temp = obj; /* For methods, the list of overloaded methods. */ struct fn_field *fns_ptr = NULL; /* For non-methods, the list of overloaded function symbols. */ struct symbol **oload_syms = NULL; + /* For extension functions, the list of extension function descriptors. */ + struct ext_fn_descriptor *ext_fn_list = NULL; /* Number of overloaded instances being considered. */ int num_fns = 0; struct type *basetype = NULL; @@ -2587,6 +2635,8 @@ find_overload_match (struct value **args, int nargs, const char *func_name = NULL; enum oload_classification match_quality; enum oload_classification method_match_quality = INCOMPATIBLE; + enum oload_classification src_method_match_quality = INCOMPATIBLE; + enum oload_classification ext_method_match_quality = INCOMPATIBLE; enum oload_classification func_match_quality = INCOMPATIBLE; /* Get the list of overloaded methods or functions. */ @@ -2615,12 +2665,13 @@ find_overload_match (struct value **args, int nargs, } /* Retrieve the list of methods with the name NAME. */ - fns_ptr = value_find_oload_method_list (&temp, name, - 0, &num_fns, - &basetype, &boffset); + value_find_oload_method_list (&temp, name, 0, &fns_ptr, &num_fns, + ext_fn ? &ext_fn_list : NULL, + &basetype, &boffset); /* If this is a method only search, and no methods were found the search has faild. */ - if (method == METHOD && (!fns_ptr || !num_fns)) + if (method == METHOD && (!fns_ptr || !num_fns) + && (ext_fn && !ext_fn_list)) error (_("Couldn't find method %s%s%s"), obj_type_name, (obj_type_name && *obj_type_name) ? "::" : "", @@ -2631,18 +2682,82 @@ find_overload_match (struct value **args, int nargs, if (fns_ptr) { gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL); - method_oload_champ = find_oload_champ (args, nargs, method, - num_fns, fns_ptr, - oload_syms, &method_badness); - - method_match_quality = - classify_oload_match (method_badness, nargs, - oload_method_static (method, fns_ptr, - method_oload_champ)); - - make_cleanup (xfree, method_badness); + src_method_oload_champ = find_oload_champ (args, nargs, method, + num_fns, fns_ptr, NULL, + oload_syms, + &src_method_badness); + + src_method_match_quality = + classify_oload_match ( + src_method_badness, nargs, + oload_method_static (method, fns_ptr, + src_method_oload_champ)); + + make_cleanup (xfree, src_method_badness); } + if (ext_fn_list) + { + ext_method_oload_champ = find_oload_champ (args, nargs, method, + 0, NULL, ext_fn_list, + NULL, &ext_method_badness); + ext_method_match_quality = classify_oload_match (ext_method_badness, + nargs, 0); + make_cleanup (xfree, ext_method_badness); + make_cleanup (ext_fn_list_free, ext_fn_list); + } + + if (src_method_oload_champ >= 0 && ext_method_oload_champ >= 0) + { + switch (compare_badness (ext_method_badness, src_method_badness)) + { + case 0: /* Src method and ext method are equally good. */ + src_and_ext_equal = 1; + case 1: /* Src method and ext method are incompatible */ + /* if ext method match is not standard, then let source method + win. */ + if (ext_method_match_quality != STANDARD) + { + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + ext_method_oload_champ = -1; + method_match_quality = src_method_match_quality; + break; + } + case 2: /* Ext method is champion. */ + method_oload_champ = ext_method_oload_champ; + method_badness = ext_method_badness; + src_method_oload_champ_bkp = src_method_oload_champ; + src_method_oload_champ = -1; + method_match_quality = ext_method_match_quality; + break; + case 3: /* Src method is champion. */ + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + ext_method_oload_champ = -1; + method_match_quality = src_method_match_quality; + break; + default: + error (_("Internal error: unexpected overload comparison " + "result")); + break; + } + } + else + { + if (src_method_oload_champ >= 0) + { + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + method_match_quality = src_method_match_quality; + } + if (ext_method_oload_champ >= 0) + { + method_oload_champ = ext_method_oload_champ; + method_badness = ext_method_badness; + method_match_quality = ext_method_match_quality; + } + } } if (method == NON_METHOD || method == BOTH) @@ -2785,21 +2900,6 @@ find_overload_match (struct value **args, int nargs, func_name); } - if (staticp != NULL) - *staticp = oload_method_static (method, fns_ptr, method_oload_champ); - - if (method_oload_champ >= 0) - { - if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) - *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ, - basetype, boffset); - else - *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, - basetype, boffset); - } - else - *symp = oload_syms[func_oload_champ]; - if (objp) { struct type *temp_type = check_typedef (value_type (temp)); @@ -2809,10 +2909,65 @@ find_overload_match (struct value **args, int nargs, && (TYPE_CODE (objtype) == TYPE_CODE_PTR || TYPE_CODE (objtype) == TYPE_CODE_REF)) { - temp = value_addr (temp); + *objp = value_addr (temp); } - *objp = temp; + else + *objp = temp; + } + + if (staticp != NULL) + *staticp = oload_method_static (method, fns_ptr, method_oload_champ); + + if (method_oload_champ >= 0) + { + if (src_method_oload_champ >= 0) + { + if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) + { + struct type *dtype; + + dtype = value_has_indirect_dynamic_type (args[0]); + if (dtype) + { + args[0] = value_cast (dtype, args[0]); + do_cleanups (all_cleanups); + return find_overload_match (args, nargs, name, method, + objp, fsym, valp, symp, ext_fn, + staticp, no_adl); + } + else + *valp = value_virtual_fn_field (&temp, fns_ptr, + method_oload_champ, + basetype, boffset); + } + else + *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, + basetype, boffset); + } + else + { + if (src_and_ext_equal + && TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, src_method_oload_champ_bkp)) + { + struct type *dtype; + + dtype = value_has_indirect_dynamic_type (args[0]); + if (dtype) + { + args[0] = value_cast (dtype, args[0]); + do_cleanups (all_cleanups); + return find_overload_match (args, nargs, name, method, + objp, fsym, valp, symp, ext_fn, + staticp, no_adl); + } + } + + *ext_fn = ext_fn_clone (ext_fn_list_get (ext_fn_list, + ext_method_oload_champ)); + } } + else + *symp = oload_syms[func_oload_champ]; do_cleanups (all_cleanups); @@ -2947,7 +3102,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs, ++num_fns; new_oload_champ = find_oload_champ (args, nargs, 0, num_fns, - NULL, new_oload_syms, + NULL, NULL, new_oload_syms, &new_oload_champ_bv); /* Case 1: We found a good match. Free earlier matches (if any), @@ -2995,6 +3150,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs, static int find_oload_champ (struct value **args, int nargs, int method, int num_fns, struct fn_field *fns_ptr, + struct ext_fn_descriptor *ext_fn_list, struct symbol **oload_syms, struct badness_vector **oload_champ_bv) { @@ -3010,16 +3166,24 @@ find_oload_champ (struct value **args, int nargs, int method, *oload_champ_bv = NULL; /* Consider each candidate in turn. */ - for (ix = 0; ix < num_fns; ix++) + for (ix = 0; (ix < num_fns) || ext_fn_list; ix++) { int jj; - int static_offset = oload_method_static (method, fns_ptr, ix); + int static_offset = 0; int nparms; struct type **parm_types; + struct ext_fn_descriptor *ext_fn = ext_fn_list; + + if (ext_fn_list) + ext_fn_list = ext_fn_list->next; if (method) { - nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + if (fns_ptr) + { + static_offset = oload_method_static (method, fns_ptr, ix); + nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + } } else { @@ -3028,13 +3192,20 @@ find_oload_champ (struct value **args, int nargs, int method, } /* Prepare array of parameter types. */ - parm_types = (struct type **) - xmalloc (nparms * (sizeof (struct type *))); - for (jj = 0; jj < nparms; jj++) - parm_types[jj] = (method - ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) - : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), - jj)); + if (fns_ptr || oload_syms) + { + parm_types = (struct type **) + xmalloc (nparms * (sizeof (struct type *))); + for (jj = 0; jj < nparms; jj++) + parm_types[jj] = (method + ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) + : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), + jj)); + } + else + { + parm_types = ext_fn_get_argtypes (ext_fn, &nparms); + } /* Compare parameter types to supplied argument types. Skip THIS for static methods. */ diff --git a/gdb/value.h b/gdb/value.h index 97ceeab..85ef377 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -31,6 +31,7 @@ struct type; struct ui_file; struct language_defn; struct value_print_options; +struct ext_fn_descriptor; /* The structure which defines the type of a value. It should never be possible for a program lval value to survive over a call to the @@ -654,6 +655,7 @@ extern int find_overload_match (struct value **args, int nargs, enum oload_search_type method, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, + struct ext_fn_descriptor **ext_fn, int *staticp, const int no_adl); extern struct value *value_field (struct value *arg1, int fieldno); ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-01-29 1:51 ` Siva Chandra @ 2013-02-25 23:02 ` Siva Chandra 2013-05-10 19:33 ` Tom Tromey 1 sibling, 0 replies; 18+ messages in thread From: Siva Chandra @ 2013-02-25 23:02 UTC (permalink / raw) To: gdb-patches Friendly ping: Is there any feedback on the patch? I do not want to get into tests and docs without first clearing the basics of the patch. On Mon, Jan 28, 2013 at 5:49 PM, Siva Chandra <sivachandra@google.com> wrote: > Attached is a new version of the patch which I have now regression > tested. Except for tests and docs, I think it is complete wrt the > feature set I have in mind. Hence, the caveats from the previous mail > still apply. > > 2013-01-01 Siva Chandra Reddy <sivachandra@google.com> > > * Makefile.in: Add entries for new files > * data-directory/Makefile.in: Add entery for new Python file > * eval.c: Use new 'find_overload_match' signature. > * ext-function.c: Support for working with functions/methods > defined in an extension language. > * ext-function.h: Support for working with functions/methods > defined in an extension language. > * python/lib/gdb/debugmethods.py: Python side of the support > for debug methods in Python. > * python/py-debugmethods.c: C side of the support for debug > methods in Python. > * python/py-objfile.c: Add 'debug_methods' attribute to > gdb.Objfile. > * python/python-internal.h: Add new function > gdb.enable_debug_methods to the Python module 'gdb'. > * python/python.c: Add new function gdb.enable_debug_methods to > the Python module 'gdb'. > * python/python.h: Add declarations of new functions. > * valarith.c: Use methods defined in extension languages. > * valops.c: Use methods defined in extension languages. > * value.h: New signature for 'find_overload_match'. > > On Mon, Jan 7, 2013 at 1:21 PM, Siva Chandra <sivachandra@google.com> wrote: >> 1. The patch is incomplete in the sense that tests and documentation >> are missing. Also, ways to enable and disable single or group of debug >> methods is missing. At this point, I am looking for feedback on my >> direction and the way I am intercepting the normal GDB flow to lookup >> methods defined in Python. I will work on completing the patch once >> there is an agreement on these basics. >> >> 2. I did not intend to provide a way for the user to define new or >> override static methods via Python. >> >> 3. I did not intend to provide a way for the user to define new >> virtual methods in Python. The user can however override existing >> virtual and non-virtual methods. The user can also define new >> non-virtual methods in Python. >> >> 4. Doug suggested that I use the C++ infrastructure in GDB as much as >> possible. I feel I have done so. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-01-29 1:51 ` Siva Chandra 2013-02-25 23:02 ` Siva Chandra @ 2013-05-10 19:33 ` Tom Tromey 2013-05-10 19:55 ` Siva Chandra 1 sibling, 1 reply; 18+ messages in thread From: Tom Tromey @ 2013-05-10 19:33 UTC (permalink / raw) To: Siva Chandra; +Cc: gdb-patches >>>>> "Siva" == Siva Chandra <sivachandra@google.com> writes: Siva> Attached is a new version of the patch which I have now regression Siva> tested. Except for tests and docs, I think it is complete wrt the Siva> feature set I have in mind. Hence, the caveats from the previous mail Siva> still apply. I'm sorry about the delay here. I think the approach is generally a good one. I didn't read the overloading bits in detail. I'm not too concerned about that though. Siva> +static ULONGEST enabled_ext_languages = EXT_LANG_PYTHON; I don't think we need this. I'd rather just have this follow the "virtual methods" approach where a function is an object that has whatever methods it needs, plus a destructor. Siva> + struct ext_fn_descriptor *ext_fn = Siva> + (struct ext_fn_descriptor *) xzalloc (sizeof (struct ext_fn_descriptor)); FWIW, XCNEW encapsulates all this. Siva> + if (item->is_method) Siva> + { Siva> + if (item->lang == EXT_LANG_PYTHON) Siva> + py_free_ext_object (item->ext_object); Siva> + } ... then here this could just call the destructor method, and not need to know anything about Python specifically. Siva> +struct ext_fn_descriptor * Siva> +ext_fn_list_extend (struct ext_fn_descriptor *l1, struct ext_fn_descriptor *l2) Siva> +{ Siva> + struct ext_fn_descriptor *item = l1; Siva> + Siva> + if (!l1) Siva> + return l2; Siva> + Siva> + if (!l2) Siva> + return l1; Siva> + Siva> + while (item->next) Siva> + item = item->next; Siva> + Siva> + item->next = l2; Why a linked list instead of a VEC? We already have all this support code written for VECs. Siva> +#ifdef HAVE_PYTHON Siva> + if ((enabled_ext_languages && EXT_LANG_PYTHON) Siva> + && (ext_fn->lang == EXT_LANG_PYTHON)) Siva> + new_ext_fn->ext_object = py_clone_ext_object (ext_fn->ext_object); Siva> +#endif This is the kind of thing that can be avoided with the virtual method approach. Basically any #ifdef HAVE_PYTHON is suspect. Lots of littler nits in the patch -- function comments, more things should be 'static'. Siva> +PyObject * Siva> +gdbpy_enable_debug_methods (PyObject *self, PyObject *args) Why is a special enabling method needed? Siva> + if (PyCallable_Check (match_methods_temp) Siva> + && PyCallable_Check (get_argtypes_temp) Siva> + && PyCallable_Check (invoke_method_temp)) Siva> + { ... Siva> + } Siva> + Siva> + return result; This can return NULL without setting the exception. Also, it does decrefs before increfs, but I think it is better to do this the other way around. Siva> + struct ext_fn_descriptor *ext_fn = new_ext_function (); Siva> + struct py_ext_object *ext_object; Siva> + Siva> + ext_object = (struct py_ext_object *) xzalloc (sizeof (struct py_ext_object)); Siva> + ext_object->object = item; Siva> + ext_object->match_py_obj_type = py_obj_type; Siva> + ext_object->match_method = method; Siva> + Siva> + ext_fn->lang = EXT_LANG_PYTHON; Siva> + ext_fn->is_method = 1; Siva> + ext_fn->ext_object = (void *) ext_object; Siva> + Siva> + return ext_fn; It seems like there should be a way to hang the needed data directly on the ext_fn, say via subclassing. Siva> +struct ext_fn_descriptor * Siva> +py_debugmethod_name_match (struct type *obj_type, const char *method_name) Siva> +{ Siva> + PyObject *py_type, *py_debugmethod_list = NULL, *list_iter, *item; Siva> + struct cleanup *cleanups; Siva> + struct ext_fn_descriptor *method_list = NULL; Siva> + Siva> + if (!obj_type || !match_methods_callable) Siva> + return NULL; Siva> + Siva> + cleanups = ensure_python_env (get_current_arch (), current_language); I don't think this is thread-safe. ensure_python_env acquires the GIL, but this checks match_methods_callable outside the lock. Siva> + list_iter = PyObject_GetIter (py_debugmethod_list); Siva> + while ((item = PyIter_Next (list_iter))) Error check for list_iter. Siva> + { Siva> + struct ext_fn_descriptor *ext_fn; Siva> + Siva> + ext_fn = new_python_ext_method (item, py_type, method_name); Siva> + method_list = ext_fn_list_prepend (method_list, ext_fn); Siva> + } Siva> + Siva> + Py_DECREF (list_iter); Need an error check here too -- the loop may exit with an error. Siva> + py_argtype_list = PyObject_CallFunction (get_argtypes_callable, Siva> + "OOs", Siva> + ext_object->object, Siva> + ext_object->match_py_obj_type, Siva> + ext_object->match_method); Siva> + Siva> + list_iter = PyObject_GetIter (py_argtype_list); Error checking here and elsewhere. Siva> + { "enable_debug_methods", gdbpy_enable_debug_methods, METH_VARARGS, Siva> + "inferiors (meth_match_function, args_match_function) -> None.\n\ Siva> +Enables Python debug methods feature." }, Wrong doc string. Tom ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-05-10 19:33 ` Tom Tromey @ 2013-05-10 19:55 ` Siva Chandra 2013-05-14 19:33 ` Tom Tromey 0 siblings, 1 reply; 18+ messages in thread From: Siva Chandra @ 2013-05-10 19:55 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On Fri, May 10, 2013 at 12:33 PM, Tom Tromey <tromey@redhat.com> wrote: >>>>>> "Siva" == Siva Chandra <sivachandra@google.com> writes: > > Siva> Attached is a new version of the patch which I have now regression > Siva> tested. Except for tests and docs, I think it is complete wrt the > Siva> feature set I have in mind. Hence, the caveats from the previous mail > Siva> still apply. > > I'm sorry about the delay here. Thanks a lot for taking a look. I have had to be away from this work myself and I will continue to be away for at least a week more. Hence, I will respond to your comments in detail late May/early June. For my own reference, I am summarizing your comments as follows: 1. The ext_fn_object should use the GDB style "virtual method" approach. 2. Python related exceptions and error checking are missing. 3. More functions could be static and function comments are missing. 4. Copy-paste errors. 5. One specific comment about thread safety and GIL. It would also be good to get feedback on the Python side API that I put together. Thanks, Siva Chandra ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-05-10 19:55 ` Siva Chandra @ 2013-05-14 19:33 ` Tom Tromey 2013-06-17 19:10 ` Siva Chandra 0 siblings, 1 reply; 18+ messages in thread From: Tom Tromey @ 2013-05-14 19:33 UTC (permalink / raw) To: Siva Chandra; +Cc: gdb-patches Siva> It would also be good to get feedback on the Python side API that I Siva> put together. It seems reasonable to me. That said I am not certain I fully understand it; even just some API docs in the .py would help... I don't see the need for gdb.enable_debug_methods though. gdb could just import the module and look up the functions by name. We do this elsewhere. Tom ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-05-14 19:33 ` Tom Tromey @ 2013-06-17 19:10 ` Siva Chandra 2013-07-22 20:47 ` Tom Tromey 0 siblings, 1 reply; 18+ messages in thread From: Siva Chandra @ 2013-06-17 19:10 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches [-- Attachment #1: Type: text/plain, Size: 1589 bytes --] Hi all, Took me longer than I had expected I but could spend some time last couple of weeks and address all of Tom's comments from last time. Like before, I do not have docs or tests as the Python side API is largely un-reviewed I guess. However, I have put in code comments in the latest version. The patch is attached and the ChangeLog is as below: 2013-06-17 Siva Chandra Reddy <sivachandra@google.com> * Makefile.in: Add entries for new files * data-directory/Makefile.in: Add entery for new Python file * eval.c: Use new 'find_overload_match' signature. * ext-function.c: Support for working with functions/methods defined in an extension language. * ext-function.h: Support for working with functions/methods defined in an extension language. * python/lib/gdb/debugmethods.py: Python side of the support for debug methods in Python. * python/py-debugmethods.c: C side of the support for debug methods in Python. * python/py-objfile.c: Add 'debug_methods' attribute to gdb.Objfile. * python/python-internal.h: Add new function gdb.enable_debug_methods to the Python module 'gdb'. * python/python.c: Add new function gdb.enable_debug_methods to the Python module 'gdb'. * python/python.h: Add declarations of new functions. * valarith.c: Use methods defined in extension languages. * valops.c: Use methods defined in extension languages. * value.h: New signature for 'find_overload_match'. Thanks, Siva Chandra [-- Attachment #2: dm_patch_v4.txt --] [-- Type: text/plain, Size: 62602 bytes --] diff --git a/gdb/Makefile.in b/gdb/Makefile.in index a6336a2..0b643e7 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -282,6 +282,7 @@ SUBDIR_PYTHON_OBS = \ py-breakpoint.o \ py-cmd.o \ py-continueevent.o \ + py-debugmethods.o \ py-event.o \ py-evtregistry.o \ py-evts.o \ @@ -317,6 +318,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-breakpoint.c \ python/py-cmd.c \ python/py-continueevent.c \ + python/py-debugmethods.c \ python/py-event.c \ python/py-evtregistry.c \ python/py-evts.c \ @@ -725,7 +727,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 ext-function.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 \ @@ -825,7 +827,7 @@ rs6000-tdep.h rs6000-aix-tdep.h \ common/gdb_locale.h common/gdb_dirent.h arch-utils.h trad-frame.h gnu-nat.h \ language.h nbsd-tdep.h solib-svr4.h \ macroexp.h ui-file.h regcache.h tracepoint.h i386-tdep.h \ -inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \ +inf-child.h p-lang.h event-top.h ext-function.h gdbtypes.h user-regs.h \ regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \ config/i386/nm-fbsd.h \ config/nm-nto.h config/sparc/nm-sol2.h config/nm-linux.h \ @@ -944,7 +946,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o \ gdb_vecs.o jit.o progspace.o skip.o probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ - format.o registry.o btrace.o record-btrace.o + format.o registry.o btrace.o record-btrace.o ext-function.o TSOBS = inflow.o @@ -2124,6 +2126,10 @@ py-continueevent.o: $(srcdir)/python/py-continueevent.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c $(POSTCOMPILE) +py-debugmethods.o: $(srcdir)/python/py-debugmethods.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-debugmethods.c + $(POSTCOMPILE) + py-event.o: $(srcdir)/python/py-event.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c $(POSTCOMPILE) diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index dec6207..212b957 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -60,6 +60,7 @@ PYTHON_FILES = \ gdb/types.py \ gdb/printing.py \ gdb/prompt.py \ + gdb/debugmethods.py \ gdb/command/__init__.py \ gdb/command/frame_filters.py \ gdb/command/type_printers.py \ diff --git a/gdb/eval.c b/gdb/eval.c index 539489f..0444311 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -22,6 +22,7 @@ #include "symtab.h" #include "gdbtypes.h" #include "value.h" +#include "ext-function.h" #include "expression.h" #include "target.h" #include "frame.h" @@ -1592,7 +1593,7 @@ evaluate_subexp_standard (struct type *expect_type, NON_METHOD, /* not method */ NULL, NULL, /* pass NULL symbol since symbol is unknown */ - NULL, &symp, NULL, 0); + NULL, &symp, NULL, NULL, 0); /* Now fix the expression being evaluated. */ exp->elts[save_pos1 + 2].symbol = symp; @@ -1622,11 +1623,12 @@ evaluate_subexp_standard (struct type *expect_type, /* Language is C++, do some overload resolution before evaluation. */ struct value *valp = NULL; + struct ext_fn_descriptor *ext_fnp = NULL; (void) find_overload_match (&argvec[1], nargs, tstr, METHOD, /* method */ &arg2, /* the object */ - NULL, &valp, NULL, + NULL, &valp, NULL, &ext_fnp, &static_memfuncp, 0); if (op == OP_SCOPE && !static_memfuncp) @@ -1636,9 +1638,26 @@ evaluate_subexp_standard (struct type *expect_type, "`this' pointer"), function_name); } - argvec[1] = arg2; /* the ``this'' pointer */ - argvec[0] = valp; /* Use the method found after overload - resolution. */ + + if (ext_fnp) + { + if (ext_fn_is_method (ext_fnp)) + { + struct value *ret_val; + + ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2, + nargs - 1); + if (ret_val == NULL) + error (_("Error invoking debug method for method %s."), + tstr); + + return ret_val; + } + } + + argvec[1] = arg2; /* the ``this'' pointer */ + argvec[0] = valp; /* Use the method found after overload + resolution. */ } else /* Non-C++ case -- or no overload resolution. */ @@ -1697,7 +1716,7 @@ evaluate_subexp_standard (struct type *expect_type, NULL, /* no need for name */ NON_METHOD, /* not method */ NULL, function, /* the function */ - NULL, &symp, NULL, no_adl); + NULL, &symp, NULL, NULL, no_adl); if (op == OP_VAR_VALUE) { diff --git a/gdb/ext-function.c b/gdb/ext-function.c new file mode 100644 index 0000000..bf30798 --- /dev/null +++ b/gdb/ext-function.c @@ -0,0 +1,172 @@ +/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "cleanups.h" +#include "ext-function.h" + +#include "gdb_assert.h" +#include "vec.h" + +struct ext_fn_descriptor + { + struct ext_lang *lang; + + int is_method; + + void *ext_object; + }; + +typedef struct ext_lang *ext_lang_p; +DEF_VEC_P (ext_lang_p); +static VEC (ext_lang_p) *ext_lang_vec = NULL; + +/* Registers an extension language with GDB. */ + +void +register_ext_lang (struct ext_lang *lang) +{ + if (ext_lang_vec == NULL) + ext_lang_vec = VEC_alloc (ext_lang_p, 1); + + VEC_safe_push (ext_lang_p, ext_lang_vec, lang); +} + +/* Returns a new ext_fn_descriptor object. LANG is the extention language the + new extension function is implemented in. IS_METHOD indicates whether the + new extension function is a method. EXT_OBJ is the extension language + specific data to be encapsulated in the ext_fn_descriptor. */ + +struct ext_fn_descriptor * +new_ext_function (struct ext_lang *lang, int is_method, void *ext_obj) +{ + struct ext_fn_descriptor *ext_fn = XCNEW (struct ext_fn_descriptor); + + ext_fn->is_method = is_method; + ext_fn->lang = lang; + ext_fn->ext_object = ext_obj; + + return ext_fn; +} + +/* Clones EXT_FN and returns a new but identical ext_fn_descriptor. */ + +struct ext_fn_descriptor * +ext_fn_clone (struct ext_fn_descriptor *ext_fn) +{ + struct ext_fn_descriptor *new_ext_fn; + struct ext_lang *lang = ext_fn->lang; + + new_ext_fn = new_ext_function (lang, ext_fn->is_method, + lang->clone_ext_object (ext_fn->ext_object)); + + return new_ext_fn; +} + +/* If a method of name METHOD is to be invoked on an object of type TYPE, then + all entension languages are searched for implementations of methods with + name METHOD in the extension languages. All matches found are returned as + a vector 'struct ent_fn_descriptor' objects. If no matching methods are + found, NULL is returned. */ + +VEC (ext_fn_descriptor_p) * +get_matching_ext_methods (struct type *type, const char *method) +{ + VEC (ext_fn_descriptor_p) *ext_methods = NULL; + ext_lang_p lang; + int i; + + for (i = 0; VEC_iterate (ext_lang_p, ext_lang_vec, i, lang); i++) + { + VEC (ext_fn_descriptor_p) *lang_methods, *new_vec; + + lang_methods = lang->get_matching_ext_methods (type, method); + new_vec = VEC_merge (ext_fn_descriptor_p, ext_methods, lang_methods); + + VEC_free (ext_fn_descriptor_p, ext_methods); + VEC_free (ext_fn_descriptor_p, lang_methods); + ext_methods = new_vec; + } + + return ext_methods; +} + +/* Given an function EXT_FN implemented in an extension language, returns an + array of types of the arguments the function accepts. The length of the + array is returned in NARGS. The type of the 'this' object is returned as + the first argument if EXT_FN is a method. If EXT_FN does not take any + arguments, then NULL is returned with 0 in NARGS. */ + +struct type ** +ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs) +{ + gdb_assert (ext_fn && ext_fn->lang && ext_fn->lang->get_ext_fn_argtypes); + + return ext_fn->lang->get_ext_fn_argtypes (ext_fn->ext_object, nargs); +} + +/* If EXT_FN is a method implemented in an extension language, invokes it and + returns the resulting value. The method is invoked on OBJ with arguments + ARGS. NARGS is the length of the ARGS array. */ + +struct value * +ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj, + struct value **args, int nargs) +{ + gdb_assert (ext_fn && ext_fn->is_method && ext_fn->lang + && ext_fn->lang->invoke_method); + + return ext_fn->lang->invoke_method (ext_fn->ext_object, obj, args, nargs); +} + +/* Returns true if EXT_FN is a method, 0 otherwise. */ + +int +ext_fn_is_method (struct ext_fn_descriptor *ext_fn) +{ + if (ext_fn != NULL) + return ext_fn->is_method; + + return 0; +} + +/* Frees a vector of ext_fn_descriptors VEC. */ + +static void +ext_fn_vec_free (void *vec) +{ + int i; + struct ext_fn_descriptor *ext_fn; + VEC (ext_fn_descriptor_p) *v = (VEC (ext_fn_descriptor_p) *) vec; + + for (i = 0; VEC_iterate (ext_fn_descriptor_p, v, i, ext_fn); i++) + { + ext_fn->lang->free_ext_obj (ext_fn->ext_object); + xfree (ext_fn); + } +} + +/* Return a cleanup object to free a vector VEC of extension function + descriptors. */ + +struct cleanup * +make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec) +{ + return make_cleanup (ext_fn_vec_free, (void *) vec); +} diff --git a/gdb/ext-function.h b/gdb/ext-function.h new file mode 100644 index 0000000..42cda14 --- /dev/null +++ b/gdb/ext-function.h @@ -0,0 +1,76 @@ +/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */ + +#if !defined (EXT_FUNCTION_H) +#define EXT_FUNCTION_H + +#include "vec.h" + +struct cleanup; +struct value; +struct type; +struct ext_fn_descriptor; + +typedef struct ext_fn_descriptor *ext_fn_descriptor_p; +DEF_VEC_P (ext_fn_descriptor_p); +typedef VEC (ext_fn_descriptor_p) ext_fn_vec; + +typedef struct value* (invoke_method_ftype) (void *ext_obj, + struct value *, + struct value **, int nargs); + +typedef void * (clone_ext_obj_ftype) (void *ext_obj); + +typedef void (free_ext_obj_ftype) (void *ext_obj); + +typedef ext_fn_vec *(get_matching_ext_methods_ftype) (struct type *type, + const char *method); + +typedef struct type** (get_ext_fn_argtypes_ftype) (void *ext_obj, int *nargs); + +struct ext_lang + { + clone_ext_obj_ftype *clone_ext_object; + free_ext_obj_ftype *free_ext_obj; + get_matching_ext_methods_ftype *get_matching_ext_methods; + get_ext_fn_argtypes_ftype *get_ext_fn_argtypes; + invoke_method_ftype *invoke_method; + }; + +extern void register_ext_lang (struct ext_lang *lang); + +extern struct value *ext_fn_invoke_method (struct ext_fn_descriptor *, + struct value *, + struct value **, int nargs); + +extern struct ext_fn_descriptor *ext_fn_clone (struct ext_fn_descriptor *); + +extern struct ext_fn_descriptor *new_ext_function (struct ext_lang *lang, + int is_method, + void *ext_obj); + +extern ext_fn_vec *get_matching_ext_methods (struct type *, const char *); + +extern struct type **ext_fn_get_argtypes (struct ext_fn_descriptor *, int *); + +extern int ext_fn_is_method (struct ext_fn_descriptor *); + +extern struct cleanup* make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec); + +#endif /* EXT_FUNCTION_H */ diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py new file mode 100644 index 0000000..7d0a891 --- /dev/null +++ b/gdb/python/lib/gdb/debugmethods.py @@ -0,0 +1,118 @@ +# Python side of the support for debug methods. +# Copyright (C) 2013 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/>. + +"""Utilities for defining debug methods""" + +import gdb +import re + +DEFAULT_DEBUG_METHOD_GROUP = 'DEFAULT_DEBUG_METHOD_GROUP' + + +class DebugMethod(object): + """Base class for all debug methods defined in Python. + + A debug method defined in Python should be derived from this class. The + derived classes should override the methods 'match', 'get_argtypes' and + 'invoke'. + + Internally, GDB first invokes the 'match' method to match the class type + and the method name. It next get the argument types of these methods via + the 'get_argtypes' method to perform overload resolution. + """ + + def __init__(self, name, group=DEFAULT_DEBUG_METHOD_GROUP): + """ + Args: + name: An identifying name for the debug method. + group: The group name of the group to which the debug method + belongs to. + """ + self.name = name + self.group = group + self.enabled = True + + def match(self, class_type, method_name): + """Match class type and method name. + + Args: + class_type: The class type to match + method_name: The name of the method to match. + """ + gdb.GdbError('ERROR: Invoking abstract method \'match\'.') + + def get_argtypes(self, class_type, method_name): + """Return a list of types, as gdb.Type objects, of the arguments of + the debug method. + + Args: + class_type: The gdb.Type value of the class on which the method + is defined. + method_name: The name of the method whose argument types are to + be returned. + """ + gdb.GdbError('ERROR: Invoking abstract method \'get_argtypes\'.') + + def invoke(self, obj, args): + """Invoke the debug method. + + Args: + obj: The gdb.Value of the object on which the method is to be + invoked. + args: The tuple of arguments to the method. + """ + gdb.GdbError('ERROR: Invoking abstract method \'invoke\'.') + + +class SimpleDebugMethod(DebugMethod): + """This is a utility class which does name match by class name of the + objects on which the debug methods are defined. + """ + def __init__(self, name, group, method_function, class_matcher, + method_matcher, *argtypes): + DebugMethod.__init__(self, name, group) + self._method_function = method_function + self._class_matcher = class_matcher + self._method_matcher = method_matcher + self._argtypes = argtypes + + def match(self, class_type, method_name): + cm = re.match(self._class_matcher, str(class_type.unqualified().tag)) + mm = re.match(self._method_matcher, method_name) + if cm and mm: + return True + else: + return False + + def get_argtypes(self, class_type, method_name): + return self._argtypes + + def invoke(self, obj, args): + return self._method_function(obj, *args) + + +def register_debug_methods(objfile, debug_methods): + existing_method_list = [] + for new_method in debug_methods: + for old_method in objfile.debug_methods: + if (new_method.group == old_method.group and + new_method.name == old_method.name): + print ('WARNING: Replacing debug method with name "%s" in ' + 'group "%s".' % (old_method.name, old_method.group)) + existing_method_list.append(old_method) + for old_method in existing_method_list: + objfile.debug_methods.remove(old_method) + objfile.debug_methods.extend(debug_methods) diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c new file mode 100644 index 0000000..38c1b69 --- /dev/null +++ b/gdb/python/py-debugmethods.c @@ -0,0 +1,508 @@ +/* Support for debug methods in Python. + + 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "arch-utils.h" +#include "ext-function.h" +#include "objfiles.h" +#include "value.h" +#include "language.h" +#include "python-internal.h" +#include "python.h" + +struct py_ext_object +{ + /* Holds an instance of the DebugMethod class. */ + PyObject *object; + + /* Holds the type of the 'this' object. */ + PyObject *match_py_obj_type; + + /* Holds the matching method name. */ + const char *match_method; +}; + +static const char *enabled_field_name = "enabled"; +static const char *match_method_name = "match"; +static const char *get_argtypes_method_name = "get_argtypes"; +static const char *invoke_method_name = "invoke"; + +static PyObject *match_method_pystring = NULL; +static PyObject *get_argtypes_method_pystring = NULL; +static PyObject *invoke_method_pystring = NULL; + +static struct ext_fn_descriptor *new_python_ext_method (PyObject *item, + PyObject *py_obj_type, + const char *method); + +/* Implementation of free_ext_obj_ftype. */ + +static void +py_free_ext_object (void *ext_object) +{ + struct py_ext_object *o = (struct py_ext_object *) ext_object; + + Py_XDECREF (o->object); + Py_XDECREF (o->match_py_obj_type); + xfree (o); +} + +/* Implementation of clone_ext_obj_ftype. */ + +static void * +py_clone_ext_object (void *obj) +{ + struct py_ext_object *new_obj, *old_obj; + + old_obj = (struct py_ext_object *) obj; + + new_obj = XCNEW (struct py_ext_object); + new_obj->object = old_obj->object; + new_obj->match_py_obj_type = old_obj->match_py_obj_type; + new_obj->match_method = old_obj->match_method; + + Py_XINCREF (new_obj->object); + Py_XINCREF (new_obj->match_py_obj_type); + + return (void *) new_obj; +} + +/* Returns true if gdb.Type object PY_OBJ_TYPE has a method defined in Python + with name METHOD_NAME. */ + +static int +is_matching_debug_method (PyObject *dm_obj, PyObject *py_obj_type, + const char *method_name) +{ + PyObject *method_name_pystring; + PyObject *match_method, *enabled_field, *match_result; + struct cleanup *cleanups; + int enabled, match; + + if (method_name == NULL) + return 0; + + cleanups = make_cleanup (null_cleanup, NULL); + + enabled_field = PyObject_GetAttrString (dm_obj, enabled_field_name); + if (enabled_field == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (enabled_field); + + enabled = PyObject_IsTrue (enabled_field); + if (enabled == -1) + { + PyErr_Clear (); + do_cleanups (cleanups); + + return 0; + } + if (enabled == 0) + { + do_cleanups (cleanups); + return 0; + } + + match_method = PyObject_GetAttrString (dm_obj, match_method_name); + if (match_method == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (match_method); + if (!PyCallable_Check (match_method)) + { + warning (_("Attribute '%s' of a registered Python debug method is not " + "callable. Ignored!"), match_method_name); + do_cleanups (cleanups); + + return 0; + } + + method_name_pystring = PyString_FromString (method_name); + if (method_name_pystring == NULL) + { + PyErr_Clear (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (method_name_pystring); + + match_result = PyObject_CallMethodObjArgs (dm_obj, + match_method_pystring, + py_obj_type, + method_name_pystring, + NULL); + if (match_result == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (match_result); + + match = PyObject_IsTrue (match_result); + if (match == -1) + { + PyErr_Clear (); + do_cleanups (cleanups); + + return 0; + } + + do_cleanups (cleanups); + + return match; +} + +/* Implementation of get_matching_ext_methods_ftype. + Return a of vector methods with name METHOD_NAME defined in Python for + objects of type OBJ_TYPE. Returns NULL if no matches are found. */ + +static VEC (ext_fn_descriptor_p) * +py_debugmethod_name_match (struct type *obj_type, const char *method_name) +{ + struct cleanup *cleanups; + struct objfile *objfile; + VEC (ext_fn_descriptor_p) *method_vec = NULL; + PyObject *py_type; + + if (obj_type == NULL || method_name == NULL) + return method_vec; + + py_type = type_to_type_object (obj_type); + if (py_type == NULL) + return method_vec; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + ALL_OBJFILES (objfile) + { + PyObject *py_debugmethod_list = NULL, *list_iter, *item; + PyObject *py_objfile = objfile_to_objfile_object (objfile); + + if (py_objfile == NULL) + { + PyErr_Clear (); + continue; + } + + py_debugmethod_list = objfpy_get_debug_methods (py_objfile, NULL); + list_iter = PyObject_GetIter (py_debugmethod_list); + if (list_iter == NULL) + { + PyErr_Clear(); + Py_DECREF (py_debugmethod_list); + continue; + } + + while ((item = PyIter_Next (list_iter))) + { + if (is_matching_debug_method (item, py_type, method_name)) + { + struct ext_fn_descriptor *ext_fn; + + ext_fn = new_python_ext_method (item, py_type, method_name); + if (method_vec == NULL) + method_vec = VEC_alloc (ext_fn_descriptor_p, 1); + VEC_safe_push (ext_fn_descriptor_p, method_vec, ext_fn); + } + + Py_DECREF (item); + } + Py_DECREF (list_iter); + if (PyErr_Occurred ()) + PyErr_Clear (); /* Ignore iteration errors silently. */ + + Py_DECREF (py_debugmethod_list); + } + + Py_DECREF (py_type); + do_cleanups (cleanups); + + return method_vec; +} + +/* Implementation of get_ext_fn_argtypes_ftype. + Return an arry of argument types for extension encapsulated in EXT_OBJ. + NARGS contains the length of the array. */ + +static struct type ** +py_ext_fn_get_argtypes (void *ext_obj, int *nargs) +{ + struct py_ext_object *ext_object = (struct py_ext_object *) ext_obj; + PyObject *debug_method_obj = ext_object->object; + PyObject *get_argtypes_method; + PyObject *py_argtype_list, *list_iter, *item; + struct cleanup *cleanups; + struct type **type_array, *obj_type; + int i = 1; + + /* Set nargs to 0 so that any premature return from this function returns + 0 arg types. */ + *nargs = 0; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + get_argtypes_method = PyObject_GetAttrString (debug_method_obj, + get_argtypes_method_name); + if (get_argtypes_method == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (get_argtypes_method); + + if (!PyCallable_Check (get_argtypes_method)) + { + warning (_("Attribute '%s' of a registered Python debug method is not " + "callable. Ignored!"), get_argtypes_method_name); + do_cleanups (cleanups); + + return NULL; + } + + py_argtype_list = PyObject_CallMethodObjArgs (debug_method_obj, + get_argtypes_method_pystring, + ext_object->match_py_obj_type, + ext_object->match_method, + NULL); + if (py_argtype_list == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (py_argtype_list); + + list_iter = PyObject_GetIter (py_argtype_list); + if (list_iter == NULL) + { + PyErr_Clear (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (list_iter); + + /* Include the 'this' argument in the size. */ + type_array = XCNEWVEC (struct type *, PyList_GET_SIZE (py_argtype_list) + 1); + while ((item = PyIter_Next (list_iter))) + { + struct type *arg_type = type_object_to_type (item); + + Py_DECREF (item); + + if (arg_type == NULL) + { + i = -1; + break; + } + + type_array[i] = arg_type; + i++; + } + if (PyErr_Occurred () || i == -1) + { + PyErr_Clear (); + do_cleanups (cleanups); + xfree (type_array); + + return NULL; + } + + /* Add the type of 'this' as the first argument. */ + obj_type = type_object_to_type (ext_object->match_py_obj_type); + type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL); + *nargs = i; + + do_cleanups (cleanups); + + return type_array; +} + +/* Implementation of invoke_method_ftype. + Invokes a method defined in Python. The value returned by the method is + returned. NULL is returned in case of errors. */ + +static struct value * +py_ext_fn_invoke_method (void *ext_obj, struct value *obj, struct value **args, + int nargs) +{ + int i; + struct cleanup *cleanups; + PyObject *py_value_obj, *py_arg_tuple, *py_result, *debug_method_obj; + PyObject *invoke_method; + struct value *result = NULL; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + debug_method_obj = ((struct py_ext_object *) ext_obj)->object; + + invoke_method = PyObject_GetAttrString (debug_method_obj, + invoke_method_name); + if (invoke_method == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (invoke_method); + + if (!PyCallable_Check (invoke_method)) + { + warning (_("Attribute '%s' of a registered Python debug method is not " + "callable. Ignored!"), invoke_method_name); + do_cleanups (cleanups); + + return NULL; + } + + py_value_obj = value_to_value_object (obj); + if (py_value_obj == NULL) + { + do_cleanups (cleanups); + return NULL; + } + make_cleanup_py_decref (py_value_obj); + + py_arg_tuple = PyTuple_New (nargs); + if (py_arg_tuple == NULL) + { + PyErr_Clear (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (py_arg_tuple); + + for (i = 0; i < nargs; i++) + { + PyObject *py_value_arg = value_to_value_object (args[i]); + + if (py_value_arg == NULL) + { + PyErr_Clear (); + do_cleanups (cleanups); + return NULL; + } + + if (PyTuple_SetItem (py_arg_tuple, i, py_value_arg)) + { + PyErr_Clear (); + do_cleanups (cleanups); + + return NULL; + } + } + + py_result = PyObject_CallMethodObjArgs (debug_method_obj, + invoke_method_pystring, + py_value_obj, py_arg_tuple, NULL); + if (py_result == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (py_result); + + /* Check that a debug method did not return None. */ + if (py_result != Py_None) + { + result = convert_value_from_python (py_result); + if (result == NULL) + { + gdbpy_print_stack (); + } + } + else + result = allocate_value (lookup_typename (python_language, python_gdbarch, + "void", NULL, 0)); + + do_cleanups (cleanups); + + return result; +} + +static struct ext_lang python_ext_lang = { + py_clone_ext_object, + py_free_ext_object, + py_debugmethod_name_match, + py_ext_fn_get_argtypes, + py_ext_fn_invoke_method +}; + +/* Creates a new python ext_function_descriptor. DEBUG_METHOD is the + debug method object. PY_OBJ_TYPE is a gdb.Type value corresponding to the + type of the object on which the debug method with name METHOD will be + invoked. */ + +static struct ext_fn_descriptor * +new_python_ext_method (PyObject *debug_method, PyObject *py_obj_type, + const char *method) +{ + struct py_ext_object *ext_object; + + ext_object = XCNEW (struct py_ext_object); + ext_object->object = debug_method; + ext_object->match_py_obj_type = py_obj_type; + ext_object->match_method = method; + + Py_XINCREF (ext_object->object); + Py_XINCREF (ext_object->match_py_obj_type); + + return new_ext_function (&python_ext_lang, 1, ext_object); +} + +/* Initializes the Python debug method support. */ + +int +gdbpy_initialize_debugmethods (void) +{ + register_ext_lang (&python_ext_lang); + + match_method_pystring = PyString_FromString (match_method_name); + if (match_method_pystring == NULL) + return -1; + + invoke_method_pystring = PyString_FromString (invoke_method_name); + if (invoke_method_pystring == NULL) + return -1; + + get_argtypes_method_pystring = PyString_FromString (get_argtypes_method_name); + if (get_argtypes_method_pystring == NULL) + return -1; + + return 1; +} diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index a954c9d..16d3f98 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -37,6 +37,9 @@ typedef struct PyObject *frame_filters; /* The type-printer list. */ PyObject *type_printers; + + /* The debug method list. */ + PyObject *debug_methods; } objfile_object; static PyTypeObject objfile_object_type @@ -66,6 +69,7 @@ objfpy_dealloc (PyObject *o) Py_XDECREF (self->printers); Py_XDECREF (self->frame_filters); Py_XDECREF (self->type_printers); + Py_XDECREF (self->debug_methods); Py_TYPE (self)->tp_free (self); } @@ -98,6 +102,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) Py_DECREF (self); return NULL; } + + self->debug_methods = PyList_New (0); + if (!self->debug_methods) + { + Py_DECREF (self); + return NULL; + } } return (PyObject *) self; } @@ -192,6 +203,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore) return self->type_printers; } +/* Get the 'debug_methods' attribute. */ + +PyObject * +objfpy_get_debug_methods (PyObject *o, void *ignore) +{ + objfile_object *self = (objfile_object *) o; + + Py_INCREF (self->debug_methods); + return self->debug_methods; +} + /* Set the 'type_printers' attribute. */ static int @@ -291,6 +313,13 @@ objfile_to_objfile_object (struct objfile *objfile) return NULL; } + object->debug_methods = PyList_New (0); + if (!object->debug_methods) + { + Py_DECREF (object); + return NULL; + } + set_objfile_data (objfile, objfpy_objfile_data_key, object); } } @@ -332,6 +361,8 @@ static PyGetSetDef objfile_getset[] = objfpy_set_frame_filters, "Frame Filters.", NULL }, { "type_printers", objfpy_get_type_printers, objfpy_set_type_printers, "Type printers.", NULL }, + { "debug_methods", objfpy_get_debug_methods, NULL, + "Debug methods.", NULL }, { NULL } }; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index d947be6..2627069 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -321,6 +321,7 @@ PyObject *objfile_to_objfile_object (struct objfile *) CPYCHECKER_RETURNS_BORROWED_REF; PyObject *objfpy_get_printers (PyObject *, void *); PyObject *objfpy_get_frame_filters (PyObject *, void *); +PyObject *objfpy_get_debug_methods (PyObject *, void *); PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch); @@ -399,6 +400,8 @@ int gdbpy_initialize_new_objfile_event (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; int gdbpy_initialize_arch (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; +int gdbpy_initialize_debugmethods (void) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; struct cleanup *make_cleanup_py_decref (PyObject *py); struct cleanup *make_cleanup_py_xdecref (PyObject *py); diff --git a/gdb/python/python.c b/gdb/python/python.c index c94198e..4f4b46b 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1686,7 +1686,8 @@ message == an error message without a stack will be printed."), || gdbpy_initialize_exited_event () < 0 || gdbpy_initialize_thread_event () < 0 || gdbpy_initialize_new_objfile_event () < 0 - || gdbpy_initialize_arch () < 0) + || gdbpy_initialize_arch () < 0 + || gdbpy_initialize_debugmethods () < 0) goto fail; observer_attach_before_prompt (before_prompt_hook); diff --git a/gdb/valarith.c b/gdb/valarith.c index 18c14fc..f105200 100644 --- a/gdb/valarith.c +++ b/gdb/valarith.c @@ -30,6 +30,7 @@ #include <math.h> #include "infcall.h" #include "exceptions.h" +#include "ext-function.h" /* Define whether or not the C operator '/' truncates towards zero for differently signed operands (truncation direction is undefined in C). */ @@ -285,21 +286,27 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1) explicitly, and perform correct overload resolution in all of the above situations or combinations thereof. */ -static struct value * +static void value_user_defined_cpp_op (struct value **args, int nargs, char *operator, + struct value **src_fn, + struct ext_fn_descriptor **ext_fn, int *static_memfuncp) { struct symbol *symp = NULL; struct value *valp = NULL; + struct ext_fn_descriptor *ext_fnp = NULL; find_overload_match (args, nargs, operator, BOTH /* could be method */, &args[0] /* objp */, NULL /* pass NULL symbol since symbol is unknown */, - &valp, &symp, static_memfuncp, 0); + &valp, &symp, &ext_fnp, static_memfuncp, 0); if (valp) - return valp; + { + *src_fn = valp; + return; + } if (symp) { @@ -307,7 +314,14 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator, expect a reference as its first argument rather the explicit structure. */ args[0] = value_ind (args[0]); - return value_of_variable (symp, 0); + *src_fn = value_of_variable (symp, 0); + return; + } + + if (ext_fnp) + { + *ext_fn = ext_fnp; + return; } error (_("Could not find %s."), operator); @@ -316,19 +330,22 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator, /* Lookup user defined operator NAME. Return a value representing the function, otherwise return NULL. */ -static struct value * +static void value_user_defined_op (struct value **argp, struct value **args, char *name, - int *static_memfuncp, int nargs) + int *static_memfuncp, int nargs, + struct value **src_fn, struct ext_fn_descriptor **ext_fn) { struct value *result = NULL; if (current_language->la_language == language_cplus) - result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp); + value_user_defined_cpp_op (args, nargs, name, src_fn, ext_fn, + static_memfuncp); else - result = value_struct_elt (argp, args, name, static_memfuncp, - "structure"); - - return result; + { + result = value_struct_elt (argp, args, name, static_memfuncp, + "structure"); + *src_fn = result; + } } /* We know either arg1 or arg2 is a structure, so try to find the right @@ -345,6 +362,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, enum exp_opcode otherop, enum noside noside) { struct value **argvec; + struct ext_fn_descriptor *ext_fn = NULL; char *ptr; char tstr[13]; int static_memfuncp; @@ -359,6 +377,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Can't do that binary op on that type")); /* FIXME be explicit */ argvec = (struct value **) alloca (sizeof (struct value *) * 4); + argvec[0] = NULL; argvec[1] = value_addr (arg1); argvec[2] = arg2; argvec[3] = 0; @@ -471,8 +490,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Invalid binary operation specified.")); } - argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, - &static_memfuncp, 2); + value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, 2, + &argvec[0], &ext_fn); if (argvec[0]) { @@ -492,6 +511,19 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1); } + if (ext_fn) + { + if (ext_fn_is_method (ext_fn)) + { + struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, &arg2, 1); + + if (ret_val == NULL) + error (_("Error invoking debug method implementation for " + "method %s"), tstr); + + return ret_val; + } + } throw_error (NOT_FOUND_ERROR, _("member function %s not found"), tstr); #ifdef lint @@ -510,6 +542,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) { struct gdbarch *gdbarch = get_type_arch (value_type (arg1)); struct value **argvec; + struct ext_fn_descriptor *ext_fn; char *ptr; char tstr[13], mangle_tstr[13]; int static_memfuncp, nargs; @@ -523,6 +556,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Can't do that unary op on that type")); /* FIXME be explicit */ argvec = (struct value **) alloca (sizeof (struct value *) * 4); + argvec[0] = NULL; argvec[1] = value_addr (arg1); argvec[2] = 0; @@ -574,8 +608,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Invalid unary operation specified.")); } - argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, - &static_memfuncp, nargs); + value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, nargs, + &argvec[0], &ext_fn); if (argvec[0]) { @@ -594,6 +628,19 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) return value_zero (return_type, VALUE_LVAL (arg1)); } return call_function_by_hand (argvec[0], nargs, argvec + 1); + } + if (ext_fn) + { + if (ext_fn_is_method (ext_fn)) + { + struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, NULL, 0); + + if (ret_val == NULL) + error (_("Error invoking debug method implementation for " + "method %s"), tstr); + + return ret_val; + } } throw_error (NOT_FOUND_ERROR, _("member function %s not found"), tstr); diff --git a/gdb/valops.c b/gdb/valops.c index 93c09d8..1d0cb80 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -45,6 +45,7 @@ #include "objfiles.h" #include "symtab.h" #include "exceptions.h" +#include "ext-function.h" extern unsigned int overload_debug; /* Local functions. */ @@ -73,8 +74,8 @@ int find_oload_champ_namespace_loop (struct value **, int, const int no_adl); static int find_oload_champ (struct value **, int, int, int, - struct fn_field *, struct symbol **, - struct badness_vector **); + struct fn_field *, VEC (ext_fn_descriptor_p) *, + struct symbol **, struct badness_vector **); static int oload_method_static (int, struct fn_field *, int); @@ -101,9 +102,10 @@ static CORE_ADDR allocate_space_in_inferior (int); static struct value *cast_into_complex (struct type *, struct value *); -static struct fn_field *find_method_list (struct value **, const char *, - int, struct type *, int *, - struct type **, int *); +static void find_method_list (struct value **, const char *, + int, struct type *, struct fn_field **, int *, + VEC (ext_fn_descriptor_p) **, + struct type **, int *); void _initialize_valops (void); @@ -2432,40 +2434,58 @@ value_struct_elt (struct value **argp, struct value **args, method is found. BOFFSET is the offset of the base subobject where the method is found. */ -static struct fn_field * +static void find_method_list (struct value **argp, const char *method, - int offset, struct type *type, int *num_fns, + int offset, struct type *type, + struct fn_field **fn_list, int *num_fns, + VEC (ext_fn_descriptor_p) **ext_fn_vec, struct type **basetype, int *boffset) { int i; - struct fn_field *f; - CHECK_TYPEDEF (type); + struct fn_field *f = NULL; - *num_fns = 0; + CHECK_TYPEDEF (type); /* First check in object itself. */ - for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) - { - /* pai: FIXME What about operators and type conversions? */ - const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); - if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) - { - int len = TYPE_FN_FIELDLIST_LENGTH (type, i); - struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); + if (fn_list && !(*fn_list)) + for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) + { + /* pai: FIXME What about operators and type conversions? */ + const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); + + if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) + { + int len = TYPE_FN_FIELDLIST_LENGTH (type, i); + f = TYPE_FN_FIELDLIST1 (type, i); + *fn_list = f; + + *num_fns = len; + *basetype = type; + *boffset = offset; + + /* Resolve any stub methods. */ + check_stub_method_group (type, i); + + break; + } + } - *num_fns = len; - *basetype = type; - *boffset = offset; + if (ext_fn_vec) + { + VEC (ext_fn_descriptor_p) *ef_vec = NULL, *new_vec = NULL; - /* Resolve any stub methods. */ - check_stub_method_group (type, i); + ef_vec = get_matching_ext_methods (type, method); + new_vec = VEC_merge (ext_fn_descriptor_p, *ext_fn_vec, ef_vec); - return f; - } + VEC_free (ext_fn_descriptor_p, *ext_fn_vec); + VEC_free (ext_fn_descriptor_p, ef_vec); + *ext_fn_vec = new_vec; } - /* Not found in object, check in base subobjects. */ + /* If source methods are not found in current class, look for them in the + base classes. We have to go through the base classes to gather extension + methods anyway. */ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--) { int base_offset; @@ -2482,13 +2502,11 @@ find_method_list (struct value **argp, const char *method, { base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8; } - f = find_method_list (argp, method, base_offset + offset, - TYPE_BASECLASS (type, i), num_fns, - basetype, boffset); - if (f) - return f; + + find_method_list (argp, method, base_offset + offset, + TYPE_BASECLASS (type, i), fn_list, num_fns, + ext_fn_vec, basetype, boffset); } - return NULL; } /* Return the list of overloaded methods of a specified name. @@ -2501,9 +2519,11 @@ find_method_list (struct value **argp, const char *method, method. BOFFSET is the offset of the base subobject which defines the method. */ -static struct fn_field * +static void value_find_oload_method_list (struct value **argp, const char *method, - int offset, int *num_fns, + int offset, struct fn_field **fn_list, + int *num_fns, + VEC (ext_fn_descriptor_p) **ext_fn_vec, struct type **basetype, int *boffset) { struct type *t; @@ -2525,8 +2545,32 @@ value_find_oload_method_list (struct value **argp, const char *method, error (_("Attempt to extract a component of a " "value that is not a struct or union")); - return find_method_list (argp, method, 0, t, num_fns, - basetype, boffset); + /* Clear the lists. */ + if (fn_list) + { + *fn_list = NULL; + *num_fns = 0; + } + if (ext_fn_vec) + *ext_fn_vec = VEC_alloc (ext_fn_descriptor_p, 1); + + find_method_list (argp, method, 0, t, fn_list, num_fns, ext_fn_vec, + basetype, boffset); +} + +static struct type * +value_has_indirect_dynamic_type (struct value *obj) +{ + struct type *stype, *dtype, *dtype_ind; + + stype = check_typedef (TYPE_TARGET_TYPE (value_type (obj))); + dtype_ind = value_rtti_indirect_type (obj, NULL, NULL, NULL); + dtype = dtype_ind ? check_typedef (TYPE_TARGET_TYPE (dtype_ind)) : stype; + + if (class_types_same_p (stype, dtype)) + return NULL; + else + return dtype_ind; } /* Given an array of arguments (ARGS) (which includes an @@ -2574,6 +2618,7 @@ find_overload_match (struct value **args, int nargs, const char *name, enum oload_search_type method, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, + struct ext_fn_descriptor **ext_fn, int *staticp, const int no_adl) { struct value *obj = (objp ? *objp : NULL); @@ -2581,16 +2626,24 @@ find_overload_match (struct value **args, int nargs, /* Index of best overloaded function. */ int func_oload_champ = -1; int method_oload_champ = -1; + int src_method_oload_champ = -1; + int src_method_oload_champ_bkp = -1; + int ext_method_oload_champ = -1; + int src_and_ext_equal = 0; /* The measure for the current best match. */ struct badness_vector *method_badness = NULL; struct badness_vector *func_badness = NULL; + struct badness_vector *ext_method_badness = NULL; + struct badness_vector *src_method_badness = NULL; struct value *temp = obj; /* For methods, the list of overloaded methods. */ struct fn_field *fns_ptr = NULL; /* For non-methods, the list of overloaded function symbols. */ struct symbol **oload_syms = NULL; + /* For extension functions, the VEC of extension function descriptors. */ + VEC (ext_fn_descriptor_p) *ext_fn_vec = NULL; /* Number of overloaded instances being considered. */ int num_fns = 0; struct type *basetype = NULL; @@ -2602,6 +2655,8 @@ find_overload_match (struct value **args, int nargs, const char *func_name = NULL; enum oload_classification match_quality; enum oload_classification method_match_quality = INCOMPATIBLE; + enum oload_classification src_method_match_quality = INCOMPATIBLE; + enum oload_classification ext_method_match_quality = INCOMPATIBLE; enum oload_classification func_match_quality = INCOMPATIBLE; /* Get the list of overloaded methods or functions. */ @@ -2630,12 +2685,12 @@ find_overload_match (struct value **args, int nargs, } /* Retrieve the list of methods with the name NAME. */ - fns_ptr = value_find_oload_method_list (&temp, name, - 0, &num_fns, - &basetype, &boffset); + value_find_oload_method_list (&temp, name, 0, &fns_ptr, &num_fns, + ext_fn ? &ext_fn_vec : NULL, + &basetype, &boffset); /* If this is a method only search, and no methods were found the search has faild. */ - if (method == METHOD && (!fns_ptr || !num_fns)) + if (method == METHOD && (!fns_ptr || !num_fns) && !ext_fn_vec) error (_("Couldn't find method %s%s%s"), obj_type_name, (obj_type_name && *obj_type_name) ? "::" : "", @@ -2646,18 +2701,82 @@ find_overload_match (struct value **args, int nargs, if (fns_ptr) { gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL); - method_oload_champ = find_oload_champ (args, nargs, method, - num_fns, fns_ptr, - oload_syms, &method_badness); - - method_match_quality = - classify_oload_match (method_badness, nargs, - oload_method_static (method, fns_ptr, - method_oload_champ)); - - make_cleanup (xfree, method_badness); + src_method_oload_champ = find_oload_champ (args, nargs, method, + num_fns, fns_ptr, NULL, + oload_syms, + &src_method_badness); + + src_method_match_quality = + classify_oload_match ( + src_method_badness, nargs, + oload_method_static (method, fns_ptr, + src_method_oload_champ)); + + make_cleanup (xfree, src_method_badness); } + if (ext_fn && VEC_length (ext_fn_descriptor_p, ext_fn_vec)) + { + ext_method_oload_champ = find_oload_champ (args, nargs, method, + 0, NULL, ext_fn_vec, + NULL, &ext_method_badness); + ext_method_match_quality = classify_oload_match (ext_method_badness, + nargs, 0); + make_cleanup (xfree, ext_method_badness); + make_ext_fn_vec_cleanup (ext_fn_vec); + } + + if (src_method_oload_champ >= 0 && ext_method_oload_champ >= 0) + { + switch (compare_badness (ext_method_badness, src_method_badness)) + { + case 0: /* Src method and ext method are equally good. */ + src_and_ext_equal = 1; + case 1: /* Src method and ext method are incompatible */ + /* if ext method match is not standard, then let source method + win. */ + if (ext_method_match_quality != STANDARD) + { + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + ext_method_oload_champ = -1; + method_match_quality = src_method_match_quality; + break; + } + case 2: /* Ext method is champion. */ + method_oload_champ = ext_method_oload_champ; + method_badness = ext_method_badness; + src_method_oload_champ_bkp = src_method_oload_champ; + src_method_oload_champ = -1; + method_match_quality = ext_method_match_quality; + break; + case 3: /* Src method is champion. */ + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + ext_method_oload_champ = -1; + method_match_quality = src_method_match_quality; + break; + default: + error (_("Internal error: unexpected overload comparison " + "result")); + break; + } + } + else + { + if (src_method_oload_champ >= 0) + { + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + method_match_quality = src_method_match_quality; + } + if (ext_method_oload_champ >= 0) + { + method_oload_champ = ext_method_oload_champ; + method_badness = ext_method_badness; + method_match_quality = ext_method_match_quality; + } + } } if (method == NON_METHOD || method == BOTH) @@ -2800,21 +2919,6 @@ find_overload_match (struct value **args, int nargs, func_name); } - if (staticp != NULL) - *staticp = oload_method_static (method, fns_ptr, method_oload_champ); - - if (method_oload_champ >= 0) - { - if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) - *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ, - basetype, boffset); - else - *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, - basetype, boffset); - } - else - *symp = oload_syms[func_oload_champ]; - if (objp) { struct type *temp_type = check_typedef (value_type (temp)); @@ -2824,11 +2928,66 @@ find_overload_match (struct value **args, int nargs, && (TYPE_CODE (objtype) == TYPE_CODE_PTR || TYPE_CODE (objtype) == TYPE_CODE_REF)) { - temp = value_addr (temp); + *objp = value_addr (temp); } - *objp = temp; + else + *objp = temp; } + if (staticp != NULL) + *staticp = oload_method_static (method, fns_ptr, method_oload_champ); + + if (method_oload_champ >= 0) + { + if (src_method_oload_champ >= 0) + { + if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) + { + struct type *dtype; + + dtype = value_has_indirect_dynamic_type (args[0]); + if (dtype) + { + args[0] = value_cast (dtype, args[0]); + do_cleanups (all_cleanups); + return find_overload_match (args, nargs, name, method, + objp, fsym, valp, symp, ext_fn, + staticp, no_adl); + } + else + *valp = value_virtual_fn_field (&temp, fns_ptr, + method_oload_champ, + basetype, boffset); + } + else + *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, + basetype, boffset); + } + else + { + if (src_and_ext_equal + && TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, src_method_oload_champ_bkp)) + { + struct type *dtype; + + dtype = value_has_indirect_dynamic_type (args[0]); + if (dtype) + { + args[0] = value_cast (dtype, args[0]); + do_cleanups (all_cleanups); + return find_overload_match (args, nargs, name, method, + objp, fsym, valp, symp, ext_fn, + staticp, no_adl); + } + } + + *ext_fn = ext_fn_clone (VEC_index (ext_fn_descriptor_p, ext_fn_vec, + ext_method_oload_champ)); + } + } + else + *symp = oload_syms[func_oload_champ]; + do_cleanups (all_cleanups); switch (match_quality) @@ -2962,7 +3121,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs, ++num_fns; new_oload_champ = find_oload_champ (args, nargs, 0, num_fns, - NULL, new_oload_syms, + NULL, NULL, new_oload_syms, &new_oload_champ_bv); /* Case 1: We found a good match. Free earlier matches (if any), @@ -3010,10 +3169,12 @@ find_oload_champ_namespace_loop (struct value **args, int nargs, static int find_oload_champ (struct value **args, int nargs, int method, int num_fns, struct fn_field *fns_ptr, + VEC (ext_fn_descriptor_p) *ext_fn_vec, struct symbol **oload_syms, struct badness_vector **oload_champ_bv) { int ix; + int ext_fn_vec_n = 0; /* A measure of how good an overloaded instance is. */ struct badness_vector *bv; /* Index of best overloaded function. */ @@ -3023,18 +3184,27 @@ find_oload_champ (struct value **args, int nargs, int method, /* 0 => no ambiguity, 1 => two good funcs, 2 => incomparable funcs. */ *oload_champ_bv = NULL; + ext_fn_vec_n = ext_fn_vec ? VEC_length (ext_fn_descriptor_p, ext_fn_vec) : 0; /* Consider each candidate in turn. */ - for (ix = 0; ix < num_fns; ix++) + for (ix = 0; (ix < num_fns) || (ix < ext_fn_vec_n); ix++) { int jj; - int static_offset = oload_method_static (method, fns_ptr, ix); + int static_offset = 0; int nparms; struct type **parm_types; + struct ext_fn_descriptor *ext_fn = NULL; + + if (ext_fn_vec) + ext_fn = VEC_index (ext_fn_descriptor_p, ext_fn_vec, ix); if (method) { - nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + if (fns_ptr) + { + static_offset = oload_method_static (method, fns_ptr, ix); + nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + } } else { @@ -3043,13 +3213,20 @@ find_oload_champ (struct value **args, int nargs, int method, } /* Prepare array of parameter types. */ - parm_types = (struct type **) - xmalloc (nparms * (sizeof (struct type *))); - for (jj = 0; jj < nparms; jj++) - parm_types[jj] = (method - ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) - : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), - jj)); + if (fns_ptr || oload_syms) + { + parm_types = (struct type **) + xmalloc (nparms * (sizeof (struct type *))); + for (jj = 0; jj < nparms; jj++) + parm_types[jj] = (method + ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) + : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), + jj)); + } + else + { + parm_types = ext_fn_get_argtypes (ext_fn, &nparms); + } /* Compare parameter types to supplied argument types. Skip THIS for static methods. */ diff --git a/gdb/value.h b/gdb/value.h index 4e839d3..c4bf51e 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -31,6 +31,7 @@ struct type; struct ui_file; struct language_defn; struct value_print_options; +struct ext_fn_descriptor; /* The structure which defines the type of a value. It should never be possible for a program lval value to survive over a call to the @@ -652,6 +653,7 @@ extern int find_overload_match (struct value **args, int nargs, enum oload_search_type method, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, + struct ext_fn_descriptor **ext_fn, int *staticp, const int no_adl); extern struct value *value_field (struct value *arg1, int fieldno); ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-06-17 19:10 ` Siva Chandra @ 2013-07-22 20:47 ` Tom Tromey 2013-11-12 2:56 ` Siva Chandra 0 siblings, 1 reply; 18+ messages in thread From: Tom Tromey @ 2013-07-22 20:47 UTC (permalink / raw) To: Siva Chandra; +Cc: gdb-patches >>>>> "Siva" == Siva Chandra <sivachandra@google.com> writes: Siva> Took me longer than I had expected I but could spend some time last Siva> couple of weeks and address all of Tom's comments from last time. Like Siva> before, I do not have docs or tests as the Python side API is largely Siva> un-reviewed I guess. However, I have put in code comments in the Siva> latest version. The patch is attached and the ChangeLog is as below: Thanks. I appreciate that you don't want to write all the docs and tests for a moving target. On the other hand, it's harder to review a patch that doesn't have these. A compromise would be if you wrote just the docs describing how it works, say the Python API. Then we could discuss this; and once ok, the implementation would follow. But, that is for future changes. For this one, I think we're in general agreement about the exposed API, and so polishing is a vital component. Siva> +/* Registers an extension language with GDB. */ Siva> + Siva> +void Siva> +register_ext_lang (struct ext_lang *lang) Siva> +{ Siva> + if (ext_lang_vec == NULL) Siva> + ext_lang_vec = VEC_alloc (ext_lang_p, 1); Siva> + Siva> + VEC_safe_push (ext_lang_p, ext_lang_vec, lang); The "extension language" code here seems like a lot of work for no real benefit. It's fine for the functions themselves, since we'd like to keep the API to the Python layer reasonably thin. But for get_matching_ext_methods, e.g., it is simple to follow the existing pattern: declare a function in python.h and provide both with- and without-Python implementations. Siva> +struct ext_lang Siva> + { Siva> + clone_ext_obj_ftype *clone_ext_object; Siva> + free_ext_obj_ftype *free_ext_obj; Siva> + get_matching_ext_methods_ftype *get_matching_ext_methods; Siva> + get_ext_fn_argtypes_ftype *get_ext_fn_argtypes; Siva> + invoke_method_ftype *invoke_method; Siva> + }; I think this struct should be given a different name. Typical in gdb is "ops". Siva> +extern struct ext_fn_descriptor *new_ext_function (struct ext_lang *lang, Siva> + int is_method, Siva> + void *ext_obj); It seems like 'lang' should be const here. Siva> + and the method name. It next get the argument types of these methods via s/get/gets/ Siva> + for old_method in existing_method_list: Siva> + objfile.debug_methods.remove(old_method) Siva> + objfile.debug_methods.extend(debug_methods) I notice this patch adds "debug_methods" to the objfile but not the program space. Updating the latter, and also providing a global debug_methods, is more in line with the other hooks we provide. Siva> +py_free_ext_object (void *ext_object) Siva> +{ Siva> + struct py_ext_object *o = (struct py_ext_object *) ext_object; You don't need casts to or from "void *". Siva> + enabled = PyObject_IsTrue (enabled_field); Siva> + if (enabled == -1) Siva> + { Siva> + PyErr_Clear (); Siva> + do_cleanups (cleanups); Silently ignoring errors doesn't seem right. There are a few instances of this. I'm not really sure about the error-handling strategy in this function. Siva> +static VEC (ext_fn_descriptor_p) * Siva> +py_debugmethod_name_match (struct type *obj_type, const char *method_name) [...] Siva> + if (method_vec == NULL) Siva> + method_vec = VEC_alloc (ext_fn_descriptor_p, 1); You don't need to explicitly allocate a vec like this. VEC_safe_push handles it. Siva> + self->debug_methods = PyList_New (0); Siva> + if (!self->debug_methods) Now we write "== NULL". Siva> + object->debug_methods = PyList_New (0); Siva> + if (!object->debug_methods) Ditto. Siva> +int gdbpy_initialize_debugmethods (void) Siva> + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; I'm curious if you ran this through the checker. It isn't a requirement; the checker gives enough false reports that it is a bit of a pain. Tom ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-07-22 20:47 ` Tom Tromey @ 2013-11-12 2:56 ` Siva Chandra 2013-11-15 22:28 ` Tom Tromey 0 siblings, 1 reply; 18+ messages in thread From: Siva Chandra @ 2013-11-12 2:56 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches [-- Attachment #1: Type: text/plain, Size: 2989 bytes --] Hi, I apologize for this long hiatus. I now have a new patch in response to Tom's last round of review. I am not very clear with some of his comments and have few questions in response. Also, Tom asked for docs with his last review. I have added a section to the doc which illustrates the debug method API/usage, but is in no way complete yet. 2013-11-11 Siva Chandra Reddy <sivachandra@google.com> Add Debug methods support in GDB. * Makefile.in: Add entries for new files * data-directory/Makefile.in: Add entry for new Python file * eval.c: Invoke methods defined in extension methods. * ext-function.c: Support for working with functions/methods defined in an extension language. * ext-function.h: Support for working with functions/methods defined in an extension language. * python/lib/gdb/debugmethods.py: Python side of the support for debug methods in Python. * python/py-debugmethods.c: C side of the support for debug methods in Python. * python/py-objfile.c: Add 'debug_methods' attribute to gdb.Objfile. * python/python-internal.h: Add new function gdb.enable_debug_methods to the Python module 'gdb'. * python/python.c: Add new function gdb.enable_debug_methods to the Python module 'gdb'. * python/python.h: Add declarations of new functions. * valarith.c: Invoke operators defined in extension languages. * valops.c: Lookup methods defined in extension languages. * value.h: New signature for 'find_overload_match'. On Mon, Jul 22, 2013 at 1:47 PM, Tom Tromey <tromey@redhat.com> wrote: > The "extension language" code here seems like a lot of work for no > real benefit. > > It's fine for the functions themselves, since we'd like to keep the API > to the Python layer reasonably thin. But for get_matching_ext_methods, > e.g., it is simple to follow the existing pattern: declare a function in > python.h and provide both with- and without-Python implementations. I am not sure I understand what is being said here. Could you kindly elaborate (pointing to an existing example)? > Silently ignoring errors doesn't seem right. > There are a few instances of this. > > I'm not really sure about the error-handling strategy in this function. They way I have looked at debug methods hook is as follows: "If there exists a Python implementation or replacement which matches better or is as good a match as the source implementation, then use the Python implementation. If there is any error looking up Python implementations, report the error but do not stop from executing an operation; proceed to use the source implementation." With that view, in the latest patch, I have modified to print the errors instead of silently ignoring them. Let me know if this does not seem to be a good strategy. > I'm curious if you ran this through the checker. I have not run it through the checker. [-- Attachment #2: dm_patch_v5.txt --] [-- Type: text/plain, Size: 70565 bytes --] diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 975edbf..3458424 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -287,6 +287,7 @@ SUBDIR_PYTHON_OBS = \ py-breakpoint.o \ py-cmd.o \ py-continueevent.o \ + py-debugmethods.o \ py-event.o \ py-evtregistry.o \ py-evts.o \ @@ -323,6 +324,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-breakpoint.c \ python/py-cmd.c \ python/py-continueevent.c \ + python/py-debugmethods.c \ python/py-event.c \ python/py-evtregistry.c \ python/py-evts.c \ @@ -733,7 +735,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 ext-function.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 \ @@ -835,7 +837,7 @@ rs6000-tdep.h rs6000-aix-tdep.h \ common/gdb_locale.h common/gdb_dirent.h arch-utils.h trad-frame.h gnu-nat.h \ language.h nbsd-tdep.h solib-svr4.h \ macroexp.h ui-file.h regcache.h tracepoint.h i386-tdep.h \ -inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \ +inf-child.h p-lang.h event-top.h ext-function.h gdbtypes.h user-regs.h \ regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \ config/i386/nm-fbsd.h \ config/nm-nto.h config/sparc/nm-sol2.h config/nm-linux.h \ @@ -957,7 +959,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o \ gdb_vecs.o jit.o progspace.o skip.o probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ - format.o registry.o btrace.o record-btrace.o waitstatus.o + format.o registry.o btrace.o record-btrace.o waitstatus.o \ + ext-function.o TSOBS = inflow.o @@ -2181,6 +2184,10 @@ py-continueevent.o: $(srcdir)/python/py-continueevent.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c $(POSTCOMPILE) +py-debugmethods.o: $(srcdir)/python/py-debugmethods.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-debugmethods.c + $(POSTCOMPILE) + py-event.o: $(srcdir)/python/py-event.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c $(POSTCOMPILE) diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 3d05213..e9e6b2e 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -61,6 +61,7 @@ PYTHON_FILES = \ gdb/types.py \ gdb/printing.py \ gdb/prompt.py \ + gdb/debugmethods.py \ gdb/command/__init__.py \ gdb/command/frame_filters.py \ gdb/command/type_printers.py \ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index c6974d7..6ec5b5d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -23472,6 +23472,7 @@ optional arguments while skipping others. Example: * Selecting Pretty-Printers:: How GDB chooses a pretty-printer. * Writing a Pretty-Printer:: Writing a Pretty-Printer. * Type Printing API:: Pretty-printing types. +* Debug Methods In Python:: Debug Methods In Python. * Frame Filter API:: Filtering Frames. * Frame Decorator API:: Decorating Frames. * Writing a Frame Filter:: Writing a Frame Filter. @@ -24855,6 +24856,75 @@ done then type printers would have to make use of the event system in order to avoid holding information that could become stale as the inferior changed. +@node Debug Methods In Python +@subsubsection Debug Methods In Python +@cindex debug methods in Python + +Debug methods are, as the name suggests, method definitions on classes +but defined in @value{GDBN} Python instead of the source language. If a +particular method (which could be an overloaded operator) needs to be +invoked to evaluate a certain expression in @value{GDBN}, then +@value{GDBN} invokes the Python implementation of the method if the +Python definition is better or as good a match as the implementation of +the method in the source language. A benefit of having a debug method +replacement for a source language method is that expressions involving +the method can be evaluated in @value{GDBN} without running the method +in the inferior. Another benefit is with regards to method which +typically get optimized out by the compiler. Since debug methods live +in @value{GDBN} Python and not in the source language, they are never +optimized out. Lastly, one could define a debug method in @value{GDBN} +Python which does not have an equivalent in the source language! + +The debug method API provided in @value{GDBN} Python can be illustrated +with the following example. Consider a C++ code snippet as follows: + +@smallexample +class A +@{ + public: + int geta(void) + @{ + return a; + @} + + private: + int a; +@}; +@end smallexample + +In the above example, the method @code{geta} in @code{class A}, might +get optimized out in a client executable if @code{geta} is never called. +However, one might want to call @code{geta} from @value{GDBN} for +debugging. For such cases, we could have an equivalent Python +implementation as a replacement for the C++ method. The debug method +can be defined in Python as follows: + +@smallexample +def A_geta(obj): + """Function which implemenents a Python replacement for A::geta. + Args: + obj: This is equivalent of the C++ 'this' + """ + return obj['a'] + +dm_A_geta = gdb.debugmethods.SimpleDebugMethod( + 'A_geta', # A unique name to the debug method + 'DEFAULT_DEBUG_METHOD_GROUP', # A group name for the debug method + A_geta, # The function implementing the debug + # method + '^dop::A$', # The class to which this debug + # method should belong. + '^geta$' # The regex which matches the method + # which this debug method is replacing. +) + +gdb.debugmethods.register_debug_methods( + gdb, # Register the debug method globally + [dm_A_geta] # The list of debug methods to register +) +@end smallexample + + @node Frame Filter API @subsubsection Filtering Frames. @cindex frame filters api diff --git a/gdb/eval.c b/gdb/eval.c index e83bfdf..b43d4d9 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -22,6 +22,7 @@ #include "symtab.h" #include "gdbtypes.h" #include "value.h" +#include "ext-function.h" #include "expression.h" #include "target.h" #include "frame.h" @@ -1596,7 +1597,7 @@ evaluate_subexp_standard (struct type *expect_type, NON_METHOD, /* not method */ NULL, NULL, /* pass NULL symbol since symbol is unknown */ - NULL, &symp, NULL, 0); + NULL, &symp, NULL, NULL, 0); /* Now fix the expression being evaluated. */ exp->elts[save_pos1 + 2].symbol = symp; @@ -1626,11 +1627,12 @@ evaluate_subexp_standard (struct type *expect_type, /* Language is C++, do some overload resolution before evaluation. */ struct value *valp = NULL; + struct ext_fn_descriptor *ext_fnp = NULL; (void) find_overload_match (&argvec[1], nargs, tstr, METHOD, /* method */ &arg2, /* the object */ - NULL, &valp, NULL, + NULL, &valp, NULL, &ext_fnp, &static_memfuncp, 0); if (op == OP_SCOPE && !static_memfuncp) @@ -1640,9 +1642,26 @@ evaluate_subexp_standard (struct type *expect_type, "`this' pointer"), function_name); } - argvec[1] = arg2; /* the ``this'' pointer */ - argvec[0] = valp; /* Use the method found after overload - resolution. */ + + if (ext_fnp) + { + if (ext_fn_is_method (ext_fnp)) + { + struct value *ret_val; + + ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2, + nargs - 1); + if (ret_val == NULL) + error (_("Error invoking debug method for method %s."), + tstr); + + return ret_val; + } + } + + argvec[1] = arg2; /* the ``this'' pointer */ + argvec[0] = valp; /* Use the method found after overload + resolution. */ } else /* Non-C++ case -- or no overload resolution. */ @@ -1701,7 +1720,7 @@ evaluate_subexp_standard (struct type *expect_type, NULL, /* no need for name */ NON_METHOD, /* not method */ NULL, function, /* the function */ - NULL, &symp, NULL, no_adl); + NULL, &symp, NULL, NULL, no_adl); if (op == OP_VAR_VALUE) { diff --git a/gdb/ext-function.c b/gdb/ext-function.c new file mode 100644 index 0000000..311c5d5 --- /dev/null +++ b/gdb/ext-function.c @@ -0,0 +1,172 @@ +/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "cleanups.h" +#include "ext-function.h" + +#include "gdb_assert.h" +#include "vec.h" + +struct ext_fn_descriptor + { + const struct ext_func_ops *lang; + + int is_method; + + void *ext_object; + }; + +typedef struct ext_func_ops *ext_lang_p; +DEF_VEC_P (ext_lang_p); +static VEC (ext_lang_p) *ext_lang_vec = NULL; + +/* Registers an extension language with GDB. */ + +void +register_ext_lang (struct ext_func_ops *lang) +{ + if (ext_lang_vec == NULL) + ext_lang_vec = VEC_alloc (ext_lang_p, 1); + + VEC_safe_push (ext_lang_p, ext_lang_vec, lang); +} + +/* Returns a new ext_fn_descriptor object. LANG is the extention language the + new extension function is implemented in. IS_METHOD indicates whether the + new extension function is a method. EXT_OBJ is the extension language + specific data to be encapsulated in the ext_fn_descriptor. */ + +struct ext_fn_descriptor * +new_ext_function (const struct ext_func_ops *lang, int is_method, void *ext_obj) +{ + struct ext_fn_descriptor *ext_fn = XCNEW (struct ext_fn_descriptor); + + ext_fn->is_method = is_method; + ext_fn->lang = lang; + ext_fn->ext_object = ext_obj; + + return ext_fn; +} + +/* Clones EXT_FN and returns a new but identical ext_fn_descriptor. */ + +struct ext_fn_descriptor * +ext_fn_clone (struct ext_fn_descriptor *ext_fn) +{ + struct ext_fn_descriptor *new_ext_fn; + const struct ext_func_ops *lang = ext_fn->lang; + + new_ext_fn = new_ext_function (lang, ext_fn->is_method, + lang->clone_ext_object (ext_fn->ext_object)); + + return new_ext_fn; +} + +/* If a method of name METHOD is to be invoked on an object of type TYPE, then + all entension languages are searched for implementations of methods with + name METHOD in the extension languages. All matches found are returned as + a vector 'struct ent_fn_descriptor' objects. If no matching methods are + found, NULL is returned. */ + +VEC (ext_fn_descriptor_p) * +get_matching_ext_methods (struct type *type, const char *method) +{ + VEC (ext_fn_descriptor_p) *ext_methods = NULL; + ext_lang_p lang; + int i; + + for (i = 0; VEC_iterate (ext_lang_p, ext_lang_vec, i, lang); i++) + { + VEC (ext_fn_descriptor_p) *lang_methods, *new_vec; + + lang_methods = lang->get_matching_ext_methods (type, method); + new_vec = VEC_merge (ext_fn_descriptor_p, ext_methods, lang_methods); + + VEC_free (ext_fn_descriptor_p, ext_methods); + VEC_free (ext_fn_descriptor_p, lang_methods); + ext_methods = new_vec; + } + + return ext_methods; +} + +/* Given an function EXT_FN implemented in an extension language, returns an + array of types of the arguments the function accepts. The length of the + array is returned in NARGS. The type of the 'this' object is returned as + the first argument if EXT_FN is a method. If EXT_FN does not take any + arguments, then NULL is returned with 0 in NARGS. */ + +struct type ** +ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs) +{ + gdb_assert (ext_fn && ext_fn->lang && ext_fn->lang->get_ext_fn_argtypes); + + return ext_fn->lang->get_ext_fn_argtypes (ext_fn->ext_object, nargs); +} + +/* If EXT_FN is a method implemented in an extension language, invokes it and + returns the resulting value. The method is invoked on OBJ with arguments + ARGS. NARGS is the length of the ARGS array. */ + +struct value * +ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj, + struct value **args, int nargs) +{ + gdb_assert (ext_fn && ext_fn->is_method && ext_fn->lang + && ext_fn->lang->invoke_method); + + return ext_fn->lang->invoke_method (ext_fn->ext_object, obj, args, nargs); +} + +/* Returns true if EXT_FN is a method, 0 otherwise. */ + +int +ext_fn_is_method (struct ext_fn_descriptor *ext_fn) +{ + if (ext_fn != NULL) + return ext_fn->is_method; + + return 0; +} + +/* Frees a vector of ext_fn_descriptors VEC. */ + +static void +ext_fn_vec_free (void *vec) +{ + int i; + struct ext_fn_descriptor *ext_fn; + VEC (ext_fn_descriptor_p) *v = (VEC (ext_fn_descriptor_p) *) vec; + + for (i = 0; VEC_iterate (ext_fn_descriptor_p, v, i, ext_fn); i++) + { + ext_fn->lang->free_ext_obj (ext_fn->ext_object); + xfree (ext_fn); + } +} + +/* Return a cleanup object to free a vector VEC of extension function + descriptors. */ + +struct cleanup * +make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec) +{ + return make_cleanup (ext_fn_vec_free, (void *) vec); +} diff --git a/gdb/ext-function.h b/gdb/ext-function.h new file mode 100644 index 0000000..db05009 --- /dev/null +++ b/gdb/ext-function.h @@ -0,0 +1,75 @@ +/* Support for functions defined in 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 <http://www.gnu.org/licenses/>. */ + +#if !defined (EXT_FUNCTION_H) +#define EXT_FUNCTION_H + +#include "vec.h" + +struct cleanup; +struct value; +struct type; +struct ext_fn_descriptor; + +typedef struct ext_fn_descriptor *ext_fn_descriptor_p; +DEF_VEC_P (ext_fn_descriptor_p); +typedef VEC (ext_fn_descriptor_p) ext_fn_vec; + +typedef struct value* (invoke_method_ftype) (void *ext_obj, + struct value *, + struct value **, int nargs); + +typedef void * (clone_ext_obj_ftype) (void *ext_obj); + +typedef void (free_ext_obj_ftype) (void *ext_obj); + +typedef ext_fn_vec *(get_matching_ext_methods_ftype) (struct type *type, + const char *method); + +typedef struct type** (get_ext_fn_argtypes_ftype) (void *ext_obj, int *nargs); + +struct ext_func_ops + { + clone_ext_obj_ftype *clone_ext_object; + free_ext_obj_ftype *free_ext_obj; + get_matching_ext_methods_ftype *get_matching_ext_methods; + get_ext_fn_argtypes_ftype *get_ext_fn_argtypes; + invoke_method_ftype *invoke_method; + }; + +extern void register_ext_lang (struct ext_func_ops *lang); + +extern struct value *ext_fn_invoke_method (struct ext_fn_descriptor *, + struct value *, + struct value **, int nargs); + +extern struct ext_fn_descriptor *ext_fn_clone (struct ext_fn_descriptor *); + +extern struct ext_fn_descriptor *new_ext_function ( + const struct ext_func_ops *lang, int is_method, void *ext_obj); + +extern ext_fn_vec *get_matching_ext_methods (struct type *, const char *); + +extern struct type **ext_fn_get_argtypes (struct ext_fn_descriptor *, int *); + +extern int ext_fn_is_method (struct ext_fn_descriptor *); + +extern struct cleanup* make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec); + +#endif /* EXT_FUNCTION_H */ diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 61f5b5e..d4d7ba7 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -67,6 +67,8 @@ pretty_printers = [] # Initial type printers. type_printers = [] +# Initial debug_methods. +debug_methods = [] # Initial frame filters. frame_filters = {} diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py new file mode 100644 index 0000000..f76ad54 --- /dev/null +++ b/gdb/python/lib/gdb/debugmethods.py @@ -0,0 +1,118 @@ +# Python side of the support for debug methods. +# Copyright (C) 2013 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/>. + +"""Utilities for defining debug methods""" + +import gdb +import re + +DEFAULT_DEBUG_METHOD_GROUP = 'DEFAULT_DEBUG_METHOD_GROUP' + + +class DebugMethod(object): + """Base class for all debug methods defined in Python. + + A debug method defined in Python should be derived from this class. The + derived classes should override the methods 'match', 'get_argtypes' and + 'invoke'. + + Internally, GDB first invokes the 'match' method to match the class type + and the method name. It next gets the argument types of these methods via + the 'get_argtypes' method to perform overload resolution. + """ + + def __init__(self, name, group=DEFAULT_DEBUG_METHOD_GROUP): + """ + Args: + name: An identifying name for the debug method. + group: The group name of the group to which the debug method + belongs to. + """ + self.name = name + self.group = group + self.enabled = True + + def match(self, class_type, method_name): + """Match class type and method name. + + Args: + class_type: The class type to match + method_name: The name of the method to match. + """ + gdb.GdbError('ERROR: Invoking abstract method \'match\'.') + + def get_argtypes(self, class_type, method_name): + """Return a list of types, as gdb.Type objects, of the arguments of + the debug method. + + Args: + class_type: The gdb.Type value of the class on which the method + is defined. + method_name: The name of the method whose argument types are to + be returned. + """ + gdb.GdbError('ERROR: Invoking abstract method \'get_argtypes\'.') + + def invoke(self, obj, args): + """Invoke the debug method. + + Args: + obj: The gdb.Value of the object on which the method is to be + invoked. + args: The tuple of arguments to the method. + """ + gdb.GdbError('ERROR: Invoking abstract method \'invoke\'.') + + +class SimpleDebugMethod(DebugMethod): + """This is a utility class which does name match by class name of the + objects on which the debug methods are defined. + """ + def __init__(self, name, group, method_function, class_matcher, + method_matcher, *argtypes): + DebugMethod.__init__(self, name, group) + self._method_function = method_function + self._class_matcher = class_matcher + self._method_matcher = method_matcher + self._argtypes = argtypes + + def match(self, class_type, method_name): + cm = re.match(self._class_matcher, str(class_type.unqualified().tag)) + mm = re.match(self._method_matcher, method_name) + if cm and mm: + return True + else: + return False + + def get_argtypes(self, class_type, method_name): + return self._argtypes + + def invoke(self, obj, args): + return self._method_function(obj, *args) + + +def register_debug_methods(objfile, debug_methods): + existing_method_list = [] + for new_method in debug_methods: + for old_method in objfile.debug_methods: + if (new_method.group == old_method.group and + new_method.name == old_method.name): + print ('WARNING: Replacing debug method with name "%s" in ' + 'group "%s".' % (old_method.name, old_method.group)) + existing_method_list.append(old_method) + for old_method in existing_method_list: + objfile.debug_methods.remove(old_method) + objfile.debug_methods.extend(debug_methods) diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c new file mode 100644 index 0000000..36ee260 --- /dev/null +++ b/gdb/python/py-debugmethods.c @@ -0,0 +1,560 @@ +/* Support for debug methods in Python. + + 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 <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "arch-utils.h" +#include "ext-function.h" +#include "objfiles.h" +#include "value.h" +#include "language.h" + +#include "python.h" + +#ifdef HAVE_PYTHON +#include "python-internal.h" + +struct py_ext_object +{ + /* Holds an instance of the DebugMethod class. */ + PyObject *object; + + /* Holds the type of the 'this' object. */ + PyObject *match_py_obj_type; + + /* Holds the matching method name. */ + const char *match_method; +}; + +static const char *enabled_field_name = "enabled"; +static const char *match_method_name = "match"; +static const char *get_argtypes_method_name = "get_argtypes"; +static const char *invoke_method_name = "invoke"; + +static PyObject *match_method_pystring = NULL; +static PyObject *get_argtypes_method_pystring = NULL; +static PyObject *invoke_method_pystring = NULL; + +static struct ext_fn_descriptor *new_python_ext_method (PyObject *item, + PyObject *py_obj_type, + const char *method); + +/* Implementation of free_ext_obj_ftype. */ + +static void +py_free_ext_object (void *ext_object) +{ + struct py_ext_object *o = ext_object; + + Py_XDECREF (o->object); + Py_XDECREF (o->match_py_obj_type); + xfree (o); +} + +/* Implementation of clone_ext_obj_ftype. */ + +static void * +py_clone_ext_object (void *obj) +{ + struct py_ext_object *new_obj, *old_obj; + + old_obj = (struct py_ext_object *) obj; + + new_obj = XCNEW (struct py_ext_object); + new_obj->object = old_obj->object; + new_obj->match_py_obj_type = old_obj->match_py_obj_type; + new_obj->match_method = old_obj->match_method; + + Py_XINCREF (new_obj->object); + Py_XINCREF (new_obj->match_py_obj_type); + + return (void *) new_obj; +} + +/* Returns true if gdb.Type object PY_OBJ_TYPE has a method defined in Python + with name METHOD_NAME. */ + +static int +is_matching_debug_method (PyObject *dm_obj, PyObject *py_obj_type, + const char *method_name) +{ + PyObject *method_name_pystring; + PyObject *match_method, *enabled_field, *match_result; + struct cleanup *cleanups; + int enabled, match; + + if (method_name == NULL) + return 0; + + cleanups = make_cleanup (null_cleanup, NULL); + + enabled_field = PyObject_GetAttrString (dm_obj, enabled_field_name); + if (enabled_field == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (enabled_field); + + enabled = PyObject_IsTrue (enabled_field); + if (enabled == -1) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + if (enabled == 0) + { + do_cleanups (cleanups); + return 0; + } + + match_method = PyObject_GetAttrString (dm_obj, match_method_name); + if (match_method == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (match_method); + if (!PyCallable_Check (match_method)) + { + warning (_("Attribute '%s' of a registered Python debug method is not " + "callable. Ignored!"), match_method_name); + do_cleanups (cleanups); + + return 0; + } + + method_name_pystring = PyString_FromString (method_name); + if (method_name_pystring == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (method_name_pystring); + + match_result = PyObject_CallMethodObjArgs (dm_obj, + match_method_pystring, + py_obj_type, + method_name_pystring, + NULL); + if (match_result == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + make_cleanup_py_decref (match_result); + + match = PyObject_IsTrue (match_result); + if (match == -1) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return 0; + } + + do_cleanups (cleanups); + + return match; +} + +/* Implementation of get_matching_ext_methods_ftype. + Return a of vector methods with name METHOD_NAME defined in Python for + objects of type OBJ_TYPE. Returns NULL if no matches are found. */ + +static VEC (ext_fn_descriptor_p) * +py_debugmethod_name_match (struct type *obj_type, const char *method_name) +{ + struct cleanup *cleanups; + struct objfile *objfile; + VEC (ext_fn_descriptor_p) *method_vec = NULL; + PyObject *py_type, *py_progspace; + PyObject *py_debugmethod_list = NULL, *list_iter, *item; + + if (obj_type == NULL || method_name == NULL) + return method_vec; + + py_type = type_to_type_object (obj_type); + if (py_type == NULL) + return method_vec; + make_cleanup_py_decref (py_type); + + /* Create an empyt list of debug methods. */ + py_debugmethod_list = PyList_New (0); + if (py_debugmethod_list == NULL) + return NULL; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + /* Gather debug methods registered with the object files. */ + ALL_OBJFILES (objfile) + { + PyObject *py_objfile = objfile_to_objfile_object (objfile); + PyObject *objfile_dmlist, *temp; + + if (py_objfile == NULL) + { + gdbpy_print_stack (); + continue; + } + + objfile_dmlist = objfpy_get_debug_methods (py_objfile, NULL); + temp = py_debugmethod_list; + py_debugmethod_list = PySequence_Concat (temp, objfile_dmlist); + if (py_debugmethod_list == NULL) + py_debugmethod_list = temp; + else + Py_DECREF (temp); + Py_XDECREF (objfile_dmlist); + } + + /* Gather debug methods registered with the current program space. */ + py_progspace = pspace_to_pspace_object (current_program_space); + if (py_progspace != NULL) + { + PyObject *temp = py_debugmethod_list; + PyObject *pspace_dmlist = pspy_get_debug_methods (py_progspace, NULL); + + py_debugmethod_list = PySequence_Concat (temp, pspace_dmlist); + if (py_debugmethod_list == NULL) + py_debugmethod_list = temp; + else + Py_DECREF (temp); + Py_XDECREF (pspace_dmlist); + } + + /* Gather debug methods registered globally. */ + if (gdb_python_module != NULL + && PyObject_HasAttrString (gdb_python_module, "debug_methods")) + { + PyObject *gdb_dmlist; + PyObject *temp = py_debugmethod_list; + + gdb_dmlist = PyObject_GetAttrString (gdb_python_module, "debug_methods"); + if (gdb_dmlist != NULL && PyList_Check (gdb_dmlist)) + { + py_debugmethod_list = PySequence_Concat (temp, gdb_dmlist); + if (py_debugmethod_list == NULL) + py_debugmethod_list = temp; + else + Py_DECREF (temp); + Py_XDECREF (gdb_dmlist); + } + } + + list_iter = PyObject_GetIter (py_debugmethod_list); + if (list_iter == NULL) + { + gdbpy_print_stack (); + Py_DECREF (py_debugmethod_list); + do_cleanups (cleanups); + return NULL; + } + + while ((item = PyIter_Next (list_iter))) + { + if (is_matching_debug_method (item, py_type, method_name)) + { + struct ext_fn_descriptor *ext_fn; + + ext_fn = new_python_ext_method (item, py_type, method_name); + VEC_safe_push (ext_fn_descriptor_p, method_vec, ext_fn); + } + Py_DECREF (item); + } + Py_DECREF (list_iter); + /* Report any error that could have occurred while iterating. */ + if (PyErr_Occurred ()) + gdbpy_print_stack (); + + Py_DECREF (py_debugmethod_list); + do_cleanups (cleanups); + + return method_vec; +} + +/* Implementation of get_ext_fn_argtypes_ftype. + Return an arry of argument types for extension encapsulated in EXT_OBJ. + NARGS contains the length of the array. */ + +static struct type ** +py_ext_fn_get_argtypes (void *ext_obj, int *nargs) +{ + struct py_ext_object *ext_object = (struct py_ext_object *) ext_obj; + PyObject *debug_method_obj = ext_object->object; + PyObject *get_argtypes_method; + PyObject *py_argtype_list, *list_iter, *item; + struct cleanup *cleanups; + struct type **type_array, *obj_type; + int i = 1; + + /* Set nargs to 0 so that any premature return from this function returns + 0 arg types. */ + *nargs = 0; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + get_argtypes_method = PyObject_GetAttrString (debug_method_obj, + get_argtypes_method_name); + if (get_argtypes_method == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (get_argtypes_method); + + if (!PyCallable_Check (get_argtypes_method)) + { + warning (_("Attribute '%s' of a registered Python debug method is not " + "callable. Ignored!"), get_argtypes_method_name); + do_cleanups (cleanups); + + return NULL; + } + + py_argtype_list = PyObject_CallMethodObjArgs (debug_method_obj, + get_argtypes_method_pystring, + ext_object->match_py_obj_type, + ext_object->match_method, + NULL); + if (py_argtype_list == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (py_argtype_list); + + list_iter = PyObject_GetIter (py_argtype_list); + if (list_iter == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (list_iter); + + /* Include the 'this' argument in the size. */ + type_array = XCNEWVEC (struct type *, PyList_GET_SIZE (py_argtype_list) + 1); + while ((item = PyIter_Next (list_iter))) + { + struct type *arg_type = type_object_to_type (item); + + Py_DECREF (item); + + if (arg_type == NULL) + { + i = -1; + break; + } + + type_array[i] = arg_type; + i++; + } + if (PyErr_Occurred () || i == -1) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + xfree (type_array); + + return NULL; + } + + /* Add the type of 'this' as the first argument. */ + obj_type = type_object_to_type (ext_object->match_py_obj_type); + type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL); + *nargs = i; + + do_cleanups (cleanups); + + return type_array; +} + +/* Implementation of invoke_method_ftype. + Invokes a method defined in Python. The value returned by the method is + returned. NULL is returned in case of errors. */ + +static struct value * +py_ext_fn_invoke_method (void *ext_obj, struct value *obj, struct value **args, + int nargs) +{ + int i; + struct cleanup *cleanups; + PyObject *py_value_obj, *py_arg_tuple, *py_result, *debug_method_obj; + PyObject *invoke_method; + struct value *result = NULL; + + cleanups = ensure_python_env (get_current_arch (), current_language); + + debug_method_obj = ((struct py_ext_object *) ext_obj)->object; + + invoke_method = PyObject_GetAttrString (debug_method_obj, + invoke_method_name); + if (invoke_method == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (invoke_method); + + if (!PyCallable_Check (invoke_method)) + { + warning (_("Attribute '%s' of a registered Python debug method is not " + "callable. Ignored!"), invoke_method_name); + do_cleanups (cleanups); + + return NULL; + } + + py_value_obj = value_to_value_object (obj); + if (py_value_obj == NULL) + { + do_cleanups (cleanups); + return NULL; + } + make_cleanup_py_decref (py_value_obj); + + py_arg_tuple = PyTuple_New (nargs); + if (py_arg_tuple == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (py_arg_tuple); + + for (i = 0; i < nargs; i++) + { + PyObject *py_value_arg = value_to_value_object (args[i]); + + if (py_value_arg == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + return NULL; + } + + if (PyTuple_SetItem (py_arg_tuple, i, py_value_arg)) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + } + + py_result = PyObject_CallMethodObjArgs (debug_method_obj, + invoke_method_pystring, + py_value_obj, py_arg_tuple, NULL); + if (py_result == NULL) + { + gdbpy_print_stack (); + do_cleanups (cleanups); + + return NULL; + } + make_cleanup_py_decref (py_result); + + /* Check that a debug method did not return None. */ + if (py_result != Py_None) + { + result = convert_value_from_python (py_result); + if (result == NULL) + { + gdbpy_print_stack (); + } + } + else + result = allocate_value (lookup_typename (python_language, python_gdbarch, + "void", NULL, 0)); + + do_cleanups (cleanups); + + return result; +} + +static struct ext_func_ops python_ext_lang = { + py_clone_ext_object, + py_free_ext_object, + py_debugmethod_name_match, + py_ext_fn_get_argtypes, + py_ext_fn_invoke_method +}; + +/* Creates a new python ext_function_descriptor. DEBUG_METHOD is the + debug method object. PY_OBJ_TYPE is a gdb.Type value corresponding to the + type of the object on which the debug method with name METHOD will be + invoked. */ + +static struct ext_fn_descriptor * +new_python_ext_method (PyObject *debug_method, PyObject *py_obj_type, + const char *method) +{ + struct py_ext_object *ext_object; + + ext_object = XCNEW (struct py_ext_object); + ext_object->object = debug_method; + ext_object->match_py_obj_type = py_obj_type; + ext_object->match_method = method; + + Py_XINCREF (ext_object->object); + Py_XINCREF (ext_object->match_py_obj_type); + + return new_ext_function (&python_ext_lang, 1, ext_object); +} + +/* Initializes the Python debug method support. */ + +int +gdbpy_initialize_debugmethods (void) +{ + register_ext_lang (&python_ext_lang); + + match_method_pystring = PyString_FromString (match_method_name); + if (match_method_pystring == NULL) + return -1; + + invoke_method_pystring = PyString_FromString (invoke_method_name); + if (invoke_method_pystring == NULL) + return -1; + + get_argtypes_method_pystring = PyString_FromString (get_argtypes_method_name); + if (get_argtypes_method_pystring == NULL) + return -1; + + return 1; +} + +#endif /* HAVE_PYTHON */ diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index 9bbd4c2..4aa0a1a 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -37,6 +37,9 @@ typedef struct PyObject *frame_filters; /* The type-printer list. */ PyObject *type_printers; + + /* The debug method list. */ + PyObject *debug_methods; } objfile_object; static PyTypeObject objfile_object_type @@ -67,6 +70,7 @@ objfpy_dealloc (PyObject *o) Py_XDECREF (self->printers); Py_XDECREF (self->frame_filters); Py_XDECREF (self->type_printers); + Py_XDECREF (self->debug_methods); Py_TYPE (self)->tp_free (self); } @@ -99,6 +103,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) Py_DECREF (self); return NULL; } + + self->debug_methods = PyList_New (0); + if (self->debug_methods == NULL) + { + Py_DECREF (self); + return NULL; + } } return (PyObject *) self; } @@ -193,6 +204,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore) return self->type_printers; } +/* Get the 'debug_methods' attribute. */ + +PyObject * +objfpy_get_debug_methods (PyObject *o, void *ignore) +{ + objfile_object *self = (objfile_object *) o; + + Py_INCREF (self->debug_methods); + return self->debug_methods; +} + /* Set the 'type_printers' attribute. */ static int @@ -292,6 +314,13 @@ objfile_to_objfile_object (struct objfile *objfile) return NULL; } + object->debug_methods = PyList_New (0); + if (object->debug_methods == NULL) + { + Py_DECREF (object); + return NULL; + } + set_objfile_data (objfile, objfpy_objfile_data_key, object); } } @@ -333,6 +362,8 @@ static PyGetSetDef objfile_getset[] = objfpy_set_frame_filters, "Frame Filters.", NULL }, { "type_printers", objfpy_get_type_printers, objfpy_set_type_printers, "Type printers.", NULL }, + { "debug_methods", objfpy_get_debug_methods, NULL, + "Debug methods.", NULL }, { NULL } }; diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c index 910c6a3..d425159 100644 --- a/gdb/python/py-progspace.c +++ b/gdb/python/py-progspace.c @@ -39,6 +39,9 @@ typedef struct PyObject *frame_filters; /* The type-printer list. */ PyObject *type_printers; + + /* The debug method list. */ + PyObject *debug_methods; } pspace_object; static PyTypeObject pspace_object_type @@ -75,6 +78,7 @@ pspy_dealloc (PyObject *self) Py_XDECREF (ps_self->printers); Py_XDECREF (ps_self->frame_filters); Py_XDECREF (ps_self->type_printers); + Py_XDECREF (ps_self->debug_methods); Py_TYPE (self)->tp_free (self); } @@ -107,6 +111,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords) Py_DECREF (self); return NULL; } + + self->debug_methods = PyList_New (0); + if (self->debug_methods == NULL) + { + Py_DECREF (self); + return NULL; + } } return (PyObject *) self; } @@ -201,6 +212,17 @@ pspy_get_type_printers (PyObject *o, void *ignore) return self->type_printers; } +/* Get the 'debug_methods' attribute. */ + +PyObject * +pspy_get_debug_methods (PyObject *o, void *ignore) +{ + pspace_object *self = (pspace_object *) o; + + Py_INCREF (self->debug_methods); + return self->debug_methods; +} + /* Set the 'type_printers' attribute. */ static int @@ -288,6 +310,13 @@ pspace_to_pspace_object (struct program_space *pspace) return NULL; } + object->debug_methods = PyList_New (0); + if (object->debug_methods == NULL) + { + Py_DECREF (object); + return NULL; + } + set_program_space_data (pspace, pspy_pspace_data_key, object); } } @@ -320,6 +349,8 @@ static PyGetSetDef pspace_getset[] = "Frame filters.", NULL }, { "type_printers", pspy_get_type_printers, pspy_set_type_printers, "Type printers.", NULL }, + { "debug_methods", pspy_get_debug_methods, NULL, + "Debug methods.", NULL }, { NULL } }; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 7d8c4ad..62eb49b 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -316,11 +316,13 @@ PyObject *pspace_to_pspace_object (struct program_space *) CPYCHECKER_RETURNS_BORROWED_REF; PyObject *pspy_get_printers (PyObject *, void *); PyObject *pspy_get_frame_filters (PyObject *, void *); +PyObject *pspy_get_debug_methods (PyObject *, void *); PyObject *objfile_to_objfile_object (struct objfile *) CPYCHECKER_RETURNS_BORROWED_REF; PyObject *objfpy_get_printers (PyObject *, void *); PyObject *objfpy_get_frame_filters (PyObject *, void *); +PyObject *objfpy_get_debug_methods (PyObject *, void *); PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch); @@ -401,6 +403,8 @@ int gdbpy_initialize_new_objfile_event (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; int gdbpy_initialize_arch (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; +int gdbpy_initialize_debugmethods (void) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; struct cleanup *make_cleanup_py_decref (PyObject *py); struct cleanup *make_cleanup_py_xdecref (PyObject *py); diff --git a/gdb/python/python.c b/gdb/python/python.c index 7cdf8a7..f5a0132 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1687,7 +1687,8 @@ message == an error message without a stack will be printed."), || gdbpy_initialize_exited_event () < 0 || gdbpy_initialize_thread_event () < 0 || gdbpy_initialize_new_objfile_event () < 0 - || gdbpy_initialize_arch () < 0) + || gdbpy_initialize_arch () < 0 + || gdbpy_initialize_debugmethods () < 0) goto fail; observer_attach_before_prompt (before_prompt_hook); diff --git a/gdb/valarith.c b/gdb/valarith.c index 18c14fc..f105200 100644 --- a/gdb/valarith.c +++ b/gdb/valarith.c @@ -30,6 +30,7 @@ #include <math.h> #include "infcall.h" #include "exceptions.h" +#include "ext-function.h" /* Define whether or not the C operator '/' truncates towards zero for differently signed operands (truncation direction is undefined in C). */ @@ -285,21 +286,27 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1) explicitly, and perform correct overload resolution in all of the above situations or combinations thereof. */ -static struct value * +static void value_user_defined_cpp_op (struct value **args, int nargs, char *operator, + struct value **src_fn, + struct ext_fn_descriptor **ext_fn, int *static_memfuncp) { struct symbol *symp = NULL; struct value *valp = NULL; + struct ext_fn_descriptor *ext_fnp = NULL; find_overload_match (args, nargs, operator, BOTH /* could be method */, &args[0] /* objp */, NULL /* pass NULL symbol since symbol is unknown */, - &valp, &symp, static_memfuncp, 0); + &valp, &symp, &ext_fnp, static_memfuncp, 0); if (valp) - return valp; + { + *src_fn = valp; + return; + } if (symp) { @@ -307,7 +314,14 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator, expect a reference as its first argument rather the explicit structure. */ args[0] = value_ind (args[0]); - return value_of_variable (symp, 0); + *src_fn = value_of_variable (symp, 0); + return; + } + + if (ext_fnp) + { + *ext_fn = ext_fnp; + return; } error (_("Could not find %s."), operator); @@ -316,19 +330,22 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator, /* Lookup user defined operator NAME. Return a value representing the function, otherwise return NULL. */ -static struct value * +static void value_user_defined_op (struct value **argp, struct value **args, char *name, - int *static_memfuncp, int nargs) + int *static_memfuncp, int nargs, + struct value **src_fn, struct ext_fn_descriptor **ext_fn) { struct value *result = NULL; if (current_language->la_language == language_cplus) - result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp); + value_user_defined_cpp_op (args, nargs, name, src_fn, ext_fn, + static_memfuncp); else - result = value_struct_elt (argp, args, name, static_memfuncp, - "structure"); - - return result; + { + result = value_struct_elt (argp, args, name, static_memfuncp, + "structure"); + *src_fn = result; + } } /* We know either arg1 or arg2 is a structure, so try to find the right @@ -345,6 +362,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, enum exp_opcode otherop, enum noside noside) { struct value **argvec; + struct ext_fn_descriptor *ext_fn = NULL; char *ptr; char tstr[13]; int static_memfuncp; @@ -359,6 +377,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Can't do that binary op on that type")); /* FIXME be explicit */ argvec = (struct value **) alloca (sizeof (struct value *) * 4); + argvec[0] = NULL; argvec[1] = value_addr (arg1); argvec[2] = arg2; argvec[3] = 0; @@ -471,8 +490,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, error (_("Invalid binary operation specified.")); } - argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, - &static_memfuncp, 2); + value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, 2, + &argvec[0], &ext_fn); if (argvec[0]) { @@ -492,6 +511,19 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op, return call_function_by_hand (argvec[0], 2 - static_memfuncp, argvec + 1); } + if (ext_fn) + { + if (ext_fn_is_method (ext_fn)) + { + struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, &arg2, 1); + + if (ret_val == NULL) + error (_("Error invoking debug method implementation for " + "method %s"), tstr); + + return ret_val; + } + } throw_error (NOT_FOUND_ERROR, _("member function %s not found"), tstr); #ifdef lint @@ -510,6 +542,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) { struct gdbarch *gdbarch = get_type_arch (value_type (arg1)); struct value **argvec; + struct ext_fn_descriptor *ext_fn; char *ptr; char tstr[13], mangle_tstr[13]; int static_memfuncp, nargs; @@ -523,6 +556,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Can't do that unary op on that type")); /* FIXME be explicit */ argvec = (struct value **) alloca (sizeof (struct value *) * 4); + argvec[0] = NULL; argvec[1] = value_addr (arg1); argvec[2] = 0; @@ -574,8 +608,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) error (_("Invalid unary operation specified.")); } - argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr, - &static_memfuncp, nargs); + value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, nargs, + &argvec[0], &ext_fn); if (argvec[0]) { @@ -594,6 +628,19 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside) return value_zero (return_type, VALUE_LVAL (arg1)); } return call_function_by_hand (argvec[0], nargs, argvec + 1); + } + if (ext_fn) + { + if (ext_fn_is_method (ext_fn)) + { + struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, NULL, 0); + + if (ret_val == NULL) + error (_("Error invoking debug method implementation for " + "method %s"), tstr); + + return ret_val; + } } throw_error (NOT_FOUND_ERROR, _("member function %s not found"), tstr); diff --git a/gdb/valops.c b/gdb/valops.c index 8bff686..e2580e4 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -44,6 +44,7 @@ #include "objfiles.h" #include "symtab.h" #include "exceptions.h" +#include "ext-function.h" extern unsigned int overload_debug; /* Local functions. */ @@ -72,8 +73,8 @@ int find_oload_champ_namespace_loop (struct value **, int, const int no_adl); static int find_oload_champ (struct value **, int, int, int, - struct fn_field *, struct symbol **, - struct badness_vector **); + struct fn_field *, VEC (ext_fn_descriptor_p) *, + struct symbol **, struct badness_vector **); static int oload_method_static (int, struct fn_field *, int); @@ -100,9 +101,10 @@ static CORE_ADDR allocate_space_in_inferior (int); static struct value *cast_into_complex (struct type *, struct value *); -static struct fn_field *find_method_list (struct value **, const char *, - int, struct type *, int *, - struct type **, int *); +static void find_method_list (struct value **, const char *, + int, struct type *, struct fn_field **, int *, + VEC (ext_fn_descriptor_p) **, + struct type **, int *); void _initialize_valops (void); @@ -2261,40 +2263,58 @@ value_struct_elt (struct value **argp, struct value **args, method is found. BOFFSET is the offset of the base subobject where the method is found. */ -static struct fn_field * +static void find_method_list (struct value **argp, const char *method, - int offset, struct type *type, int *num_fns, + int offset, struct type *type, + struct fn_field **fn_list, int *num_fns, + VEC (ext_fn_descriptor_p) **ext_fn_vec, struct type **basetype, int *boffset) { int i; - struct fn_field *f; - CHECK_TYPEDEF (type); + struct fn_field *f = NULL; - *num_fns = 0; + CHECK_TYPEDEF (type); /* First check in object itself. */ - for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) - { - /* pai: FIXME What about operators and type conversions? */ - const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); - if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) - { - int len = TYPE_FN_FIELDLIST_LENGTH (type, i); - struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); + if (fn_list && !(*fn_list)) + for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--) + { + /* pai: FIXME What about operators and type conversions? */ + const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i); + + if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0)) + { + int len = TYPE_FN_FIELDLIST_LENGTH (type, i); + f = TYPE_FN_FIELDLIST1 (type, i); + *fn_list = f; + + *num_fns = len; + *basetype = type; + *boffset = offset; + + /* Resolve any stub methods. */ + check_stub_method_group (type, i); + + break; + } + } - *num_fns = len; - *basetype = type; - *boffset = offset; + if (ext_fn_vec) + { + VEC (ext_fn_descriptor_p) *ef_vec = NULL, *new_vec = NULL; - /* Resolve any stub methods. */ - check_stub_method_group (type, i); + ef_vec = get_matching_ext_methods (type, method); + new_vec = VEC_merge (ext_fn_descriptor_p, *ext_fn_vec, ef_vec); - return f; - } + VEC_free (ext_fn_descriptor_p, *ext_fn_vec); + VEC_free (ext_fn_descriptor_p, ef_vec); + *ext_fn_vec = new_vec; } - /* Not found in object, check in base subobjects. */ + /* If source methods are not found in current class, look for them in the + base classes. We have to go through the base classes to gather extension + methods anyway. */ for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--) { int base_offset; @@ -2311,13 +2331,11 @@ find_method_list (struct value **argp, const char *method, { base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8; } - f = find_method_list (argp, method, base_offset + offset, - TYPE_BASECLASS (type, i), num_fns, - basetype, boffset); - if (f) - return f; + + find_method_list (argp, method, base_offset + offset, + TYPE_BASECLASS (type, i), fn_list, num_fns, + ext_fn_vec, basetype, boffset); } - return NULL; } /* Return the list of overloaded methods of a specified name. @@ -2330,9 +2348,11 @@ find_method_list (struct value **argp, const char *method, method. BOFFSET is the offset of the base subobject which defines the method. */ -static struct fn_field * +static void value_find_oload_method_list (struct value **argp, const char *method, - int offset, int *num_fns, + int offset, struct fn_field **fn_list, + int *num_fns, + VEC (ext_fn_descriptor_p) **ext_fn_vec, struct type **basetype, int *boffset) { struct type *t; @@ -2354,8 +2374,32 @@ value_find_oload_method_list (struct value **argp, const char *method, error (_("Attempt to extract a component of a " "value that is not a struct or union")); - return find_method_list (argp, method, 0, t, num_fns, - basetype, boffset); + /* Clear the lists. */ + if (fn_list) + { + *fn_list = NULL; + *num_fns = 0; + } + if (ext_fn_vec) + *ext_fn_vec = VEC_alloc (ext_fn_descriptor_p, 1); + + find_method_list (argp, method, 0, t, fn_list, num_fns, ext_fn_vec, + basetype, boffset); +} + +static struct type * +value_has_indirect_dynamic_type (struct value *obj) +{ + struct type *stype, *dtype, *dtype_ind; + + stype = check_typedef (TYPE_TARGET_TYPE (value_type (obj))); + dtype_ind = value_rtti_indirect_type (obj, NULL, NULL, NULL); + dtype = dtype_ind ? check_typedef (TYPE_TARGET_TYPE (dtype_ind)) : stype; + + if (class_types_same_p (stype, dtype)) + return NULL; + else + return dtype_ind; } /* Given an array of arguments (ARGS) (which includes an @@ -2403,6 +2447,7 @@ find_overload_match (struct value **args, int nargs, const char *name, enum oload_search_type method, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, + struct ext_fn_descriptor **ext_fn, int *staticp, const int no_adl) { struct value *obj = (objp ? *objp : NULL); @@ -2410,16 +2455,24 @@ find_overload_match (struct value **args, int nargs, /* Index of best overloaded function. */ int func_oload_champ = -1; int method_oload_champ = -1; + int src_method_oload_champ = -1; + int src_method_oload_champ_bkp = -1; + int ext_method_oload_champ = -1; + int src_and_ext_equal = 0; /* The measure for the current best match. */ struct badness_vector *method_badness = NULL; struct badness_vector *func_badness = NULL; + struct badness_vector *ext_method_badness = NULL; + struct badness_vector *src_method_badness = NULL; struct value *temp = obj; /* For methods, the list of overloaded methods. */ struct fn_field *fns_ptr = NULL; /* For non-methods, the list of overloaded function symbols. */ struct symbol **oload_syms = NULL; + /* For extension functions, the VEC of extension function descriptors. */ + VEC (ext_fn_descriptor_p) *ext_fn_vec = NULL; /* Number of overloaded instances being considered. */ int num_fns = 0; struct type *basetype = NULL; @@ -2431,6 +2484,8 @@ find_overload_match (struct value **args, int nargs, const char *func_name = NULL; enum oload_classification match_quality; enum oload_classification method_match_quality = INCOMPATIBLE; + enum oload_classification src_method_match_quality = INCOMPATIBLE; + enum oload_classification ext_method_match_quality = INCOMPATIBLE; enum oload_classification func_match_quality = INCOMPATIBLE; /* Get the list of overloaded methods or functions. */ @@ -2459,12 +2514,12 @@ find_overload_match (struct value **args, int nargs, } /* Retrieve the list of methods with the name NAME. */ - fns_ptr = value_find_oload_method_list (&temp, name, - 0, &num_fns, - &basetype, &boffset); + value_find_oload_method_list (&temp, name, 0, &fns_ptr, &num_fns, + ext_fn ? &ext_fn_vec : NULL, + &basetype, &boffset); /* If this is a method only search, and no methods were found the search has faild. */ - if (method == METHOD && (!fns_ptr || !num_fns)) + if (method == METHOD && (!fns_ptr || !num_fns) && !ext_fn_vec) error (_("Couldn't find method %s%s%s"), obj_type_name, (obj_type_name && *obj_type_name) ? "::" : "", @@ -2475,18 +2530,82 @@ find_overload_match (struct value **args, int nargs, if (fns_ptr) { gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL); - method_oload_champ = find_oload_champ (args, nargs, method, - num_fns, fns_ptr, - oload_syms, &method_badness); - - method_match_quality = - classify_oload_match (method_badness, nargs, - oload_method_static (method, fns_ptr, - method_oload_champ)); - - make_cleanup (xfree, method_badness); + src_method_oload_champ = find_oload_champ (args, nargs, method, + num_fns, fns_ptr, NULL, + oload_syms, + &src_method_badness); + + src_method_match_quality = + classify_oload_match ( + src_method_badness, nargs, + oload_method_static (method, fns_ptr, + src_method_oload_champ)); + + make_cleanup (xfree, src_method_badness); } + if (ext_fn && VEC_length (ext_fn_descriptor_p, ext_fn_vec)) + { + ext_method_oload_champ = find_oload_champ (args, nargs, method, + 0, NULL, ext_fn_vec, + NULL, &ext_method_badness); + ext_method_match_quality = classify_oload_match (ext_method_badness, + nargs, 0); + make_cleanup (xfree, ext_method_badness); + make_ext_fn_vec_cleanup (ext_fn_vec); + } + + if (src_method_oload_champ >= 0 && ext_method_oload_champ >= 0) + { + switch (compare_badness (ext_method_badness, src_method_badness)) + { + case 0: /* Src method and ext method are equally good. */ + src_and_ext_equal = 1; + case 1: /* Src method and ext method are incompatible */ + /* if ext method match is not standard, then let source method + win. */ + if (ext_method_match_quality != STANDARD) + { + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + ext_method_oload_champ = -1; + method_match_quality = src_method_match_quality; + break; + } + case 2: /* Ext method is champion. */ + method_oload_champ = ext_method_oload_champ; + method_badness = ext_method_badness; + src_method_oload_champ_bkp = src_method_oload_champ; + src_method_oload_champ = -1; + method_match_quality = ext_method_match_quality; + break; + case 3: /* Src method is champion. */ + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + ext_method_oload_champ = -1; + method_match_quality = src_method_match_quality; + break; + default: + error (_("Internal error: unexpected overload comparison " + "result")); + break; + } + } + else + { + if (src_method_oload_champ >= 0) + { + method_oload_champ = src_method_oload_champ; + method_badness = src_method_badness; + method_match_quality = src_method_match_quality; + } + if (ext_method_oload_champ >= 0) + { + method_oload_champ = ext_method_oload_champ; + method_badness = ext_method_badness; + method_match_quality = ext_method_match_quality; + } + } } if (method == NON_METHOD || method == BOTH) @@ -2629,21 +2748,6 @@ find_overload_match (struct value **args, int nargs, func_name); } - if (staticp != NULL) - *staticp = oload_method_static (method, fns_ptr, method_oload_champ); - - if (method_oload_champ >= 0) - { - if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) - *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ, - basetype, boffset); - else - *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, - basetype, boffset); - } - else - *symp = oload_syms[func_oload_champ]; - if (objp) { struct type *temp_type = check_typedef (value_type (temp)); @@ -2653,11 +2757,66 @@ find_overload_match (struct value **args, int nargs, && (TYPE_CODE (objtype) == TYPE_CODE_PTR || TYPE_CODE (objtype) == TYPE_CODE_REF)) { - temp = value_addr (temp); + *objp = value_addr (temp); } - *objp = temp; + else + *objp = temp; } + if (staticp != NULL) + *staticp = oload_method_static (method, fns_ptr, method_oload_champ); + + if (method_oload_champ >= 0) + { + if (src_method_oload_champ >= 0) + { + if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ)) + { + struct type *dtype; + + dtype = value_has_indirect_dynamic_type (args[0]); + if (dtype) + { + args[0] = value_cast (dtype, args[0]); + do_cleanups (all_cleanups); + return find_overload_match (args, nargs, name, method, + objp, fsym, valp, symp, ext_fn, + staticp, no_adl); + } + else + *valp = value_virtual_fn_field (&temp, fns_ptr, + method_oload_champ, + basetype, boffset); + } + else + *valp = value_fn_field (&temp, fns_ptr, method_oload_champ, + basetype, boffset); + } + else + { + if (src_and_ext_equal + && TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, src_method_oload_champ_bkp)) + { + struct type *dtype; + + dtype = value_has_indirect_dynamic_type (args[0]); + if (dtype) + { + args[0] = value_cast (dtype, args[0]); + do_cleanups (all_cleanups); + return find_overload_match (args, nargs, name, method, + objp, fsym, valp, symp, ext_fn, + staticp, no_adl); + } + } + + *ext_fn = ext_fn_clone (VEC_index (ext_fn_descriptor_p, ext_fn_vec, + ext_method_oload_champ)); + } + } + else + *symp = oload_syms[func_oload_champ]; + do_cleanups (all_cleanups); switch (match_quality) @@ -2791,7 +2950,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs, ++num_fns; new_oload_champ = find_oload_champ (args, nargs, 0, num_fns, - NULL, new_oload_syms, + NULL, NULL, new_oload_syms, &new_oload_champ_bv); /* Case 1: We found a good match. Free earlier matches (if any), @@ -2839,10 +2998,12 @@ find_oload_champ_namespace_loop (struct value **args, int nargs, static int find_oload_champ (struct value **args, int nargs, int method, int num_fns, struct fn_field *fns_ptr, + VEC (ext_fn_descriptor_p) *ext_fn_vec, struct symbol **oload_syms, struct badness_vector **oload_champ_bv) { int ix; + int ext_fn_vec_n = 0; /* A measure of how good an overloaded instance is. */ struct badness_vector *bv; /* Index of best overloaded function. */ @@ -2852,18 +3013,27 @@ find_oload_champ (struct value **args, int nargs, int method, /* 0 => no ambiguity, 1 => two good funcs, 2 => incomparable funcs. */ *oload_champ_bv = NULL; + ext_fn_vec_n = ext_fn_vec ? VEC_length (ext_fn_descriptor_p, ext_fn_vec) : 0; /* Consider each candidate in turn. */ - for (ix = 0; ix < num_fns; ix++) + for (ix = 0; (ix < num_fns) || (ix < ext_fn_vec_n); ix++) { int jj; - int static_offset = oload_method_static (method, fns_ptr, ix); + int static_offset = 0; int nparms; struct type **parm_types; + struct ext_fn_descriptor *ext_fn = NULL; + + if (ext_fn_vec) + ext_fn = VEC_index (ext_fn_descriptor_p, ext_fn_vec, ix); if (method) { - nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + if (fns_ptr) + { + static_offset = oload_method_static (method, fns_ptr, ix); + nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix)); + } } else { @@ -2872,13 +3042,20 @@ find_oload_champ (struct value **args, int nargs, int method, } /* Prepare array of parameter types. */ - parm_types = (struct type **) - xmalloc (nparms * (sizeof (struct type *))); - for (jj = 0; jj < nparms; jj++) - parm_types[jj] = (method - ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) - : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), - jj)); + if (fns_ptr || oload_syms) + { + parm_types = (struct type **) + xmalloc (nparms * (sizeof (struct type *))); + for (jj = 0; jj < nparms; jj++) + parm_types[jj] = (method + ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type) + : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]), + jj)); + } + else + { + parm_types = ext_fn_get_argtypes (ext_fn, &nparms); + } /* Compare parameter types to supplied argument types. Skip THIS for static methods. */ diff --git a/gdb/value.h b/gdb/value.h index db964e3..3216249 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -31,6 +31,7 @@ struct type; struct ui_file; struct language_defn; struct value_print_options; +struct ext_fn_descriptor; /* The structure which defines the type of a value. It should never be possible for a program lval value to survive over a call to the @@ -671,6 +672,7 @@ extern int find_overload_match (struct value **args, int nargs, enum oload_search_type method, struct value **objp, struct symbol *fsym, struct value **valp, struct symbol **symp, + struct ext_fn_descriptor **ext_fn, int *staticp, const int no_adl); extern struct value *value_field (struct value *arg1, int fieldno); ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-12 2:56 ` Siva Chandra @ 2013-11-15 22:28 ` Tom Tromey 2013-11-16 0:05 ` Siva Chandra 0 siblings, 1 reply; 18+ messages in thread From: Tom Tromey @ 2013-11-15 22:28 UTC (permalink / raw) To: Siva Chandra; +Cc: gdb-patches >>>>> "Siva" == Siva Chandra <sivachandra@google.com> writes: >> It's fine for the functions themselves, since we'd like to keep the API >> to the Python layer reasonably thin. But for get_matching_ext_methods, >> e.g., it is simple to follow the existing pattern: declare a function in >> python.h and provide both with- and without-Python implementations. Siva> I am not sure I understand what is being said here. Could you kindly Siva> elaborate (pointing to an existing example)? I think it's irrelevant now. >> Silently ignoring errors doesn't seem right. >> There are a few instances of this. >> >> I'm not really sure about the error-handling strategy in this function. Siva> They way I have looked at debug methods hook is as follows: Siva> "If there exists a Python implementation or replacement which matches Siva> better or is as good a match as the source implementation, then use Siva> the Python implementation. If there is any error looking up Python Siva> implementations, report the error but do not stop from executing an Siva> operation; proceed to use the source implementation." Siva> With that view, in the latest patch, I have modified to print the Siva> errors instead of silently ignoring them. Let me know if this does not Siva> seem to be a good strategy. I think that's fine. Swallowing exceptions is unfriendly for folks writing the Python code. Ignoring them can be ok as long as they are printed at the point at which they are dropped. Then one can at least see the stack trace and fix the bug. Siva> +optimized out. Lastly, one could define a debug method in @value{GDBN} Siva> +Python which does not have an equivalent in the source language! Very interesting. I was going to ask about that. I'd like to see it done in the tests... I think I'm ok with the overall approach. I didn't read every single bit in detail. So I didn't go over the error-checking and reference counting bits the way I normally would. I can do that if you want; but meanwhile I think if you wrote the tests... Also I was curious if this supports operator overloading. Like can I define an "operator+"? Siva> + if (ext_fnp) Siva> + { Siva> + if (ext_fn_is_method (ext_fnp)) Siva> + { Siva> + struct value *ret_val; Siva> + Siva> + ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2, Siva> + nargs - 1); Siva> + if (ret_val == NULL) Siva> + error (_("Error invoking debug method for method %s."), Siva> + tstr); Siva> + Siva> + return ret_val; Siva> + } Siva> + } What happens here if "ext_fnp && !ext_fn_is_method"? It seems like a check is needed. Siva> +struct py_ext_object Siva> +{ Siva> + /* Holds an instance of the DebugMethod class. */ Siva> + PyObject *object; Siva> + Siva> + /* Holds the type of the 'this' object. */ Siva> + PyObject *match_py_obj_type; Siva> + Siva> + /* Holds the matching method name. */ Siva> + const char *match_method; How "match_method" is allocated and how its lifetime is managed is really unclear to me. Tom ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-15 22:28 ` Tom Tromey @ 2013-11-16 0:05 ` Siva Chandra 2013-11-16 0:54 ` Doug Evans 0 siblings, 1 reply; 18+ messages in thread From: Siva Chandra @ 2013-11-16 0:05 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Thanks a lot for reviewing. On Fri, Nov 15, 2013 at 1:59 PM, Tom Tromey <tromey@redhat.com> wrote: > I can do that if you want; but meanwhile I think if you wrote the > tests... Yes. Now that you are OK with the general approach, I am going to write the tests and elaborate the docs. Will send out patches for these early next week. > Also I was curious if this supports operator overloading. Like can I > define an "operator+"? Yes. Operator overloading is handled the same way as methods. The operator functions have to be methods though. > Siva> + if (ext_fnp) > Siva> + { > Siva> + if (ext_fn_is_method (ext_fnp)) > Siva> + { > Siva> + struct value *ret_val; > Siva> + > Siva> + ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2, > Siva> + nargs - 1); > Siva> + if (ret_val == NULL) > Siva> + error (_("Error invoking debug method for method %s."), > Siva> + tstr); > Siva> + > Siva> + return ret_val; > Siva> + } > Siva> + } > > What happens here if "ext_fnp && !ext_fn_is_method"? > Siva> +struct py_ext_object > Siva> +{ > Siva> + /* Holds an instance of the DebugMethod class. */ > Siva> + PyObject *object; > Siva> + > Siva> + /* Holds the type of the 'this' object. */ > Siva> + PyObject *match_py_obj_type; > Siva> + > Siva> + /* Holds the matching method name. */ > Siva> + const char *match_method; > > How "match_method" is allocated and how its lifetime is managed is > really unclear to me. I will address the above two question in the next round of review. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-16 0:05 ` Siva Chandra @ 2013-11-16 0:54 ` Doug Evans 2013-11-16 1:03 ` Siva Chandra 0 siblings, 1 reply; 18+ messages in thread From: Doug Evans @ 2013-11-16 0:54 UTC (permalink / raw) To: Siva Chandra; +Cc: Tom Tromey, gdb-patches On Fri, Nov 15, 2013 at 2:27 PM, Siva Chandra <sivachandra@google.com> wrote: > Thanks a lot for reviewing. > > On Fri, Nov 15, 2013 at 1:59 PM, Tom Tromey <tromey@redhat.com> wrote: > >> I can do that if you want; but meanwhile I think if you wrote the >> tests... > > Yes. Now that you are OK with the general approach, I am going to > write the tests and elaborate the docs. Will send out patches for > these early next week. > >> Also I was curious if this supports operator overloading. Like can I >> define an "operator+"? > > Yes. Operator overloading is handled the same way as methods. The > operator functions have to be methods though. > >> Siva> + if (ext_fnp) >> Siva> + { >> Siva> + if (ext_fn_is_method (ext_fnp)) >> Siva> + { >> Siva> + struct value *ret_val; >> Siva> + >> Siva> + ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2, >> Siva> + nargs - 1); >> Siva> + if (ret_val == NULL) >> Siva> + error (_("Error invoking debug method for method %s."), >> Siva> + tstr); >> Siva> + >> Siva> + return ret_val; >> Siva> + } >> Siva> + } >> >> What happens here if "ext_fnp && !ext_fn_is_method"? > >> Siva> +struct py_ext_object >> Siva> +{ >> Siva> + /* Holds an instance of the DebugMethod class. */ >> Siva> + PyObject *object; >> Siva> + >> Siva> + /* Holds the type of the 'this' object. */ >> Siva> + PyObject *match_py_obj_type; >> Siva> + >> Siva> + /* Holds the matching method name. */ >> Siva> + const char *match_method; >> >> How "match_method" is allocated and how its lifetime is managed is >> really unclear to me. > > I will address the above two question in the next round of review. Hi. It's great that you're getting back at this. I have a few thoughts, nothing burdensome I hope, in random order It would be good to provide enable/disable/info support akin the the python pretty-printers. The original pretty-printers used regexps for matching but that was IIUC found to be excessively general. We might want to avoid them in the basic versions of debug methods. I could be wrong but it seemed like errors were handled differently than in the pretty-printers. The inconsistency doesn't feel warranted. IIRC the "ext_lang" stuff was going to be deleted, right? I wonder if other users are going to constantly trip on the name "debug methods" like I do. I don't have a better name at the moment, but maybe something to think about. What are debug method groups for? One thought I had, and this is mostly for discussion's sake, is why stop at providing support for user-defined methods (incl. operators)? Why not allow anything that might be "hand called" to be implemented in Python? [One way of implementing user-defined methods/operators was to translate, e.g. my_stl_vector[3], into a "pseudo- hand call", and then call into Python at the point where we would have hand-called the inferior instead.] TIA [and thanks again for getting back to this!] ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-16 0:54 ` Doug Evans @ 2013-11-16 1:03 ` Siva Chandra 2013-11-16 2:48 ` Siva Chandra 2013-11-19 23:52 ` Doug Evans 0 siblings, 2 replies; 18+ messages in thread From: Siva Chandra @ 2013-11-16 1:03 UTC (permalink / raw) To: Doug Evans; +Cc: Tom Tromey, gdb-patches On Fri, Nov 15, 2013 at 4:05 PM, Doug Evans <dje@google.com> wrote: > It would be good to provide enable/disable/info support akin the the > python pretty-printers. Yes. I am planning to bring them in the next version of the patches. > The original pretty-printers used regexps for matching but that was > IIUC found to be excessively general. > We might want to avoid them in the basic versions of debug methods. Do you have an alternate approach in mind? > I could be wrong but it seemed like errors were handled differently > than in the pretty-printers. > The inconsistency doesn't feel warranted. Yes, there is a difference. > IIRC the "ext_lang" stuff was going to be deleted, right? I am not sure. Tom had a comment long time back on this, but his latest review said that his comments on this might be irrelevant now. I have renamed some of the pieces related to this in my last patch. Do you have any specific comments? > What are debug method groups for? They are for disabling and enabling a group of debug methods. For example, they could be used for debugging the debug methods themselves or writing tests for them: You can disable a group at once instead of disabling individually. > One thought I had, and this is mostly for discussion's sake, > is why stop at providing support for user-defined methods (incl. operators)? > Why not allow anything that might be "hand called" to be implemented in Python? I think that could be a fairly straightforward extension. Do you want it to be done together with this work? > [One way of implementing user-defined methods/operators was to > translate, e.g. my_stl_vector[3], into a "pseudo- hand call", > and then call into Python at the point where we would have hand-called > the inferior instead.] IIRC, you had suggested similar ideas earlier as well. However, I have not gone that route because I thought debug methods/functions should go through the method/function matching infrastructure. Am I missing something here? ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-16 1:03 ` Siva Chandra @ 2013-11-16 2:48 ` Siva Chandra 2013-11-20 0:03 ` Doug Evans 2013-11-19 23:52 ` Doug Evans 1 sibling, 1 reply; 18+ messages in thread From: Siva Chandra @ 2013-11-16 2:48 UTC (permalink / raw) To: Doug Evans; +Cc: Tom Tromey, gdb-patches On Fri, Nov 15, 2013 at 4:53 PM, Siva Chandra <sivachandra@google.com> wrote: > On Fri, Nov 15, 2013 at 4:05 PM, Doug Evans <dje@google.com> wrote: >> I could be wrong but it seemed like errors were handled differently >> than in the pretty-printers. >> The inconsistency doesn't feel warranted. > > Yes, there is a difference. Sorry, sent the last mail without completing my response here. So, yes there is a difference. But as I explained, I did it that way because, in my opinion we should not stop as soon as we hit an error when looking up for a match from among the Python implementations as there could be a source language implementation. If there were to be an error invoking a Python implementation, then that is reported via the relevant functions in valarith.c. All other Python errors are reported but do not stop GDB from proceeding to evaluate expressions. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-16 2:48 ` Siva Chandra @ 2013-11-20 0:03 ` Doug Evans 0 siblings, 0 replies; 18+ messages in thread From: Doug Evans @ 2013-11-20 0:03 UTC (permalink / raw) To: Siva Chandra; +Cc: Tom Tromey, gdb-patches On Fri, Nov 15, 2013 at 5:03 PM, Siva Chandra <sivachandra@google.com> wrote: > On Fri, Nov 15, 2013 at 4:53 PM, Siva Chandra <sivachandra@google.com> wrote: >> On Fri, Nov 15, 2013 at 4:05 PM, Doug Evans <dje@google.com> wrote: >>> I could be wrong but it seemed like errors were handled differently >>> than in the pretty-printers. >>> The inconsistency doesn't feel warranted. >> >> Yes, there is a difference. > > Sorry, sent the last mail without completing my response here. > > So, yes there is a difference. But as I explained, I did it that way > because, in my opinion we should not stop as soon as we hit an error > when looking up for a match from among the Python implementations as > there could be a source language implementation. If there were to be > an error invoking a Python implementation, then that is reported via > the relevant functions in valarith.c. All other Python errors are > reported but do not stop GDB from proceeding to evaluate expressions. Ah. I can imagine scenarios where I would not want gdb to continue anyway, though I'm not sure they're important enough to worry about now. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-16 1:03 ` Siva Chandra 2013-11-16 2:48 ` Siva Chandra @ 2013-11-19 23:52 ` Doug Evans 2013-11-20 0:39 ` Siva Chandra 2013-11-20 2:48 ` Doug Evans 1 sibling, 2 replies; 18+ messages in thread From: Doug Evans @ 2013-11-19 23:52 UTC (permalink / raw) To: Siva Chandra; +Cc: Tom Tromey, gdb-patches On Fri, Nov 15, 2013 at 4:53 PM, Siva Chandra <sivachandra@google.com> wrote: > On Fri, Nov 15, 2013 at 4:05 PM, Doug Evans <dje@google.com> wrote: >> It would be good to provide enable/disable/info support akin the the >> python pretty-printers. > > Yes. I am planning to bring them in the next version of the patches. > >> The original pretty-printers used regexps for matching but that was >> IIUC found to be excessively general. >> We might want to avoid them in the basic versions of debug methods. > > Do you have an alternate approach in mind? For matching the class I would look into how the current version of the libstdc++ pretty-printers do this. http://gcc.gnu.org/viewcvs/gcc/trunk/libstdc%2B%2B-v3/python/libstdcxx/v6/printers.py I don't know if that's the best way to handle debug methods, but doing something different requires a compelling reason. For matching on the method, I would just use a string comparison. Again, this is for the simple version. IIUC the API allows for more complex mechanisms, but for the start I'd say start small with something simple. [I can imagine an issue arising with operators, e.g., "operator ()" vs "operator()" or some such. Is handling that with a regexp the best way to go? Dunno.] >> I could be wrong but it seemed like errors were handled differently >> than in the pretty-printers. >> The inconsistency doesn't feel warranted. > > Yes, there is a difference. > >> IIRC the "ext_lang" stuff was going to be deleted, right? > > I am not sure. Tom had a comment long time back on this, but his > latest review said that his comments on this might be irrelevant now. > I have renamed some of the pieces related to this in my last patch. Do > you have any specific comments? The name is a tad confusing, though I'm warming up to it. The high order bit for me is you're really just abstracting away a subset of a much bigger problem space: The abstraction of all calls from gdb-proper to the extension language. >> What are debug method groups for? > > They are for disabling and enabling a group of debug methods. For > example, they could be used for debugging the debug methods themselves > or writing tests for them: You can disable a group at once instead of > disabling individually. Ah. The python pretty-printers support this differently. Again, I think there needs to be a compelling reason to do it differently. For example, note how grouping is handled there. IWBN if the user commands for enable/disable/info were as consistent as possible between the two. >> One thought I had, and this is mostly for discussion's sake, >> is why stop at providing support for user-defined methods (incl. operators)? >> Why not allow anything that might be "hand called" to be implemented in Python? > > I think that could be a fairly straightforward extension. Do you want > it to be done together with this work? Naw, I don't want to delay this work by adding more features. OTOH, I do think there is value to at least thinking about the future and not making extending what's there now harder than it otherwise could have been (to the extent that one can reasonably reason about such things without delaying the implementation forever). >> [One way of implementing user-defined methods/operators was to >> translate, e.g. my_stl_vector[3], into a "pseudo- hand call", >> and then call into Python at the point where we would have hand-called >> the inferior instead.] > > IIRC, you had suggested similar ideas earlier as well. However, I have > not gone that route because I thought debug methods/functions should > go through the method/function matching infrastructure. Am I missing > something here? My thought was that they *would* go through the method/function matching infrastructure, and out of that would be an appropriately crafted "hand call". It's not critical though. --- One thing I noticed in the patch is an assumption of an initial "this" pointer. While it doesn't have to be implemented today, I think we should at least know *how* it will be handled in the API, and that is, e.g., static methods where there is no "this". APIs are harder to change than implementations. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-19 23:52 ` Doug Evans @ 2013-11-20 0:39 ` Siva Chandra 2013-11-20 2:48 ` Doug Evans 1 sibling, 0 replies; 18+ messages in thread From: Siva Chandra @ 2013-11-20 0:39 UTC (permalink / raw) To: Doug Evans; +Cc: Tom Tromey, gdb-patches I am working the new patch sets that would address most of your comments. For one or two, I will probably ask more questions when I post the patches. But I want to respond to one comment now. On Tue, Nov 19, 2013 at 3:41 PM, Doug Evans <dje@google.com> wrote: > For matching on the method, I would just use a string comparison. > Again, this is for the simple version. IIUC the API allows for more > complex mechanisms, but for the start I'd say start small with > something simple. > [I can imagine an issue arising with operators, e.g., "operator ()" vs > "operator()" or some such. Is handling that with a regexp the best > way to go? Dunno.] For things "operator ()" vs "operator()", I think GDB handles that before looking up the method. But otherwise, a simple name match can have problems with template methods as the template argument could be anything. I am working on setting up tests which illustrate debug methods for template classes with template methods. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [RFC] Debug Methods in GDB Python 2013-11-19 23:52 ` Doug Evans 2013-11-20 0:39 ` Siva Chandra @ 2013-11-20 2:48 ` Doug Evans 1 sibling, 0 replies; 18+ messages in thread From: Doug Evans @ 2013-11-20 2:48 UTC (permalink / raw) To: Siva Chandra; +Cc: Tom Tromey, gdb-patches On Tue, Nov 19, 2013 at 3:41 PM, Doug Evans <dje@google.com> wrote: >>> IIRC the "ext_lang" stuff was going to be deleted, right? >> >> I am not sure. Tom had a comment long time back on this, but his >> latest review said that his comments on this might be irrelevant now. >> I have renamed some of the pieces related to this in my last patch. Do >> you have any specific comments? > > The name is a tad confusing, though I'm warming up to it. > The high order bit for me is you're really just abstracting away a > subset of a much bigger problem space: The abstraction of all calls > from gdb-proper to the extension language. For reference sake, I have a patch that addresses the general problem. I'll be submitting it after a couple more cleanups are committed. ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2013-11-20 2:42 UTC | newest] Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2013-01-07 21:22 [RFC] Debug Methods in GDB Python Siva Chandra 2013-01-29 1:51 ` Siva Chandra 2013-02-25 23:02 ` Siva Chandra 2013-05-10 19:33 ` Tom Tromey 2013-05-10 19:55 ` Siva Chandra 2013-05-14 19:33 ` Tom Tromey 2013-06-17 19:10 ` Siva Chandra 2013-07-22 20:47 ` Tom Tromey 2013-11-12 2:56 ` Siva Chandra 2013-11-15 22:28 ` Tom Tromey 2013-11-16 0:05 ` Siva Chandra 2013-11-16 0:54 ` Doug Evans 2013-11-16 1:03 ` Siva Chandra 2013-11-16 2:48 ` Siva Chandra 2013-11-20 0:03 ` Doug Evans 2013-11-19 23:52 ` Doug Evans 2013-11-20 0:39 ` Siva Chandra 2013-11-20 2:48 ` Doug Evans
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox