[Patch v8] Debug methods using GDB Python
Siva Chandra
sivachandra@google.com
Tue Jan 28 11:19:00 GMT 2014
Hi,
Attached is the latest version of the patch which adds the debug
methods feature to the GDB Python API. Changes from v7:
1. I have cleaned up all formatting issues that I could identify.
Given that the patch is fairly large, I will not be surprised if I
missed some.
2. Fixed a memory leak.
I still do not have doc changes included in the patch. Would be good
to get feedback on the code changes while I am working on the doc
changes.
v7 posting for reference:
https://sourceware.org/ml/gdb-patches/2014-01/msg00882.html
2014-01-28 Siva Chandra Reddy <sivachandra@google.com>
* Makefile.in: Add entries for new files.
* data-directory/Makefile.in: Add entries for new Python files.
* eval.c (evaluate_subexp_standard): Lookup and invoke methods
defined in extension languages.
* valarith.c (value_x_binop, value_x_unop): Lookup and invoke
overloaded operator methods defined in extension languages.
* valops.c (find_oload_method_list, find_method_list,
find_overload_match, find_oload_champ): Lookup methods defined
in extension languages.
(value_has_indirect_dynamic_type): New function to determine
the indirect dynamic type of a value.
* value.h (find_overload_match): Update signature.
* ext-function.c: New file.
* ext-function.h: New file.
* python/py-debugmethods.c: New file.
* python/py-objfile.c (objfile_object): New field
'debugmethod_matchers'.
(objfpy_dealloc): XDECREF on the new debugmethod_matcherss field.
(objfpy_new, objfile_to_objfile_object): Initialize
debugmethod_macthers field.
(objfpy_get_debugmethod_matchers): New function.
(objfile_getset): New entry 'debugmethod_matchers'.
* python/py-progspace.c (pspace_object): New field
'debugmethod_matchers'.
(pspy_dealloc): XDECREF on the new debugmethod_matchers field.
(pspy_new, pspace_to_pspace_object): Initialize
debugmethod_matchers field.
(pspy_get_debugmethod_matchers): New function.
(pspace_getset): New entry 'debug_methods'.
* python/python-internal.h: Add declarations for new functions.
* python/python.c (_initialize_python): Invoke
gdbpy_initialize_debugmethods.
* python/python.h: Add declarations of new functions.
* python/lib/gdb/__init__.py (debug_methods): New attribute.
* python/lib/gdb/debugmethods.py: New file.
* python/lib/gdb/command/debugmethods.py: New file.
testuite/
* gdb.python/py-debugmethods.cc: New testcase to test debug
methods.
* gdb.python/py-debugmethods.exp: New tests to test debug
methods.
* gdb.python/py-debugmethods.py: Python script supporting the
new testcase and tests.
-------------- next part --------------
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ed84e35..9006dfa 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -292,6 +292,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 \
@@ -328,6 +329,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 \
@@ -739,7 +741,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 \
@@ -841,7 +843,7 @@ rs6000-tdep.h rs6000-aix-tdep.h \
common/gdb_locale.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 \
@@ -964,7 +966,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
@@ -2216,6 +2219,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 29a48e4..972854a 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -62,8 +62,10 @@ PYTHON_FILES = \
gdb/types.py \
gdb/printing.py \
gdb/prompt.py \
+ gdb/debugmethods.py \
gdb/command/bound_registers.py \
gdb/command/__init__.py \
+ gdb/command/debugmethods.py \
gdb/command/frame_filters.py \
gdb/command/type_printers.py \
gdb/command/pretty_printers.py \
diff --git a/gdb/eval.c b/gdb/eval.c
index 949f76c..b91ffa0 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,11 +1593,11 @@ evaluate_subexp_standard (struct type *expect_type,
func_name = (char *) alloca (name_len + 1);
strcpy (func_name, &exp->elts[string_pc + 1].string);
- find_overload_match (&argvec[1], nargs, func_name,
- NON_METHOD, /* not method */
- NULL, NULL, /* pass NULL symbol since
+ find_overload_match (&argvec[1], nargs, func_name,
+ 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 */
+ 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,38 @@ 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 a method implemented in an extension language is the best
+ match, then invoke it. */
+ 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);
+
+ xfree (ext_fnp);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
+ }
+
+ argvec[1] = arg2; /* the ``this'' pointer */
+ argvec[0] = valp; /* Use the method found after overload
+ resolution. */
}
else
/* Non-C++ case -- or no overload resolution. */
@@ -1699,9 +1730,9 @@ evaluate_subexp_standard (struct type *expect_type,
(void) find_overload_match (&argvec[1], nargs,
NULL, /* no need for name */
- NON_METHOD, /* not method */
- NULL, function, /* the function */
- NULL, &symp, NULL, no_adl);
+ NON_METHOD, /* not method */
+ NULL, function, /* the function */
+ 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..2db5b4d
--- /dev/null
+++ b/gdb/ext-function.c
@@ -0,0 +1,174 @@
+/* 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);
+ }
+
+ VEC_free (ext_fn_descriptor_p, v);
+}
+
+/* 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..e8584ab
--- /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 95a76c2..76e35dc 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.
+debugmethod_matchers = []
# Initial frame filters.
frame_filters = {}
diff --git a/gdb/python/lib/gdb/command/debugmethods.py b/gdb/python/lib/gdb/command/debugmethods.py
new file mode 100644
index 0000000..cac5c09
--- /dev/null
+++ b/gdb/python/lib/gdb/command/debugmethods.py
@@ -0,0 +1,261 @@
+# Debug method commands.
+# Copyright 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/>.
+
+import gdb
+import re
+
+"""GDB commands for working with debug-methods."""
+
+
+def validate_dm_regexp(part_name, regexp):
+ try:
+ return re.compile(regexp)
+ except SyntaxError:
+ raise SyntaxError("Invalid %s regexp: %s", part_name, regexp)
+
+
+def parse_dm_command_args(arg):
+ """Parses the arguments passed to a debug method command.
+
+ Arguments:
+ arg: The argument string passed to a debug method command.
+
+ Returns:
+ A 2-tuple: (<locus matching regular expression>,
+ <name matching regular expression>)
+ """
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+ raise SyntaxError("Too many arguments to command.")
+ locus_regexp = ""
+ matcher_name_regexp = ""
+ dm_name_regexp = None
+ if argc >= 1:
+ locus_regexp = argv[0]
+ if argc == 2:
+ parts = argv[1].split(";", 1)
+ matcher_name_regexp = parts[0]
+ if len(parts) > 1:
+ dm_name_regexp = parts[1]
+ if dm_name_regexp:
+ name_re = validate_dm_regexp("debugmethod name", dm_name_regexp)
+ else:
+ name_re = None
+ return (validate_dm_regexp("locus", locus_regexp),
+ validate_dm_regexp("matcher name", matcher_name_regexp),
+ name_re)
+
+
+def get_global_method_matchers(locus_re, matcher_re):
+ """Returns a dict of matching globally registered debug methods.
+
+ Arguments:
+ locus_re: Even though only globally registered debug methods are
+ looked up, they will be looked up only if 'global' matches
+ LOCUS_RE.
+ name_re: The regular expression matching the names of debug methods.
+ Returns:
+ A dict of matching globally registered debug methods. The only key in
+ the dict will be 'global'.
+ """
+ locus_str = "registered globally"
+ dm_dict = { locus_str: [] }
+ if locus_re.match("global"):
+ dm_dict[locus_str].extend(
+ [m for m in gdb.debugmethod_matchers if matcher_re.match(m.name)]
+ )
+ return dm_dict
+
+
+def get_method_matchers_in_loci(loci, locus_re, matcher_re):
+ """Returns a dict of macthing registered debug methods in the LOCI.
+
+ Arguments:
+ loci: The list of loci to lookup matching debug methods in.
+ locus_re: Debug methods will be looked up in a particular locus only
+ if its filename matches the regular expression LOCUS_RE.
+ name_re: The regular expression to match the names of debug methods.
+
+ Returns:
+ A dict of matching debug methods. The keys of the dict are the
+ filenames of the loci the debug methods belong to.
+ """
+ dm_dict = {}
+ for locus in loci:
+ if not locus_re.match(locus.filename):
+ continue
+ locus_type = "objfile"
+ if isinstance(locus, gdb.Progspace):
+ locus_type = "progspace"
+ locus_str = "registered with %s %s" % (locus_type, locus.filename)
+ dm_dict[locus_str] = [
+ m for m in locus.debugmethod_matchers if matcher_re.match(m.name)
+ ]
+ return dm_dict
+
+
+def get_status(m):
+ if m.enabled:
+ return "enabled"
+ else:
+ return "disabled"
+
+
+def print_dm_info(dm_dict, name_re):
+ """Print a dictionary of debug methods with an optional header."""
+ if not dm_dict:
+ return
+ for locus_str in dm_dict:
+ print ("Debug methods %s:" % locus_str)
+ for matcher in dm_dict[locus_str]:
+ print (" %s - %s" % (matcher.name, get_status(matcher)))
+ if not matcher.methods:
+ continue
+ for m in matcher.methods:
+ if name_re is None or name_re.match(m.name):
+ print (" %s - %s" % (m.name, get_status(m)))
+
+
+def set_dm_status1(dm_dict, name_re, status):
+ """Set the status (enabled/disabled) of a dictionary of debug methods."""
+ for locus_str, matchers in dm_dict.iteritems():
+ if not matchers:
+ continue
+ for matcher in matchers:
+ if not name_re:
+ matcher.enabled = status
+ continue
+ if not matcher.methods:
+ continue
+ for m in matcher.methods:
+ if name_re.match(m.name):
+ m.enabled = status
+
+
+def set_dm_status(arg, status):
+ """Set the status (enabled/disabled) of debug methods matching ARG.
+ This is a helper function for enable/disable commands. ARG is the
+ argument string passed to the commands.
+ """
+ locus_re, matcher_re, name_re = parse_dm_command_args(arg)
+ set_dm_status1(get_global_method_matchers(locus_re, matcher_re), name_re,
+ status)
+ set_dm_status1(
+ get_method_matchers_in_loci(gdb.progspaces(), locus_re, matcher_re),
+ name_re,
+ status
+ )
+ set_dm_status1(
+ get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re),
+ name_re,
+ status
+ )
+
+
+class InfoDebugMethod(gdb.Command):
+ """GDB command to list registered debug method matchers.
+
+ Usage: info debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ method matcherss. If it is ommitted, all registered debug methods from all
+ loci are listed. A locus could be 'global', a regular expression matching
+ filenames of program spaces, or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug method
+ matchers. If this omitted for a specified locus, then all registered debug
+ methods in the locus are listed. To list only a certain debug methods
+ managed by a single matcher, the name regexp can be specified as
+ matcher-name-regexp;debug-method-name-regexp.
+ """
+
+ def __init__(self):
+ super(InfoDebugMethod, self).__init__("info debugmethod",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ locus_re, matcher_re, name_re = parse_dm_command_args(arg)
+ print_dm_info(get_global_method_matchers(locus_re, matcher_re),
+ name_re)
+ print_dm_info(
+ get_method_matchers_in_loci(
+ gdb.progspaces(), locus_re, matcher_re),
+ name_re
+ )
+ print_dm_info(
+ get_method_matchers_in_loci(gdb.objfiles(), locus_re, matcher_re),
+ name_re
+ )
+
+
+class EnableDebugMethod(gdb.Command):
+ """GDB command to enable a specified (group of) debug method(s).
+
+ Usage: enable debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are enabled. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are enabled.
+ """
+
+ def __init__(self):
+ super(EnableDebugMethod, self).__init__("enable debugmethod",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_dm_status(arg, True)
+
+
+class DisableDebugMethod(gdb.Command):
+ """GDB command to disable a specified (group of) debug method(s).
+
+ Usage: disable debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are disabled. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are disabled.
+ """
+
+ def __init__(self):
+ super(DisableDebugMethod, self).__init__("disable debugmethod",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_dm_status(arg, False)
+
+
+def register_debug_method_commands():
+ """Installs the debug method commands."""
+ InfoDebugMethod()
+ EnableDebugMethod()
+ DisableDebugMethod()
+
+
+register_debug_method_commands()
diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py
new file mode 100644
index 0000000..08088d7
--- /dev/null
+++ b/gdb/python/lib/gdb/debugmethods.py
@@ -0,0 +1,236 @@
+# Python side of the support for debug methods.
+# Copyright (C) 2014 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
+import sys
+
+if sys.version_info[0] > 2:
+ # Python 3 removed basestring and long
+ basestring = str
+ long = int
+
+class DebugMethod(object):
+ """Base class (or a prototype) for debug method description.
+
+ Currently, the description only requires only 'name' and 'enabled'
+ attributes. Description objects are managed by 'DebugMethodMatcher'
+ objects (see below).
+
+ Attributes:
+ name: The name of the debug method.
+ enabled: A boolean indicating if the debug method is enabled.
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.enabled = True
+
+
+class DebugMethodMatcher(object):
+ """Abstract base class for matching a debug method.
+
+ When looking for debug methods, GDB invokes the `match' method of a
+ registered debug method matcher to match the object type and method name.
+ The `match' method in concrete classes derived from this class should
+ return a `DebugMethodWorker' object, or a list of `DebugMethodWorker'
+ objects if there is a match (see below for 'DebugMethodWorker' class).
+
+ Attributes:
+ name: The name of the matcher.
+ enabled: A boolean indicating if the matcher is enabled.
+ debug_methods: A sequence of objects of type 'DebugMethod', or objects
+ which have atleast the attribute of a 'DebugMethod' object.
+ This list is used by the 'enable'/'disable'/'info' commands to
+ enable/disable/list the debug methods registered with GDB. See
+ the 'match' method below to know how this sequence is to be used.
+ """
+
+ def __init__(self, name):
+ """
+ Args:
+ name: An identifying name for the debug method or the group of
+ debug methods returned by the `match' method.
+ """
+ self.name = name
+ self.enabled = True
+ self.methods = None
+
+ def match(self, class_type, method_name):
+ """Match class type and method name.
+
+ In derived classes, it should return a DebugMethodWorker object, or a
+ sequence of 'DebugMethodWorker' objects. Only those debug method
+ workers whose corresponding 'DebugMethod' descriptor object is enabled
+ should be returned.
+
+ Args:
+ class_type: The class type (gdb.Type object) to match.
+ method_name: The name (string) of the method to match.
+ """
+ raise NotImplementedError("DebugMethodMatcher match")
+
+
+class DebugMethodWorker(object):
+ """Base class for all debug method workers defined in Python.
+
+ A debug method worker is an object which matches the method arguments, and
+ invokes the method when GDB wants it to. Internally, GDB first invokes the
+ 'get_argtypes' method to perform overload resolution. If GDB selects to
+ invoke this Python debug method, then it invokes it via the overridden
+ 'invoke' method.
+
+ Derived classes should override the 'get_argtypes' and 'invoke' methods.
+ """
+
+ def get_argtypes(self):
+ """Return a sequence of gdb.Type objects corresponding to the arguments
+ of the debug method.
+ """
+ raise NotImplementedError("DebugMethod 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.
+ """
+ raise NotImplementedError("DebugMethod invoke")
+
+
+class SimpleDebugMethodMatcher(DebugMethodMatcher):
+ """This is a utility class which does name match by class name of the
+ objects on which the debug methods are defined. For simple classes and
+ methods, one can choose to use this class. For complex debug methods,
+ which need to replace/implement template source methods on possibly
+ template classes, one should implement their own debug method classes
+ deriving from the base class 'DebugMethod'. See the py-debugmethods.py
+ in the testsuite/gdb.python directory of the GDB source tree for
+ examples.
+ """
+
+ class SimpleDebugMethodWorker(DebugMethodWorker):
+ def __init__(self, method_function, argtypes):
+ self._argtypes = argtypes
+ self._method_function = method_function
+
+ def get_argtypes(self):
+ return self._argtypes
+
+ def invoke(self, obj, args):
+ return self._method_function(obj, *args)
+
+
+ def __init__(self, name, class_matcher, method_matcher, method_function,
+ *argtypes):
+ """
+ Args:
+ name: Name of the debug method matcher.
+ class_matcher: A regular expression used to match the name of the
+ class whose method this debug method is implementing/replacing.
+ method_matcher: A regular expression used to match the name of the
+ method this debug method is implementing/replacing.
+ method_function: A Python callable which would be called via the
+ invoke them of this class to actually invoke the debug method.
+ This callable should accept the object (*this) as the first
+ argument followed by the rest of the arguments to the method.
+ argtypes: The gdb.Type objects corresponding to the arguments that
+ this debug method takes.
+ """
+ DebugMethodMatcher.__init__(self, name)
+ assert callable(method_function), (
+ "The 'method_function' argument to 'SimpleDebugMethodMatcher' "
+ "__init__ method should be a callable.")
+ 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 SimpleDebugMethodMatcher.SimpleDebugMethodWorker(
+ self._method_function, self._argtypes)
+
+
+# A helper function for register_debugmethod_matcher which returns an error
+# object if MATCHER is not having the requisite attributes in the proper
+# format.
+def validate_debugmethod_matcher(matcher):
+ if not isinstance(matcher, DebugMethodMatcher):
+ return TypeError("Debug method matcher is not an instance of "
+ "'DebugMethodMatcher'")
+ if not hasattr(matcher, "name"):
+ return TypeError("Debug method matcher is missing attribute: name")
+ if not hasattr(matcher, "enabled"):
+ return TypeError("Debug method matcher is missing attribute: enabled")
+ if not isinstance(matcher.name, basestring):
+ return TypeError("Attribute 'name' of debug method matcher is not a "
+ "string")
+ if matcher.name.find(";") >= 0:
+ return ValueError("Debug method matcher name cannot contain ';' in it")
+
+
+# A helper function for register_debugmethod_matcher which looks up a debug
+# method matcher with NAME in LOCUS. Returns the index of the debug method
+# matcher in 'debug_method_matchers' sequence attribute of the LOCUS.
+def lookup_debugmethod_matcher(locus, name):
+ i = 0
+ for m in locus.debugmethod_matchers:
+ if m.name == name:
+ return i
+ i = i + 1
+
+
+def register_debugmethod_matcher(locus, matcher, replace=False):
+ """Registers a debug method matcher MATCHER with a LOCUS.
+
+ Arguments:
+ locus: The locus in which the debug methods should be registered. It
+ can be 'None' to indicate that the debug methods should be
+ registered globally. Or, it could be a gdb.Objfile or a
+ gdb.Progspace object in which the debug methods should be
+ registered.
+ matcher: The debug method matcher to register with the LOCUS. It
+ should be an instance of 'DebugMethodMatcher' class.
+ replace: If True, replace any existing debug method matcher with the
+ same name in the locus. Otherwise, if a matcher with the same name
+ exists in the locus, raise an exception.
+ """
+ err = validate_debugmethod_matcher(matcher)
+ if err:
+ raise err
+ if not locus:
+ locus = gdb
+ if locus == gdb:
+ locus_name = 'GDB globally'
+ else:
+ locus_name = locus.filename
+ index = lookup_debugmethod_matcher(locus, matcher.name)
+ if index:
+ if replace:
+ del locus.debugmethod_matchers[index]
+ else:
+ raise RuntimeError('Debug method matcher already registered with '
+ '%s: %s' % (locus_name, new_method.name))
+ if gdb.parameter("verbose"):
+ gdb.write("Registering debug method matcher '%s' with %s' ...\n")
+ locus.debugmethod_matchers.insert(0, matcher)
diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c
new file mode 100644
index 0000000..da062dc
--- /dev/null
+++ b/gdb/python/py-debugmethods.c
@@ -0,0 +1,638 @@
+/* Support for debug methods in Python.
+
+ Copyright (C) 2014 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"
+
+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 const char matchers_attr_str[] = "debugmethod_matchers";
+
+static PyObject *py_match_method_name = NULL;
+static PyObject *py_get_argtypes_method_name = NULL;
+static PyObject *py_invoke_method_name = NULL;
+
+struct py_ext_object
+{
+ PyObject *worker;
+ PyObject *this_type;
+};
+
+static struct ext_fn_descriptor *new_python_ext_method (PyObject *item,
+ PyObject *py_obj_type);
+
+/* Implementation of free_ext_obj_ftype. */
+
+static void
+py_free_ext_object (void *ext_obj)
+{
+ struct py_ext_object *ext_object = ext_obj;
+
+ Py_XDECREF (ext_object->worker);
+ Py_XDECREF (ext_object->this_type);
+ xfree (ext_object);
+}
+
+/* Implementation of clone_ext_obj_ftype. */
+
+static void *
+py_clone_ext_object (void *ext_obj)
+{
+ struct py_ext_object *ext_object = ext_obj, *new_object;
+
+ new_object = XCNEW (struct py_ext_object);
+ new_object->worker = ext_object->worker;
+ new_object->this_type = ext_object->this_type;
+ Py_XINCREF (new_object->worker);
+ Py_XINCREF (new_object->this_type);
+
+ return new_object;
+}
+
+/* Invoke the "match" method of the MATCHER and return a new reference
+ to the result. Returns NULL on error. */
+
+static PyObject *
+invoke_match_method (PyObject *matcher, PyObject *py_obj_type,
+ const char *debugmethod_name)
+{
+ PyObject *py_debugmethod_name;
+ PyObject *match_method, *enabled_field, *match_result;
+ struct cleanup *cleanups;
+ int enabled;
+
+ if (debugmethod_name == NULL)
+ return NULL;
+
+ cleanups = make_cleanup (null_cleanup, NULL);
+
+ enabled_field = PyObject_GetAttrString (matcher, enabled_field_name);
+ if (enabled_field == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (enabled_field);
+
+ enabled = PyObject_IsTrue (enabled_field);
+ if (enabled == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ if (enabled == 0)
+ {
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ match_method = PyObject_GetAttrString (matcher, match_method_name);
+ if (match_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (match_method);
+ if (!PyCallable_Check (match_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method macther "
+ "is not a callable. Ignored!"), match_method_name);
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ py_debugmethod_name = PyString_FromString (debugmethod_name);
+ if (py_debugmethod_name == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_debugmethod_name);
+
+ match_result = PyObject_CallMethodObjArgs (matcher,
+ py_match_method_name,
+ py_obj_type,
+ py_debugmethod_name,
+ NULL);
+ if (match_result == NULL)
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return match_result;
+}
+
+/* Extend LIST1 with LIST2.
+ Similar to list.extend. Prints the stack and returns NULL on error. */
+
+static PyObject *
+matcher_list_extend (PyObject *list1, PyObject *list2)
+{
+ PyObject *new_list = PySequence_Concat (list1, list2);
+
+ if (new_list == NULL)
+ gdbpy_print_stack ();
+
+ return new_list;
+}
+
+/* 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) *worker_vec = NULL;
+ PyObject *py_type, *py_progspace;
+ PyObject *py_debugmethod_matcher_list = NULL, *list_iter, *matcher;
+
+ if (obj_type == NULL || method_name == NULL)
+ return NULL;
+
+ py_type = type_to_type_object (obj_type);
+ if (py_type == NULL)
+ return NULL;
+ make_cleanup_py_decref (py_type);
+
+ /* Create an empyt list of debug methods. */
+ py_debugmethod_matcher_list = PyList_New (0);
+ if (py_debugmethod_matcher_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_matchers, *temp = py_debugmethod_matcher_list;
+
+ if (py_objfile == NULL)
+ {
+ gdbpy_print_stack ();
+ continue;
+ }
+
+ objfile_matchers = objfpy_get_debugmethod_matchers (py_objfile, NULL);
+ py_debugmethod_matcher_list = matcher_list_extend (temp,
+ objfile_matchers);
+ if (py_debugmethod_matcher_list == NULL)
+ py_debugmethod_matcher_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (objfile_matchers);
+ }
+
+ /* Gather debug methods matchers registered with the current program
+ space. */
+ py_progspace = pspace_to_pspace_object (current_program_space);
+ if (py_progspace != NULL)
+ {
+ PyObject *temp = py_debugmethod_matcher_list;
+ PyObject *pspace_matchers = pspy_get_debugmethod_matchers (py_progspace,
+ NULL);
+
+ py_debugmethod_matcher_list = matcher_list_extend (temp, pspace_matchers);
+ if (py_debugmethod_matcher_list == NULL)
+ py_debugmethod_matcher_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (pspace_matchers);
+ }
+ else
+ gdbpy_print_stack ();
+
+ /* Gather debug methods registered globally. */
+ if (gdb_python_module != NULL
+ && PyObject_HasAttrString (gdb_python_module, matchers_attr_str))
+ {
+ PyObject *gdb_matchers;
+ PyObject *temp = py_debugmethod_matcher_list;
+
+ gdb_matchers = PyObject_GetAttrString (gdb_python_module,
+ matchers_attr_str);
+ if (gdb_matchers != NULL)
+ {
+ py_debugmethod_matcher_list = matcher_list_extend (temp,
+ gdb_matchers);
+ if (py_debugmethod_matcher_list == NULL)
+ py_debugmethod_matcher_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_DECREF (gdb_matchers);
+ }
+ else
+ gdbpy_print_stack ();
+ }
+
+ list_iter = PyObject_GetIter (py_debugmethod_matcher_list);
+ if (list_iter == NULL)
+ {
+ gdbpy_print_stack ();
+ Py_DECREF (py_debugmethod_matcher_list);
+ do_cleanups (cleanups);
+ return NULL;
+ }
+ while ((matcher = PyIter_Next (list_iter)))
+ {
+ PyObject *match_result = invoke_match_method (matcher, py_type,
+ method_name);
+
+ if (match_result == Py_None || match_result == NULL)
+ ;
+ else if (PySequence_Check (match_result))
+ {
+ PyObject *iter = PyObject_GetIter (match_result);
+ PyObject *worker;
+
+ if (iter == NULL)
+ {
+ gdbpy_print_stack ();
+ Py_DECREF (matcher);
+ Py_DECREF (match_result);
+ do_cleanups (cleanups);
+
+ continue;
+ }
+ while ((worker = PyIter_Next (iter)))
+ {
+ struct ext_fn_descriptor *ext_fn;
+
+ ext_fn = new_python_ext_method (worker, py_type);
+ VEC_safe_push (ext_fn_descriptor_p, worker_vec, ext_fn);
+ Py_DECREF (worker);
+ }
+ Py_DECREF (iter);
+ /* Report any error that could have occurred while iterating. */
+ if (PyErr_Occurred ())
+ gdbpy_print_stack ();
+ }
+ else
+ {
+ struct ext_fn_descriptor *ext_fn;
+
+ ext_fn = new_python_ext_method (match_result, py_type);
+ VEC_safe_push (ext_fn_descriptor_p, worker_vec, ext_fn);
+ }
+
+ Py_XDECREF (match_result);
+ Py_DECREF (matcher);
+ }
+ Py_DECREF (list_iter);
+ /* Report any error that could have occurred while iterating. */
+ if (PyErr_Occurred ())
+ gdbpy_print_stack ();
+
+ Py_DECREF (py_debugmethod_matcher_list);
+ do_cleanups (cleanups);
+
+ return worker_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 = ext_obj;
+ PyObject *worker = ext_object->worker;
+ PyObject *get_argtypes_method;
+ PyObject *py_argtype_list, *list_iter = NULL, *item;
+ struct cleanup *cleanups;
+ struct type **type_array, *obj_type;
+ int i = 1, arg_count;
+
+ /* 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 (worker,
+ 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 (worker,
+ py_get_argtypes_method_name,
+ NULL);
+ if (py_argtype_list == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_argtype_list);
+ if (py_argtype_list == Py_None)
+ arg_count = 0;
+ else if (PySequence_Check (py_argtype_list))
+ {
+ arg_count = PySequence_Size (py_argtype_list);
+ if (arg_count == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ 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);
+ }
+ else
+ arg_count = 1;
+
+ /* Include the 'this' argument in the size. */
+ type_array = XCNEWVEC (struct type *, arg_count + 1);
+ i = 1;
+ if (list_iter != NULL)
+ {
+ while ((item = PyIter_Next (list_iter)))
+ {
+ struct type *arg_type = type_object_to_type (item);
+
+ Py_DECREF (item);
+ if (arg_type == NULL)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("Arg type returned by the get_argtypes method "
+ "of a debug method worker object is not a "
+ "gdb.Type object."));
+ break;
+ }
+
+ type_array[i] = arg_type;
+ i++;
+ }
+ }
+ else if (arg_count == 1)
+ {
+ /* py_argtype_list is not actually a list but a single gdb.Type
+ object. */
+ struct type *arg_type = type_object_to_type (py_argtype_list);
+
+ if (arg_type == NULL)
+ PyErr_SetString (PyExc_TypeError,
+ _("Arg type returned by the get_argtypes method "
+ "of a debug method worker object is not a gdb.Type "
+ "object."));
+ else
+ {
+ type_array[1] = arg_type;
+ i++;
+ }
+ }
+ if (PyErr_Occurred ())
+ {
+ 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->this_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;
+ PyObject *invoke_method;
+ struct type *obj_type, *this_type;
+ struct value *result = NULL;
+ struct py_ext_object *py_ext_obj = ext_obj;
+ PyObject *debugmethod_worker = py_ext_obj->worker;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ invoke_method = PyObject_GetAttrString (debugmethod_worker,
+ 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;
+ }
+
+ obj_type = check_typedef (value_type (obj));
+ this_type = check_typedef (type_object_to_type (py_ext_obj->this_type));
+ if (TYPE_CODE (obj_type) == TYPE_CODE_PTR)
+ {
+ struct type *this_ptr = lookup_pointer_type (this_type);
+
+ if (!types_equal (obj_type, this_ptr))
+ obj = value_cast (this_ptr, obj);
+ }
+ else if (TYPE_CODE (obj_type) == TYPE_CODE_REF)
+ {
+ struct type *this_ref = lookup_reference_type (this_type);
+
+ if (!types_equal (obj_type, this_ref))
+ obj = value_cast (this_ref, obj);
+ }
+ else
+ {
+ if (!types_equal (obj_type, this_type))
+ obj = value_cast (this_type, obj);
+ }
+ py_value_obj = value_to_value_object (obj);
+ if (py_value_obj == NULL)
+ {
+ gdbpy_print_stack ();
+ 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;
+ }
+
+ PyTuple_SET_ITEM (py_arg_tuple, i, py_value_arg);
+ }
+
+ py_result = PyObject_CallMethodObjArgs (debugmethod_worker,
+ py_invoke_method_name,
+ 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. WORKER is the corresponding
+ debug method worker for object of THIS_TYPE. */
+
+static struct ext_fn_descriptor *
+new_python_ext_method (PyObject *worker, PyObject *this_type)
+{
+ struct py_ext_object *ext_object = XCNEW (struct py_ext_object);
+
+ ext_object->worker = worker;
+ ext_object->this_type = this_type;
+ Py_XINCREF (worker);
+ Py_XINCREF (this_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);
+
+ py_match_method_name = PyString_FromString (match_method_name);
+ if (py_match_method_name == NULL)
+ return -1;
+
+ py_invoke_method_name = PyString_FromString (invoke_method_name);
+ if (py_invoke_method_name == NULL)
+ return -1;
+
+ py_get_argtypes_method_name = PyString_FromString (get_argtypes_method_name);
+ if (py_get_argtypes_method_name == NULL)
+ return -1;
+
+ return 1;
+}
+
+#endif /* HAVE_PYTHON */
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 97fb0be..fd4254a 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 matcher list. */
+ PyObject *debugmethod_matchers;
} 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->debugmethod_matchers);
Py_TYPE (self)->tp_free (self);
}
@@ -99,6 +103,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debugmethod_matchers = PyList_New (0);
+ if (self->debugmethod_matchers == 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 'debugmethod_matchers' attribute. */
+
+PyObject *
+objfpy_get_debugmethod_matchers (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->debugmethod_matchers);
+ return self->debugmethod_matchers;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -292,6 +314,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->debugmethod_matchers = PyList_New (0);
+ if (object->debugmethod_matchers == 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 },
+ { "debugmethod_matchers", objfpy_get_debugmethod_matchers, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index cda5a86..c65d863 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 *debugmethod_matchers;
} 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->debugmethod_matchers);
Py_TYPE (self)->tp_free (self);
}
@@ -107,6 +111,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debugmethod_matchers = PyList_New (0);
+ if (self->debugmethod_matchers == 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 'debugmethod_matchers' attribute. */
+
+PyObject *
+pspy_get_debugmethod_matchers (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->debugmethod_matchers);
+ return self->debugmethod_matchers;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -288,6 +310,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->debugmethod_matchers = PyList_New (0);
+ if (object->debugmethod_matchers == 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 },
+ { "debugmethod_matchers", pspy_get_debugmethod_matchers, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index ef5cd3f..8e58c62 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -317,11 +317,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_debugmethod_matchers (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_debugmethod_matchers (PyObject *, void *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -402,6 +404,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 337c170..d0e0aba 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1688,7 +1688,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/testsuite/gdb.python/py-debugmethods.cc b/gdb/testsuite/gdb.python/py-debugmethods.cc
new file mode 100644
index 0000000..0ffc59f
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.cc
@@ -0,0 +1,192 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 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/>. */
+
+#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 CC <A_plus_A>:" << endl;
+ return a + obj.a;
+}
+
+int A::operator- (const A &obj)
+{
+ cout << "From CC <A_minus_A>:" << endl;
+ return a - obj.a;
+}
+
+int A::geta (void)
+{
+ cout << "From CC geta:" << endl;
+ return a;
+}
+
+class B : public A
+{
+ public:
+ virtual int operator* (const B &obj);
+};
+
+int
+B::operator* (const B &obj)
+{
+ cout << "From CC <B_star_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:
+ /* This class overrides the virtual operator* defined in B. The
+ associated Python script replaces B::operator* but not D::operator*.
+ Hence, if we have a reference pointing to an instance of D, the C++
+ version of D::operator* should be invoked and not the Python version
+ B::operator* even after loading the python script. */
+ virtual int operator* (const B &obj);
+};
+
+int
+D::operator* (const B &obj)
+{
+ cout << "From CC <D_star_D>:" << endl;
+ return a * obj.a;
+}
+
+class E : public A
+{
+ public:
+ /* This class has a member named 'a', while the base class also has a
+ member named 'a'. When one invokes A::geta(), A::a should be
+ returned and not E::a as the 'geta' method is defined on class 'A'.
+ This class tests this aspect of debug methods. */
+ int a;
+};
+
+template <typename T>
+class G
+{
+ public:
+ template <typename T1>
+ int size_diff ();
+
+ template <int M>
+ int size_mul ();
+
+ template <typename T1>
+ T mul(const T1 t1);
+
+ public:
+ T t;
+};
+
+template <typename T>
+template <typename T1>
+int
+G<T>::size_diff ()
+{
+ cout << "From CC G<>::size_diff:" << endl;
+ return sizeof (T1) - sizeof (T);
+}
+
+template <typename T>
+template <int M>
+int
+G<T>::size_mul ()
+{
+ cout << "From CC G<>::size_mul:" << endl;
+ return M * sizeof (T);
+}
+
+template <typename T>
+template <typename T1>
+T
+G<T>::mul (const T1 t1)
+{
+ cout << "From CC G<>::mul:" << endl;
+ return t1 * t;
+}
+
+}
+
+using namespace dop;
+
+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;
+ G<int> g, *g_ptr;
+ g.t = 5;
+ g_ptr = &g;
+ E e;
+ e.a = 1000;
+ e.A::a = 100;
+
+ int diff = g.size_diff<float> ();
+ int smul = g.size_mul<2> ();
+ int mul = g.mul (1.0);
+
+ for (int i = 0; i < 10; i++)
+ {
+ a1.array[i] = a2.array[i] = i;
+ }
+
+ return 0; /* Break here. */
+}
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.exp b/gdb/testsuite/gdb.python/py-debugmethods.exp
new file mode 100644
index 0000000..18d8d52
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.exp
@@ -0,0 +1,125 @@
+# Copyright 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/>.
+
+# This file is part of the GDB testsuite. It tests the debug methods
+# feature in the Python extension language.
+
+load_lib gdb-python.exp
+
+if { [skip_cplus_tests] } { continue }
+
+standard_testfile py-debugmethods.cc
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] {
+ return -1
+}
+
+set debug_methods_script "${srcdir}/${subdir}/${testfile}.py"
+
+gdb_breakpoint [gdb_get_line_number "Break here."]
+gdb_continue_to_breakpoint "Break here" ".*Break here.*"
+
+# Tests before loading the debug methods.
+gdb_test "p a1 + a2" "From CC <A_plus_A>.*15" "Before: a1 + a2"
+gdb_test "p a2 - a1" "From CC <A_minus_A>.*5" "Before: a1 - a2"
+gdb_test "p b1 - a1" "From CC <A_minus_A>.*25" "Before: b1 - a1"
+gdb_test "p c1 - a1" "From CC <A_minus_A>.*15" "Before: c1 - a1"
+gdb_test "p c1 - c1" "From CC <A_minus_A>.*0" "Before: c1 - c1"
+gdb_test "p a1.geta()" "From CC geta.*5" "Before: a1.geta()"
+gdb_test "p ref_c - a1" "From CC <A_minus_A>.*15" "Before: ref_c - a1"
+gdb_test "p ref_c - c1" "From CC <A_minus_A>.*0" "Before: ref_c - c1"
+gdb_test "p b1 * b1" "From CC <B_star_B>.*900" "Before: b1 * b1"
+gdb_test "p ref_c * b1" "No symbol.*" "Before: ref_c * b1"
+gdb_test "p ref_d * b1" "From CC <D_star_D>.*1500" "Before: ref_d * b1"
+gdb_test "p bt * c1" "From CC <B_star_B>.*800" "Before: bt * c1"
+gdb_test "p ++a1" "No symbol.*" "Before: ++a1"
+gdb_test "p a1.getarrayind(5)" "Couldn't find method.*" \
+ "Before: a1.getarrayind(5)"
+gdb_test "p e.geta()" "From CC geta.*100" "Before: e.geta()"
+gdb_test "p g.size_diff<float>()" "From CC G<>::size_diff.*" \
+ "Before: g.size_diff<float>()"
+gdb_test "p g.size_diff<unsigned long>()" "Couldn't find method.*" \
+ "Before: g.size_diff<unsigned long>()"
+gdb_test "p g.size_mul<2>()" "From CC G<>::size_mul.*" \
+ "Before: g.size_mul<2>()"
+gdb_test "p g.size_mul<5>()" "Couldn't find method.*" \
+ "Before: g.size_mul<5>()"
+gdb_test "p g.mul<double>(2.0)" "From CC G<>::mul.*" \
+ "Before: g.mul<double>(2.0)"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "Before: g.mul<char>('a')"
+
+# Load the script which adds the debug methods.
+gdb_test_no_output "source ${debug_methods_script}" "load the script file"
+
+# Tests after loading debug methods.
+gdb_test "p a1 + a2" "From Python <A_plus_A>.*15" "After: a1 + a2"
+gdb_test "p a2 - a1" "From CC <A_minus_A>.*5" "After: a1 - a2"
+gdb_test "p b1 - a1" "From CC <A_minus_A>.*25" "After: b1 - a2"
+gdb_test "p c1 - a1" "From CC <A_minus_A>.*15" "After: c1 - a1"
+gdb_test "p c1 - c1" "From Python <C_minus_C>.*0" "After: c1 - c1"
+gdb_test "p a1.geta()" "From Python <A_geta>.*5" "After: a1.geta()"
+gdb_test "p ref_c - a1" "From CC <A_minus_A>.*15" "After: ref_c - a1"
+gdb_test "p ref_c - c1" "From Python <C_minus_C>.*0" "After: ref_c - c1"
+gdb_test "p b1 * b1" "From Python <B_star_B>.*900" "After: b1 * b1"
+gdb_test "p ref_c * b1" "No symbol.*" "After: ref_c * b1 failure"
+gdb_test "p ref_d * b1" "From CC <D_star_D>.*1500" "After: ref_d * b1"
+gdb_test "p bt * c1" "From Python <B_star_B>.*800" "After: bt * c1"
+gdb_test "p ++a1" "From Python <plus_plus_A>.*6" "After: ++a1"
+gdb_test "p a1.getarrayind(5)" "From Python <A_getarrayind>.*5" \
+ "After: a1.getarrayind(5)"
+gdb_test "p e.geta()" "From Python <A_geta>.*100" "After: e.geta()"
+gdb_test "p g.size_diff<float> ()" "From Python G<>::size_diff.*" \
+ "After: g.size_diff<float>()"
+gdb_test "p g.size_diff< unsigned long >()" "From Python G<>::size_diff.*" \
+ "After: g.size_diff<unsigned long>()"
+gdb_test "p g.size_mul<2>()" "From Python G<>::size_mul.*" \
+ "After: g.size_mul<2>()"
+gdb_test "p g.size_mul< 5 >()" "From Python G<>::size_mul.*" \
+ "After: g.size_mul< 5 >()"
+gdb_test "p g.mul<double>(2.0)" "From Python G<>::mul.*" \
+ "After: g.mul<double>(2.0)"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+gdb_test "p g_ptr->mul<char>('a')" "From Python G<>::mul.*" \
+ "After: g->mul<char>('a')"
+
+# Tests for 'disable/enable debugmethod' command.
+gdb_test_no_output "disable debugmethod .*debugmethods G_methods" \
+ "Disable G_methods"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "g.mul<char>('a') after disabling G_methods"
+gdb_test_no_output "enable debugmethod .*debugmethods G_methods" \
+ "Enable G_methods"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+ "After enabling G_methods"
+gdb_test_no_output "disable debugmethod .*debugmethods G_methods;mul" \
+ "Disable G_methods;mul"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "g.mul<char>('a') after disabling G_methods;mul"
+gdb_test_no_output "enable debugmethod .*debugmethods G_methods;mul" \
+ "Enable G_methods;mul"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+ "After enabling G_methods;mul"
+
+# Test for 'info debug-methods' command
+gdb_test "info debugmethod global plus" "global.*plus_plus_A - enabled" \
+ "info debug-method global plus"
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.py b/gdb/testsuite/gdb.python/py-debugmethods.py
new file mode 100644
index 0000000..d263d48
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.py
@@ -0,0 +1,185 @@
+# Copyright 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/>.
+
+# This file is part of the GDB testsuite. It implements debug methods
+# in the Python extension language.
+
+import gdb
+import re
+
+from gdb.debugmethods import DebugMethod, DebugMethodMatcher, DebugMethodWorker
+from gdb.debugmethods import SimpleDebugMethodMatcher
+
+def A_plus_A(obj, opr):
+ print ('From Python <A_plus_A>:')
+ return obj['a'] + opr['a']
+
+def plus_plus_A(obj):
+ print ('From Python <plus_plus_A>:')
+ return obj['a'] + 1
+
+def C_minus_C(obj, opr):
+ # This function is not defined for objects of class C in the associated C++
+ # file. However, C is derived from A which has a virtual operator- method.
+ # Hence, if an operator '-' is used on a reference pointing to 'C' after
+ # loading this script, then this Python version should be invoked.
+ 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_getarrayind(obj, index):
+ print 'From Python <A_getarrayind>:'
+ 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()
+
+
+class G_size_diff_worker(DebugMethodWorker):
+ def __init__(self, class_template_type, method_template_type):
+ self._class_template_type = class_template_type
+ self._method_template_type = method_template_type
+
+ def get_argtypes(self):
+ pass
+
+ def invoke(self, obj, args):
+ print ('From Python G<>::size_diff()')
+ return (self._method_template_type.sizeof -
+ self._class_template_type.sizeof)
+
+
+class G_size_mul_worker(DebugMethodWorker):
+ def __init__(self, class_template_type, method_template_val):
+ self._class_template_type = class_template_type
+ self._method_template_val = method_template_val
+
+ def get_argtypes(self):
+ pass
+
+ def invoke(self, obj, args):
+ print ('From Python G<>::size_mul()')
+ return self._class_template_type.sizeof * self._method_template_val
+
+
+class G_mul_worker(DebugMethodWorker):
+ def __init__(self, class_template_type, method_template_type):
+ self._class_template_type = class_template_type
+ self._method_template_type = method_template_type
+
+ def get_argtypes(self):
+ return self._method_template_type
+
+ def invoke(self, obj, args):
+ print ('From Python G<>::mul()')
+ return obj['t'] * args[0]
+
+
+class G_methods_matcher(DebugMethodMatcher):
+ def __init__(self):
+ DebugMethodMatcher.__init__(self, 'G_methods')
+ self.methods = [DebugMethod('size_diff'),
+ DebugMethod('size_mul'),
+ DebugMethod('mul')]
+
+ def _is_enabled(self, name):
+ for method in self.methods:
+ if method.name == name and method.enabled:
+ return True
+
+ def match(self, class_type, method_name):
+ class_tag = class_type.unqualified().tag
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_tag):
+ return None
+ t_name = class_tag[7:-1]
+ try:
+ t_type = gdb.lookup_type(t_name)
+ except Exception:
+ return None
+ if re.match('^size_diff<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$', method_name):
+ if not self._is_enabled('size_diff'):
+ return None
+ t1_name = method_name[10:-1]
+ try:
+ t1_type = gdb.lookup_type(t1_name)
+ return G_size_diff_worker(t_type, t1_type)
+ except Exception:
+ return None
+ if re.match('^size_mul<[ ]*[0-9]+[ ]*>$', method_name):
+ if not self._is_enabled('size_mul'):
+ return None
+ m_val = int(method_name[9:-1])
+ return G_size_mul_worker(t_type, m_val)
+ if re.match('^mul<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$', method_name):
+ if not self._is_enabled('mul'):
+ return None
+ t1_name = method_name[4:-1]
+ try:
+ t1_type = gdb.lookup_type(t1_name)
+ return G_mul_worker(t_type, t1_type)
+ except Exception:
+ return None
+
+
+global_dm_list = [
+ SimpleDebugMethodMatcher('A_plus_A',
+ '^dop::A$',
+ 'operator\+',
+ A_plus_A,
+ # This is a replacement, hence match the arg type
+ # exactly!
+ type_A.const().reference()),
+ SimpleDebugMethodMatcher('plus_plus_A',
+ '^dop::A$',
+ 'operator\+\+',
+ plus_plus_A),
+ SimpleDebugMethodMatcher('C_minus_C',
+ '^dop::C$',
+ 'operator\-',
+ C_minus_C,
+ type_C),
+ SimpleDebugMethodMatcher('B_star_B',
+ '^dop::B$',
+ 'operator\*',
+ B_star_B,
+ # This is a replacement, hence match the arg type
+ # exactly!
+ type_B.const().reference()),
+ SimpleDebugMethodMatcher('A_geta',
+ '^dop::A$',
+ '^geta$',
+ A_geta),
+ SimpleDebugMethodMatcher('A_getarrayind',
+ '^dop::A$',
+ '^getarrayind$',
+ A_getarrayind,
+ type_int),
+]
+
+for matcher in global_dm_list:
+ gdb.debugmethods.register_debugmethod_matcher(gdb, matcher)
+gdb.debugmethods.register_debugmethod_matcher(gdb.current_progspace(),
+ G_methods_matcher())
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 8e863e3..6f3ed62 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,
- int *static_memfuncp)
+ 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);
+ &args[0] /* objp */,
+ NULL /* pass NULL symbol since symbol is unknown */,
+ &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,29 @@ 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);
+
+ xfree (ext_fn);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
+ }
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
#ifdef lint
@@ -510,6 +552,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 +566,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 +618,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 +638,29 @@ 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);
+
+ xfree (ext_fn);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
}
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index deb01cb..86681cb 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -42,6 +42,7 @@
#include "observer.h"
#include "objfiles.h"
#include "exceptions.h"
+#include "ext-function.h"
extern unsigned int overload_debug;
/* Local functions. */
@@ -70,8 +71,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);
@@ -98,9 +99,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);
@@ -2293,52 +2295,76 @@ value_struct_elt_bitpos (struct value **argp, int bitpos, struct type *ftype,
/* Search through the methods of an object (and its bases) to find a
specified method. Return the pointer to the fn_field list of
- overloaded instances.
+ overloaded instances defined in the source language. If available
+ and matching, a vector matching/overloaded debug methods defined in
+ extension languages are also returned.
Helper function for value_find_oload_list.
ARGP is a pointer to a pointer to a value (the object).
METHOD is a string containing the method name.
OFFSET is the offset within the value.
TYPE is the assumed type of the object.
+ FN_LIST The pointer to matching overloaded instances defined in
+ source language.
NUM_FNS is the number of overloaded instances.
+ EXT_FN_VEC The vector matching debug methods defined in extension
+ languages.
BASETYPE is set to the actual type of the subobject where the
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;
@@ -2355,13 +2381,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.
@@ -2369,14 +2393,20 @@ find_method_list (struct value **argp, const char *method,
ARGP is a pointer to a pointer to a value (the object).
METHOD is the method name.
OFFSET is the offset within the value contents.
+ FN_LIST The pointer to matching overloaded instances defined in
+ source language.
NUM_FNS is the number of overloaded instances.
+ EXT_FN_VEC The vector matching debug methods defined in extension
+ languages.
BASETYPE is set to the type of the base subobject that defines the
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;
@@ -2398,8 +2428,34 @@ 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);
+}
+
+/* Return the dynamic type of OBJ. */
+
+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
@@ -2427,9 +2483,11 @@ value_find_oload_method_list (struct value **argp, const char *method,
Return value is an integer: 0 -> good match, 10 -> debugger applied
non-standard coercions, 100 -> incompatible.
- If a method is being searched for, VALP will hold the value.
- If a non-method is being searched for, SYMP will hold the symbol
- for it.
+ If the best match is a debug function/method implemented in an extension
+ language, then EXT_FN will hold the matching function/method. Otherwise,
+ VALP will hold the value if a method is being searched for, or SYMP will
+ hold the symbol for the matching function if a non-method is being
+ searched for.
If a method is being searched for, and it is a static method,
then STATICP will point to a non-zero value.
@@ -2447,6 +2505,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);
@@ -2454,16 +2513,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;
@@ -2475,6 +2542,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. */
@@ -2503,12 +2572,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) ? "::" : "",
@@ -2519,18 +2588,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));
+ 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);
+ }
- make_cleanup (xfree, 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)
@@ -2673,21 +2806,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));
@@ -2697,10 +2815,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);
+ }
+ 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));
}
- *objp = temp;
}
+ else
+ *symp = oload_syms[func_oload_champ];
do_cleanups (all_cleanups);
@@ -2835,7 +3008,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),
@@ -2873,7 +3046,9 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
/* Look for a function to take NARGS args of ARGS. Find
the best match from among the overloaded methods or functions
- (depending on METHOD) given by FNS_PTR or OLOAD_SYMS, respectively.
+ given by FNS_PTR or OLOAD_SYMS or EXT_FN_VEC, respectively. If
+ EXT_FN_VEC is NULL, then METHOD indicates whether to use FNS_PTR
+ or OLOAD_SYMS to find the best match.
The number of methods/functions in the list is given by NUM_FNS.
Return the index of the best match; store an indication of the
quality of the match in OLOAD_CHAMP_BV.
@@ -2883,10 +3058,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. */
@@ -2896,18 +3073,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
{
@@ -2916,13 +3102,18 @@ 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 f846669..74cac84 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
@@ -690,6 +691,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);
More information about the Gdb-patches
mailing list