@@ -426,24 +426,46 @@ def value(self):
426
426
class _CollectionsUserObject (object ):
427
427
@property
428
428
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
+
429
434
dict_offset = (
430
435
self .lldb_value .GetChildMemberWithName ("ob_type" )
431
436
.GetChildMemberWithName ("tp_dictoffset" )
432
- .unsigned
437
+ .signed
433
438
)
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
+ )
434
449
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
+ # "data" is the application level collection that stores the items
451
+ return next (
452
+ v for k , v in PyDictObject (value ).value .items () if k .value == "data"
453
+ )
454
+ else :
455
+ # CPython >= 3.11: &__dict__ is always stored at a fixed offset
456
+ # before the start of the common PyObject header.
457
+ object_type = self .target .FindFirstType ("PyDictValues" )
458
+ address = lldb .SBAddress (int (self .lldb_value .value , 16 ) - 32 , self .target )
459
+ value = self .target .CreateValueFromAddress (
460
+ "value" , address , object_type .GetPointerType ()
461
+ )
442
462
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
- )
463
+ # TODO: This only works because there is only instance variable called "data"
464
+ # right now. We need to solve the generic case and implement iteration over
465
+ # PyDictValues entries.
466
+ return PyObject .from_value (
467
+ value .deref .GetChildMemberWithName ("values" ).GetChildAtIndex (0 )
468
+ )
447
469
448
470
449
471
class UserDict (_CollectionsUserObject , PyObject ):
0 commit comments