From: Daniel Colascione <dancol@dancol.org>
To: gdb-patches@sourceware.org
Subject: [RFC] Support for calling overloaded C++ functions from Python
Date: Mon, 06 Oct 2014 21:02:00 -0000 [thread overview]
Message-ID: <54330374.8000302@dancol.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 12859 bytes --]
This patch adds support for more naturally calling overloaded C++
functions from Python extensions. It adds a new top-level function
exposing find_overload_match to Python, then uses this facility to
implement object-specific call methods that take function names and
argument lists, automatically resolving overloads based on type information.
e.g.,
def make_calls(obj):
obj.call('aMemberFunction', -1)
gdb.call('aFreeFunction', 'foo', obj)
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 557e168..3e8683e 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -110,6 +110,18 @@ def auto_load_packages():
auto_load_packages()
+def call(fn_name, *args):
+ "Call function fn_name with args, performing overload resolution."
+ fn_symbol, is_member = lookup_symbol(fn_name)
+ if fn_symbol is None:
+ # No symbol for this value, so just look it up globally and
+ # call it as a value.
+ fn = parse_and_eval(fn_name)
+ return fn(*args)
+
+ # Otherwise, try to resolve the C++ overload set
+ find_overload(fn_name, *args, symbol = fn_symbol)(*args)
+
def GdbSetPythonDirectory(dir):
"""Update sys.path, reload gdb and auto-load packages."""
global PYTHONDIR
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index 6900d58..74b1a88 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -453,6 +453,233 @@ gdbpy_lookup_global_symbol (PyObject *self,
PyObject *args, PyObject *kw)
return sym_obj;
}
+/* Implementation of gdb.find_overload(). */
+
+struct find_overload_arguments {
+ int side_effects;
+ int adl;
+ int full;
+ int both;
+ struct value *this_value;
+ struct symbol *this_symbol;
+};
+
+static int
+parse_find_overload_kw_arguments (PyObject *kw,
+ struct find_overload_arguments *foargs)
+{
+ PyObject *pyobj = NULL;
+ PyObject *pysym = NULL;
+ static char* keywords[] = { "side_effects",
+ "adl",
+ "full",
+ "both",
+ "object",
+ "symbol",
+ NULL };
+
+ PyObject *empty_tuple = PyTuple_New (0);
+ if (empty_tuple == NULL)
+ return -1;
+
+ make_cleanup_py_decref (empty_tuple);
+ if (!PyArg_ParseTupleAndKeywords (empty_tuple, kw,
+ "|iiiiOO", keywords,
+ &foargs->side_effects,
+ &foargs->adl,
+ &foargs->full,
+ &foargs->both,
+ &pyobj,
+ &pysym))
+ return -1;
+
+ if (pyobj && pyobj != Py_None)
+ {
+ foargs->this_value = convert_value_from_python (pyobj);
+ if (foargs->this_value == NULL)
+ return -1;
+ }
+
+ if (pysym && pysym != Py_None)
+ {
+ foargs->this_symbol = symbol_object_to_symbol (pysym);
+ if (foargs->this_symbol == NULL)
+ {
+ PyErr_SetString (PyExc_TypeError, "invalid symbol");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static PyObject *
+gdbpy_find_overload_1 (PyObject *self, PyObject *args, PyObject *kw)
+{
+ struct value **vargs;
+ Py_ssize_t nargs, i, varg_nr, nr_vargs;
+ int badness;
+ char *name = NULL;
+ struct find_overload_arguments foargs;
+ enum oload_search_type search_method;
+ PyObject* pyresult;
+
+ struct value *result_value;
+ struct symbol *result_symbol;
+ int result_staticp;
+
+ memset (&foargs, 0, sizeof (foargs));
+ foargs.adl = 1;
+ foargs.side_effects = 1;
+
+ nargs = PyTuple_Size (args);
+ if (nargs == -1)
+ return NULL;
+
+ if (nargs == 0)
+ {
+ PyErr_SetString (PyExc_TypeError, "no function given");
+ return NULL;
+ }
+
+ if (PyTuple_GET_ITEM (args, 0) != Py_None)
+ {
+ name = python_string_to_host_string (PyTuple_GET_ITEM (args, 0));
+ if (name == NULL)
+ return NULL;
+
+ make_cleanup (xfree, name);
+ }
+
+ if (parse_find_overload_kw_arguments (kw, &foargs) == -1)
+ return NULL;
+
+ nr_vargs = nargs - 1;
+ if (foargs.this_value != NULL)
+ nr_vargs += 1;
+
+ vargs = alloca (sizeof (*vargs) * nr_vargs);
+ varg_nr = 0;
+
+ if (foargs.this_value != NULL)
+ vargs[varg_nr++] = foargs.this_value;
+
+ for (i = 1; i < nargs; ++i)
+ {
+ PyObject *parg = PyTuple_GET_ITEM (args, i);
+ struct value *varg = convert_value_from_python (parg);
+ if (varg == NULL)
+ return NULL;
+
+ vargs[varg_nr++] = varg;
+ }
+
+ if (foargs.both)
+ {
+ search_method = BOTH;
+ if (foargs.this_value == NULL)
+ {
+ PyErr_SetString (PyExc_TypeError,
+ "with both=True, object must be set");
+ return NULL;
+ }
+ }
+ else if (foargs.this_value && foargs.this_symbol)
+ search_method = BOTH;
+ else if (foargs.this_value)
+ search_method = METHOD;
+ else
+ search_method = NON_METHOD;
+
+ result_value = NULL;
+ result_symbol = NULL;
+ result_staticp = 0;
+
+ badness = find_overload_match (vargs, nr_vargs,
+ name,
+ search_method,
+ ( foargs.this_value
+ ? &foargs.this_value
+ : NULL ),
+ foargs.this_symbol,
+ &result_value,
+ &result_symbol,
+ &result_staticp,
+ foargs.adl,
+ ( foargs.side_effects
+ ? EVAL_NORMAL
+ : EVAL_AVOID_SIDE_EFFECTS));
+
+ if (!foargs.full && result_symbol != NULL)
+ {
+ if (symbol_read_needs_frame (result_symbol))
+ {
+ PyErr_SetString (PyExc_RuntimeError, "need frame?!");
+ return NULL;
+ }
+
+ result_value = read_var_value (result_symbol, NULL);
+ if (result_value == NULL)
+ return NULL;
+
+ result_symbol = NULL;
+ }
+
+ if (result_symbol != NULL)
+ pyresult = symbol_to_symbol_object (result_symbol);
+ else if (result_value != NULL)
+ pyresult = value_to_value_object (result_value);
+ else
+ {
+ pyresult = Py_None;
+ Py_INCREF (pyresult);
+ }
+
+ if (pyresult == NULL)
+ return NULL;
+
+ if (foargs.full) {
+ PyObject* pyobject;
+
+ if (foargs.this_value == NULL)
+ {
+ pyobject = Py_None;
+ Py_INCREF (pyobject);
+ }
+ else
+ {
+ pyobject = value_to_value_object (foargs.this_value);
+ if (pyobject == NULL)
+ return NULL;
+ }
+
+ make_cleanup_py_decref (pyresult);
+ return Py_BuildValue ("(iOOO)",
+ badness,
+ pyobject,
+ pyresult,
+ result_staticp ? Py_True : Py_False);
+ }
+
+ return pyresult;
+}
+
+PyObject *
+gdbpy_find_overload (PyObject *self, PyObject *args, PyObject *kw)
+{
+ volatile struct gdb_exception except;
+ PyObject *result = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanup = make_cleanup_value_free_to_mark
(value_mark ());
+ result = gdbpy_find_overload_1 (self, args, kw);
+ do_cleanups (cleanup);
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+ return result;
+}
+
/* This function is called when an objfile is about to be freed.
Invalidate the symbol as further actions on the symbol would result
in bad data. All access to obj->symbol should be gated by
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index bdec389..0cefd4f 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -758,7 +758,11 @@ valpy_call (PyObject *self, PyObject *args,
PyObject *keywords)
}
GDB_PY_HANDLE_EXCEPTION (except);
- if (TYPE_CODE (ftype) != TYPE_CODE_FUNC)
+ /* call_function_by_hand will fail if our function happens not to be
+ callable, but it considers integers callable, treating them as
+ function addresses. Allowing any integer to be callable from
+ Python seems silly, however. */
+ if (TYPE_CODE (ftype) == TYPE_CODE_INT)
{
PyErr_SetString (PyExc_RuntimeError,
_("Value is not callable (not TYPE_CODE_FUNC)."));
@@ -894,6 +898,93 @@ valpy_fetch_lazy (PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
+/* Convenient wrapper around find_overload. */
+
+static PyObject *
+valpy_call_method_1 (PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject* function = NULL;
+ PyObject* value_args = NULL;
+ Py_ssize_t nargs, i;
+ struct value *self_value = ((value_object *) self)->value;
+ struct type *self_type;
+
+ self_type = check_typedef (value_type (self_value));
+
+ if (TYPE_CODE (self_type) == TYPE_CODE_CLASS ||
+ TYPE_CODE (self_type) == TYPE_CODE_STRUCT ||
+ TYPE_CODE (self_type) == TYPE_CODE_REF)
+ {
+ /* Calls of member functions need `this' to be an address, but
+ we have a value. Get the address here. */
+ self = valpy_get_address (self, NULL);
+ if (self == NULL)
+ return NULL;
+
+ make_cleanup_py_decref (self);
+ }
+
+ nargs = PyTuple_Size (args);
+ if (nargs == -1)
+ return NULL;
+
+ if (nargs == 0)
+ {
+ PyErr_SetString (PyExc_TypeError, "no function given");
+ return NULL;
+ }
+
+ if (kw == NULL)
+ {
+ kw = PyDict_New ();
+ if (kw == NULL)
+ return NULL;
+
+ make_cleanup_py_decref (kw);
+ }
+
+ if (PyMapping_SetItemString (kw, "object", self) == -1)
+ return NULL;
+
+ function = gdbpy_find_overload (self, args, kw);
+ if (function == NULL)
+ return NULL;
+
+ make_cleanup_py_decref (function);
+
+ value_args = PyTuple_New (nargs);
+ if (value_args == NULL)
+ return NULL;
+
+ make_cleanup_py_decref (value_args);
+ Py_INCREF (self);
+ PyTuple_SET_ITEM (value_args, 0, self);
+ for (i = 1; i < nargs; ++i)
+ {
+ PyObject *arg = PyTuple_GET_ITEM (args, i);
+ Py_INCREF (arg);
+ PyTuple_SET_ITEM (value_args, i, arg);
+ }
+
+ return PyObject_CallObject (function, value_args);
+}
+
+static PyObject *
+valpy_call_method (PyObject *self, PyObject *args, PyObject *kw)
+{
+ volatile struct gdb_exception except;
+ PyObject *result = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanup = make_cleanup_value_free_to_mark
(value_mark ());
+ result = valpy_call_method_1 (self, args, kw);
+ do_cleanups (cleanup);
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+ return result;
+}
+
/* Calculate and return the address of the PyObject as the value of
the builtin __hash__ call. */
static long
@@ -1624,6 +1715,9 @@ Return a lazy string representation of the value." },
Return Unicode string representation of the value." },
{ "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
"Fetches the value from the inferior, if it was lazy." },
+ { "call", (PyCFunction) valpy_call_method,
+ METH_VARARGS | METH_KEYWORDS,
+ "Call a member function with overload resolution." },
{NULL} /* Sentinel */
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 6e7e600..65fe71d 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -356,6 +356,8 @@ PyObject *gdbpy_frame_stop_reason_string (PyObject
*, PyObject *);
PyObject *gdbpy_lookup_symbol (PyObject *self, PyObject *args, PyObject
*kw);
PyObject *gdbpy_lookup_global_symbol (PyObject *self, PyObject *args,
PyObject *kw);
+PyObject *gdbpy_find_overload (PyObject *self, PyObject *args,
+ PyObject *kw);
PyObject *gdbpy_newest_frame (PyObject *self, PyObject *args);
PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 40c4ec9..708eddd 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1952,6 +1952,17 @@ a boolean indicating if name is a field of the
current implied argument\n\
METH_VARARGS | METH_KEYWORDS,
"lookup_global_symbol (name [, domain]) -> symbol\n\
Return the symbol corresponding to the given name (or None)." },
+ { "find_overload", (PyCFunction) gdbpy_find_overload,
+ METH_VARARGS | METH_KEYWORDS,
+ "find_overload (name, *args, symbol=none, adl=True, object=None,\n\
+ side_effects=True) -> (result, object, score,
is_static)\n\
+Low-level overload searching. Name is a string giving the base name\n\
+of the overload for which to search; remaining positional arguments\n\
+constrain the overloads of name considered. If adl is true, use C++\n\
+argument-dependent overload to find additional overloads; if object is
not\n\
+None, use it for resolving virtual overloads and as the implicit this
pointer.\n\
+If side_effects is True, permit reading target memory to perform
overload\n\
+resolution." },
{ "block_for_pc", gdbpy_block_for_pc, METH_VARARGS,
"Return the block containing the given pc value, or None." },
{ "solib_name", gdbpy_solib_name, METH_VARARGS,
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
next reply other threads:[~2014-10-06 21:02 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-10-06 21:02 Daniel Colascione [this message]
2014-10-07 13:35 ` Phil Muldoon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=54330374.8000302@dancol.org \
--to=dancol@dancol.org \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox