This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Support for calling overloaded C++ functions from Python


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: signature.asc
Description: OpenPGP digital signature


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]