From ca9fe330d4767abc38461c2d24bdd0605c8f5e48 Mon Sep 17 00:00:00 2001 From: Roman Podoliaka Date: Sun, 17 Nov 2024 17:42:08 +0000 Subject: [PATCH] cpython3.11: fix pretty-printing of UserDict, UserString, UserList https://github.com/python/cpython/pull/29879 changed the layout of user classes, and the code is no longer able to find the location of __dict__. Do a quick fix for now, as we don't support pretty-printing user defined classes. --- cpython_lldb.py | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/cpython_lldb.py b/cpython_lldb.py index 1268aaf..92b35e1 100644 --- a/cpython_lldb.py +++ b/cpython_lldb.py @@ -426,24 +426,45 @@ def value(self): class _CollectionsUserObject(object): @property def value(self): + # UserDict, UserString, and UserList all have a single instance variable + # called "data", which is the collection used for storing the elements. + # As usual, that instance variable is stored in __dict__, so we need to + # find the location of that dict object first and look up the key. + dict_offset = ( self.lldb_value.GetChildMemberWithName("ob_type") .GetChildMemberWithName("tp_dictoffset") - .unsigned + .signed ) + if dict_offset > 0: + # CPython < 3.11: __dict__ is located $tp_dictoffset bytes after the + # start of the common PyObject header. + object_type = self.target.FindFirstType("PyObject") + address = lldb.SBAddress( + int(self.lldb_value.value, 16) + dict_offset, self.target + ) + value = self.target.CreateValueFromAddress( + "value", address, object_type.GetPointerType() + ) - object_type = self.target.FindFirstType("PyObject") - address = lldb.SBAddress( - int(self.lldb_value.value, 16) + dict_offset, self.target - ) - value = self.target.CreateValueFromAddress( - "value", address, object_type.GetPointerType() - ) + return next( + v for k, v in PyDictObject(value).value.items() if k.value == "data" + ) + else: + # CPython >= 3.11: &__dict__ is always stored at a fixed offset + # before the start of the common PyObject header. + object_type = self.target.FindFirstType("PyDictValues") + address = lldb.SBAddress(int(self.lldb_value.value, 16) - 32, self.target) + value = self.target.CreateValueFromAddress( + "value", address, object_type.GetPointerType() + ) - # "data" is the real object used to store the contents of the class - return next( - v for k, v in PyDictObject(value).value.items() if k.value == "data" - ) + # TODO: This only works because there is only one instance variable + # called "data" right now. We need to solve the generic case and + # implement iteration over PyDictValues entries. + return PyObject.from_value( + value.deref.GetChildMemberWithName("values").GetChildAtIndex(0) + ) class UserDict(_CollectionsUserObject, PyObject):