From patchwork Mon Oct 6 21:02:44 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Colascione X-Patchwork-Id: 3120 Received: (qmail 17942 invoked by alias); 6 Oct 2014 21:02:54 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 17931 invoked by uid 89); 6 Oct 2014 21:02:53 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: dancol.org Received: from dancol.org (HELO dancol.org) (96.126.100.184) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Mon, 06 Oct 2014 21:02:51 +0000 Received: from [2620:10d:c083:1003:863a:4bff:fec8:e538] by dancol.org with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.84) (envelope-from ) id 1XbFQg-0006OD-Cx for gdb-patches@sourceware.org; Mon, 06 Oct 2014 14:02:50 -0700 Message-ID: <54330374.8000302@dancol.org> Date: Mon, 06 Oct 2014 14:02:44 -0700 From: Daniel Colascione User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.1.2 MIME-Version: 1.0 To: gdb-patches@sourceware.org Subject: [RFC] Support for calling overloaded C++ functions from Python X-IsSubscribed: yes 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) 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, 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