@@ -426,24 +426,45 @@ def value(self):
426426class _CollectionsUserObject (object ):
427427 @property
428428 def value (self ):
429+ # UserDict, UserString, and UserList all have a single instance variable
430+ # called "data", which is the collection used for storing the elements.
431+ # As usual, that instance variable is stored in __dict__, so we need to
432+ # find the location of that dict object first and look up the key.
433+
429434 dict_offset = (
430435 self .lldb_value .GetChildMemberWithName ("ob_type" )
431436 .GetChildMemberWithName ("tp_dictoffset" )
432- .unsigned
437+ .signed
433438 )
439+ if dict_offset > 0 :
440+ # CPython < 3.11: __dict__ is located $tp_dictoffset bytes after the
441+ # start of the common PyObject header.
442+ object_type = self .target .FindFirstType ("PyObject" )
443+ address = lldb .SBAddress (
444+ int (self .lldb_value .value , 16 ) + dict_offset , self .target
445+ )
446+ value = self .target .CreateValueFromAddress (
447+ "value" , address , object_type .GetPointerType ()
448+ )
434449
435- object_type = self .target .FindFirstType ("PyObject" )
436- address = lldb .SBAddress (
437- int (self .lldb_value .value , 16 ) + dict_offset , self .target
438- )
439- value = self .target .CreateValueFromAddress (
440- "value" , address , object_type .GetPointerType ()
441- )
450+ return next (
451+ v for k , v in PyDictObject (value ).value .items () if k .value == "data"
452+ )
453+ else :
454+ # CPython >= 3.11: &__dict__ is always stored at a fixed offset
455+ # before the start of the common PyObject header.
456+ object_type = self .target .FindFirstType ("PyDictValues" )
457+ address = lldb .SBAddress (int (self .lldb_value .value , 16 ) - 32 , self .target )
458+ value = self .target .CreateValueFromAddress (
459+ "value" , address , object_type .GetPointerType ()
460+ )
442461
443- # "data" is the real object used to store the contents of the class
444- return next (
445- v for k , v in PyDictObject (value ).value .items () if k .value == "data"
446- )
462+ # TODO: This only works because there is only one instance variable
463+ # called "data" right now. We need to solve the generic case and
464+ # implement iteration over PyDictValues entries.
465+ return PyObject .from_value (
466+ value .deref .GetChildMemberWithName ("values" ).GetChildAtIndex (0 )
467+ )
447468
448469
449470class UserDict (_CollectionsUserObject , PyObject ):
0 commit comments