From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1726) id D61E63858439; Tue, 16 May 2023 09:31:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D61E63858439 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1684229481; bh=Tk+s1Jp3kDqaiT/Q6ipwRtioWifPyfGPMCsfZvMc2mM=; h=From:To:Subject:Date:From; b=dt4qWBBbODVqM1GewZSC0vH0jrjFUerdRnP/2IfqT1Bx1teSESqy4NrGu1CPuXJ+j PJqkPxfkbBkOPKyiKH5lFRqFhdewR5JuyPDCmWiPvEaEK2BADgByl7IyVb0VJRii/C 9zo28fjenHfyAadTGGg0U3AWdNcUvSKRu+dMe0x0= Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable From: Andrew Burgess To: gdb-cvs@sourceware.org Subject: [binutils-gdb] gdb/python: rework how the disassembler API reads the result object X-Act-Checkin: binutils-gdb X-Git-Author: Andrew Burgess X-Git-Refname: refs/heads/master X-Git-Oldrev: 56c1f748a5df6d0f7c75586204e3010fa3e164f9 X-Git-Newrev: 0af2f233330024e0e9b4697d510c7030e518e64c Message-Id: <20230516093121.D61E63858439@sourceware.org> Date: Tue, 16 May 2023 09:31:21 +0000 (GMT) List-Id: https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3D0af2f2333300= 24e0e9b4697d510c7030e518e64c commit 0af2f233330024e0e9b4697d510c7030e518e64c Author: Andrew Burgess Date: Mon Jan 23 15:31:28 2023 +0000 gdb/python: rework how the disassembler API reads the result object =20 This commit is a refactor ahead of the next change which will make disassembler styling available through the Python API. =20 Unfortunately, in order to make the styling support available, I think the easiest solution is to make a very small change to the existing API. =20 The current API relies on returning a DisassemblerResult object to represent each disassembled instruction. Currently GDB allows the DisassemblerResult class to be sub-classed, which could mean that a user tries to override the various attributes that exist on the DisassemblerResult object. =20 This commit removes this ability, effectively making the DisassemblerResult class final. =20 Though this is a change to the existing API, I'm hoping this isn't going to cause too many issues: =20 - The Python disassembler API was only added in the previous release of GDB, so I don't expect it to be widely used yet, and =20 - It's not clear to me why a user would need to sub-class the DisassemblerResult type, I allowed it in the original patch because at the time I couldn't see any reason to NOT allow it. =20 Having prevented sub-classing I can now rework the tail end of the gdbpy_print_insn function; instead of pulling the results out of the DisassemblerResult object by calling back into Python, I now cast the Python object back to its C++ type (disasm_result_object), and access the fields directly from there. In later commits I will be reworking the disasm_result_object type in order to hold information about the styled disassembler output. =20 The tests that dealt with sub-classing DisassemblerResult have been removed, and a new test that confirms that DisassemblerResult can't be sub-classed has been added. =20 Reviewed-By: Eli Zaretskii Reviewed-By: Tom Tromey Diff: --- gdb/NEWS | 3 ++ gdb/doc/python.texi | 2 ++ gdb/python/py-disasm.c | 54 +++++++++---------------------= ---- gdb/testsuite/gdb.python/py-disasm.exp | 15 ++++++++-- gdb/testsuite/gdb.python/py-disasm.py | 37 ----------------------- 5 files changed, 31 insertions(+), 80 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 6aa0d5171f2..ca164257126 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -170,6 +170,9 @@ info main (program-counter) values, and can be used as the frame-id when calling gdb.PendingFrame.create_unwind_info. =20 + ** It is now no longer possible to sub-class the + gdb.disassembler.DisassemblerResult type. + *** Changes in GDB 13 =20 * MI version 1 is deprecated, and will be removed in GDB 14. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 11135910656..a906c168373 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -7032,6 +7032,8 @@ instance of this class should be returned from @w{@code{Disassembler.__call__}} (@pxref{Disassembler Class}) if an instruction was successfully disassembled. =20 +It is not possible to sub-class the @code{DisassemblerResult} class. + The @code{DisassemblerResult} class has the following properties and methods: =20 diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c index 9c281f6e089..f246a093014 100644 --- a/gdb/python/py-disasm.c +++ b/gdb/python/py-disasm.c @@ -904,43 +904,14 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR = memaddr, return gdb::optional (-1); } =20 - /* The call into Python neither raised an exception, or returned None. - Check to see if the result looks valid. */ - gdbpy_ref<> length_obj (PyObject_GetAttrString (result.get (), "length")= ); - if (length_obj =3D=3D nullptr) - { - gdbpy_print_stack (); - return gdb::optional (-1); - } - - gdbpy_ref<> string_obj (PyObject_GetAttrString (result.get (), "string")= ); - if (string_obj =3D=3D nullptr) - { - gdbpy_print_stack (); - return gdb::optional (-1); - } - if (!gdbpy_is_string (string_obj.get ())) - { - PyErr_SetString (PyExc_TypeError, _("String attribute is not a strin= g.")); - gdbpy_print_stack (); - return gdb::optional (-1); - } - - gdb::unique_xmalloc_ptr string - =3D gdbpy_obj_to_string (string_obj.get ()); - if (string =3D=3D nullptr) - { - gdbpy_print_stack (); - return gdb::optional (-1); - } - - long length; - if (!gdb_py_int_as_long (length_obj.get (), &length)) - { - gdbpy_print_stack (); - return gdb::optional (-1); - } - + /* The result from the Python disassembler has the correct type. Convert + this back to the underlying C++ object and read the state directly + from this object. */ + struct disasm_result_object *result_obj + =3D (struct disasm_result_object *) result.get (); + + /* Validate the length of the disassembled instruction. */ + long length =3D result_obj->length; long max_insn_length =3D (gdbarch_max_insn_length_p (gdbarch) ? gdbarch_max_insn_length (gdbarch) : INT_MAX); if (length <=3D 0) @@ -961,7 +932,10 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR m= emaddr, return gdb::optional (-1); } =20 - if (strlen (string.get ()) =3D=3D 0) + /* Validate the text of the disassembled instruction. */ + gdb_assert (result_obj->content !=3D nullptr); + std::string string (std::move (result_obj->content->release ())); + if (strlen (string.c_str ()) =3D=3D 0) { PyErr_SetString (PyExc_ValueError, _("String attribute must not be empty.")); @@ -971,7 +945,7 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR me= maddr, =20 /* Print the disassembled instruction back to core GDB, and return the length of the disassembled instruction. */ - info->fprintf_func (info->stream, "%s", string.get ()); + info->fprintf_func (info->stream, "%s", string.c_str ()); return gdb::optional (length); } =20 @@ -1159,7 +1133,7 @@ PyTypeObject disasm_result_object_type =3D { 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB object, representing a disassembler result", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/gdb/testsuite/gdb.python/py-disasm.exp b/gdb/testsuite/gdb.pyt= hon/py-disasm.exp index 2550e60111e..5cbf02fc9fe 100644 --- a/gdb/testsuite/gdb.python/py-disasm.exp +++ b/gdb/testsuite/gdb.python/py-disasm.exp @@ -96,9 +96,7 @@ set test_plans \ [list "ReadMemoryCaughtRuntimeErrorDisassembler" "${addr_pattern}${nop}\= r\n.*"] \ [list "MemorySourceNotABufferDisassembler" "${addr_pattern}Python Except= ion : Result from read_memory is not a buffer\r\n\r\n${u= nknown_error_pattern}"] \ [list "MemorySourceBufferTooLongDisassembler" "${addr_pattern}Python Exc= eption : Buffer returned from read_memory is sized $dec= imal instead of the expected $decimal\r\n\r\n${unknown_error_pattern}"] \ - [list "ResultOfWrongType" "${addr_pattern}Python Exception : Result is not a DisassemblerResult.\r\n.*"] \ - [list "ResultWithInvalidLength" "${addr_pattern}Python Exception : Invalid length attribute: length must be greater than 0.\r\n= .*"] \ - [list "ResultWithInvalidString" "${addr_pattern}Python Exception : String attribute must not be empty.\r\n.*"]] + [list "ResultOfWrongType" "${addr_pattern}Python Exception : Result is not a DisassemblerResult.\r\n.*"]] =20 # Now execute each test plan. foreach plan $test_plans { @@ -217,3 +215,14 @@ with_test_prefix "Bad DisassembleInfo creation" { "RuntimeError: DisassembleInfo is no longer valid\\." \ "Error while executing Python code\\."] } + +# Test that we can't inherit from the DisassemblerResult class. +gdb_test_multiline "Sub-class a breakpoint" \ + "python" "" \ + "class InvalidResultType(gdb.disassembler.DisassemblerResult):" "" \ + " def __init__(self):" "" \ + " pass" "" \ + "end" \ + [multi_line \ + "TypeError: type 'gdb\\.disassembler\\.DisassemblerResult' is not an acc= eptable base type" \ + "Error while executing Python code\\."] diff --git a/gdb/testsuite/gdb.python/py-disasm.py b/gdb/testsuite/gdb.pyth= on/py-disasm.py index 435a3bf5339..17a7e752935 100644 --- a/gdb/testsuite/gdb.python/py-disasm.py +++ b/gdb/testsuite/gdb.python/py-disasm.py @@ -274,43 +274,6 @@ class ResultOfWrongType(TestDisassembler): return self.Blah(1, "ABC") =20 =20 -class ResultWrapper(gdb.disassembler.DisassemblerResult): - def __init__(self, length, string, length_x=3DNone, string_x=3DNone): - super().__init__(length, string) - if length_x is None: - self.__length =3D length - else: - self.__length =3D length_x - if string_x is None: - self.__string =3D string - else: - self.__string =3D string_x - - @property - def length(self): - return self.__length - - @property - def string(self): - return self.__string - - -class ResultWithInvalidLength(TestDisassembler): - """Return a result object with an invalid length.""" - - def disassemble(self, info): - result =3D gdb.disassembler.builtin_disassemble(info) - return ResultWrapper(result.length, result.string, 0) - - -class ResultWithInvalidString(TestDisassembler): - """Return a result object with an empty string.""" - - def disassemble(self, info): - result =3D gdb.disassembler.builtin_disassemble(info) - return ResultWrapper(result.length, result.string, None, "") - - class TaggingDisassembler(TestDisassembler): """A simple disassembler that just tags the output."""