From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: 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-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@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 21:02:00 -0000 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 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="C49OVkgWGFsAUU4s7DeIijsatmgiX7uGU" X-IsSubscribed: yes X-SW-Source: 2014-10/txt/msg00102.txt.bz2 This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --C49OVkgWGFsAUU4s7DeIijsatmgiX7uGU Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Content-length: 12658 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 =3D 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 =3D parse_and_eval(fn_name) + return fn(*args) + + # Otherwise, try to resolve the C++ overload set + find_overload(fn_name, *args, symbol =3D 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 =3D NULL; + PyObject *pysym =3D NULL; + static char* keywords[] =3D { "side_effects", + "adl", + "full", + "both", + "object", + "symbol", + NULL }; + + PyObject *empty_tuple =3D PyTuple_New (0); + if (empty_tuple =3D=3D 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 !=3D Py_None) + { + foargs->this_value =3D convert_value_from_python (pyobj); + if (foargs->this_value =3D=3D NULL) + return -1; + } + + if (pysym && pysym !=3D Py_None) + { + foargs->this_symbol =3D symbol_object_to_symbol (pysym); + if (foargs->this_symbol =3D=3D 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 =3D 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 =3D 1; + foargs.side_effects =3D 1; + + nargs =3D PyTuple_Size (args); + if (nargs =3D=3D -1) + return NULL; + + if (nargs =3D=3D 0) + { + PyErr_SetString (PyExc_TypeError, "no function given"); + return NULL; + } + + if (PyTuple_GET_ITEM (args, 0) !=3D Py_None) + { + name =3D python_string_to_host_string (PyTuple_GET_ITEM (args, 0)); + if (name =3D=3D NULL) + return NULL; + + make_cleanup (xfree, name); + } + + if (parse_find_overload_kw_arguments (kw, &foargs) =3D=3D -1) + return NULL; + + nr_vargs =3D nargs - 1; + if (foargs.this_value !=3D NULL) + nr_vargs +=3D 1; + + vargs =3D alloca (sizeof (*vargs) * nr_vargs); + varg_nr =3D 0; + + if (foargs.this_value !=3D NULL) + vargs[varg_nr++] =3D foargs.this_value; + + for (i =3D 1; i < nargs; ++i) + { + PyObject *parg =3D PyTuple_GET_ITEM (args, i); + struct value *varg =3D convert_value_from_python (parg); + if (varg =3D=3D NULL) + return NULL; + + vargs[varg_nr++] =3D varg; + } + + if (foargs.both) + { + search_method =3D BOTH; + if (foargs.this_value =3D=3D NULL) + { + PyErr_SetString (PyExc_TypeError, + "with both=3DTrue, object must be set"); + return NULL; + } + } + else if (foargs.this_value && foargs.this_symbol) + search_method =3D BOTH; + else if (foargs.this_value) + search_method =3D METHOD; + else + search_method =3D NON_METHOD; + + result_value =3D NULL; + result_symbol =3D NULL; + result_staticp =3D 0; + + badness =3D 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 !=3D NULL) + { + if (symbol_read_needs_frame (result_symbol)) + { + PyErr_SetString (PyExc_RuntimeError, "need frame?!"); + return NULL; + } + + result_value =3D read_var_value (result_symbol, NULL); + if (result_value =3D=3D NULL) + return NULL; + + result_symbol =3D NULL; + } + + if (result_symbol !=3D NULL) + pyresult =3D symbol_to_symbol_object (result_symbol); + else if (result_value !=3D NULL) + pyresult =3D value_to_value_object (result_value); + else + { + pyresult =3D Py_None; + Py_INCREF (pyresult); + } + + if (pyresult =3D=3D NULL) + return NULL; + + if (foargs.full) { + PyObject* pyobject; + + if (foargs.this_value =3D=3D NULL) + { + pyobject =3D Py_None; + Py_INCREF (pyobject); + } + else + { + pyobject =3D value_to_value_object (foargs.this_value); + if (pyobject =3D=3D 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 =3D NULL; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + struct cleanup *cleanup =3D make_cleanup_value_free_to_mark (value_mark ()); + result =3D 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) !=3D 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) =3D=3D 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 =3D NULL; + PyObject* value_args =3D NULL; + Py_ssize_t nargs, i; + struct value *self_value =3D ((value_object *) self)->value; + struct type *self_type; + + self_type =3D check_typedef (value_type (self_value)); + + if (TYPE_CODE (self_type) =3D=3D TYPE_CODE_CLASS || + TYPE_CODE (self_type) =3D=3D TYPE_CODE_STRUCT || + TYPE_CODE (self_type) =3D=3D TYPE_CODE_REF) + { + /* Calls of member functions need `this' to be an address, but + we have a value. Get the address here. */ + self =3D valpy_get_address (self, NULL); + if (self =3D=3D NULL) + return NULL; + + make_cleanup_py_decref (self); + } + + nargs =3D PyTuple_Size (args); + if (nargs =3D=3D -1) + return NULL; + + if (nargs =3D=3D 0) + { + PyErr_SetString (PyExc_TypeError, "no function given"); + return NULL; + } + + if (kw =3D=3D NULL) + { + kw =3D PyDict_New (); + if (kw =3D=3D NULL) + return NULL; + + make_cleanup_py_decref (kw); + } + + if (PyMapping_SetItemString (kw, "object", self) =3D=3D -1) + return NULL; + + function =3D gdbpy_find_overload (self, args, kw); + if (function =3D=3D NULL) + return NULL; + + make_cleanup_py_decref (function); + + value_args =3D PyTuple_New (nargs); + if (value_args =3D=3D NULL) + return NULL; + + make_cleanup_py_decref (value_args); + Py_INCREF (self); + PyTuple_SET_ITEM (value_args, 0, self); + for (i =3D 1; i < nargs; ++i) + { + PyObject *arg =3D 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 =3D NULL; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + struct cleanup *cleanup =3D make_cleanup_value_free_to_mark (value_mark ()); + result =3D 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=3Dnone, adl=3DTrue, object=3DNone,= \n\ + side_effects=3DTrue) -> (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, --C49OVkgWGFsAUU4s7DeIijsatmgiX7uGU Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" Content-length: 819 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAEBCAAGBQJUMwN0AAoJEN4WImmbpWBlmN0P/1kFaptLWPubC+/CnB7Y1+q0 Z87eUMg+uDZB9DfXGh59ouBpQbo3F9A4SHQx5CuEl2lqa8Vc8OQxmbD6Q94JCPHM CfNc+WQvlf3Zmj82YACEu3NzljJipjkcRnhdA3Y/p9aLIjZ8nqSuKvNEiAewXXtc 8chsJTJ91cchgt9mifNKaRcpXebsTNPK5kk1n4RN3FIEPkBkQsB0NHyQHN2e7gF/ 6ShSArKecAF51BOTfH1v+WWyIQiNq/DK6MEVNOrWo+0bIa+3zxbr0rTSrjgSLAJ/ x0zEItfn6ZuAI9YQ+ACpCwDRkzEKMo6CMTTuK8HAQDtg1pFZyw9p3fdv1QgRWk8U dkJ3zLG70iYg2Y4y7loYmXK+y/RiBqZsEmCsYaQFitJDAdY00BMHq3mk2K9cx3at OlVzQ11Gms+ync1H2p537So6rcLWWxGFwuHiTX20DjWLkX7I3mGcPxaJxnQ7IRIm nCUXZGuRjiuD4t0LZJtTD7U2KQFnLLp9EmDSyO7uwxT3Rle9PQtbCqOCwAbihO6z oRvSp4fCBdat4Sm+Z078CKJsh1ZVv3sVIdKsywsIV+KoxYMi10+ja2RLJmwoizhR 17WOWNDF/fD+V84wPCHGyNDi6B3cD5UZ22JdadhyMKjPFYT6bFc8UhJILAX774F7 J+BKuNFWsl/OFHZaSwmR =trzB -----END PGP SIGNATURE----- --C49OVkgWGFsAUU4s7DeIijsatmgiX7uGU--