2 Objective-C runtime wrapper for use by LLDB Python formatters
4 Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 See https://llvm.org/LICENSE.txt for license information.
6 SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9 import lldb
.formatters
.cache
10 import lldb
.formatters
.attrib_fromdict
12 import lldb
.formatters
.Logger
17 def read_ascii(process
, pointer
, max_len
=128):
18 logger
= lldb
.formatters
.Logger
.Logger()
19 error
= lldb
.SBError()
22 content
= process
.ReadCStringFromMemory(pointer
, max_len
, error
)
25 if content
is None or len(content
) == 0 or error
.fail
:
30 def is_valid_pointer(pointer
, pointer_size
, allow_tagged
=0, allow_NULL
=0):
31 logger
= lldb
.formatters
.Logger
.Logger()
36 if allow_tagged
and (pointer
% 2) == 1:
38 return (pointer
% pointer_size
) == 0
40 # Objective-C runtime has a rule that pointers in a class_t will only have bits 0 thru 46 set
41 # so if any pointer has bits 47 thru 63 high we know that this is not a
44 def is_allowed_pointer(pointer
):
45 logger
= lldb
.formatters
.Logger
.Logger()
48 return (pointer
& 0xFFFF800000000000) == 0
51 def read_child_of(valobj
, offset
, type):
52 logger
= lldb
.formatters
.Logger
.Logger()
53 if offset
== 0 and type.GetByteSize() == valobj
.GetByteSize():
54 return valobj
.GetValueAsUnsigned()
55 child
= valobj
.CreateChildAtOffset("childUNK", offset
, type)
56 if child
is None or child
.IsValid() == 0:
58 return child
.GetValueAsUnsigned()
61 def is_valid_identifier(name
):
62 logger
= lldb
.formatters
.Logger
.Logger()
67 # technically, the ObjC runtime does not enforce any rules about what name a class can have
68 # in practice, the commonly used byte values for a class name are the letters, digits and some
69 # symbols: $, %, -, _, .
70 # WARNING: this means that you cannot use this runtime implementation if you need to deal
71 # with class names that use anything but what is allowed here
72 ok_values
= dict.fromkeys(
73 "$%_.-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
75 return all(c
in ok_values
for c
in name
)
78 def check_is_osx_lion(target
):
79 logger
= lldb
.formatters
.Logger
.Logger()
80 # assume the only thing that has a Foundation.framework is a Mac
81 # assume anything < Lion does not even exist
83 mod
= target
.module
["Foundation"]
86 if mod
is None or mod
.IsValid() == 0:
88 ver
= mod
.GetVersion()
89 if ver
is None or ver
== []:
93 # a utility method that factors out code common to almost all the formatters
94 # takes in an SBValue and a metrics object
95 # returns a class_data and a wrapper (or None, if the runtime alone can't
96 # decide on a wrapper)
98 def prepare_class_detection(valobj
, statistics
):
99 logger
= lldb
.formatters
.Logger
.Logger()
100 class_data
= ObjCRuntime(valobj
)
101 if class_data
.is_valid() == 0:
102 statistics
.metric_hit("invalid_pointer", valobj
)
103 wrapper
= InvalidPointer_Description(valobj
.GetValueAsUnsigned(0) == 0)
104 return class_data
, wrapper
105 class_data
= class_data
.read_class_data()
106 if class_data
.is_valid() == 0:
107 statistics
.metric_hit("invalid_isa", valobj
)
108 wrapper
= InvalidISA_Description()
109 return class_data
, wrapper
110 if class_data
.is_kvo():
111 class_data
= class_data
.get_superclass()
112 if class_data
.class_name() == "_NSZombie_OriginalClass":
113 wrapper
= ThisIsZombie_Description()
114 return class_data
, wrapper
115 return class_data
, None
119 def __init__(self
, rot_pointer
, params
):
120 logger
= lldb
.formatters
.Logger
.Logger()
121 if Utilities
.is_valid_pointer(
122 rot_pointer
.GetValueAsUnsigned(), params
.pointer_size
, allow_tagged
=0
124 self
.sys_params
= params
125 self
.valobj
= rot_pointer
126 # self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t)
127 # self.instanceStart = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t)
128 self
.instanceSize
= None # lazy fetching
129 offset
= 24 if self
.sys_params
.is_64_bit
else 16
130 # self.ivarLayoutPtr = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type)
131 self
.namePointer
= Utilities
.read_child_of(
132 self
.valobj
, offset
, self
.sys_params
.types_cache
.addr_ptr_type
134 self
.valid
= 1 # self.check_valid()
136 logger
>> "Marking as invalid - rot is invalid"
139 self
.name
= Utilities
.read_ascii(
140 self
.valobj
.GetTarget().GetProcess(), self
.namePointer
142 if not (Utilities
.is_valid_identifier(self
.name
)):
143 logger
>> "Marking as invalid - name is invalid"
146 # perform sanity checks on the contents of this class_ro_t
147 def check_valid(self
):
149 # misaligned pointers seem to be possible for this field
150 # if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0)):
155 logger
= lldb
.formatters
.Logger
.Logger()
158 + hex(self
.instance_size())
161 + hex(self
.namePointer
)
169 def instance_size(self
, align
=0):
170 logger
= lldb
.formatters
.Logger
.Logger()
171 if self
.is_valid() == 0:
173 if self
.instanceSize
is None:
174 self
.instanceSize
= Utilities
.read_child_of(
175 self
.valobj
, 8, self
.sys_params
.types_cache
.uint32_t
178 unalign
= self
.instance_size(0)
179 if self
.sys_params
.is_64_bit
:
180 return ((unalign
+ 7) & ~
7) % 0x100000000
182 return ((unalign
+ 3) & ~
3) % 0x100000000
184 return self
.instanceSize
188 def __init__(self
, rwt_pointer
, params
):
189 logger
= lldb
.formatters
.Logger
.Logger()
190 if Utilities
.is_valid_pointer(
191 rwt_pointer
.GetValueAsUnsigned(), params
.pointer_size
, allow_tagged
=0
193 self
.sys_params
= params
194 self
.valobj
= rwt_pointer
195 # self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t)
196 # self.version = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t)
197 self
.roPointer
= Utilities
.read_child_of(
198 self
.valobj
, 8, self
.sys_params
.types_cache
.addr_ptr_type
202 logger
>> "Marking as invalid - rwt is invald"
205 self
.rot
= self
.valobj
.CreateValueFromData(
207 lldb
.SBData
.CreateDataFromUInt64Array(
208 self
.sys_params
.endianness
,
209 self
.sys_params
.pointer_size
,
212 self
.sys_params
.types_cache
.addr_ptr_type
,
214 # self.rot = self.valobj.CreateValueFromAddress("rot",self.roPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf()
215 self
.data
= RoT_Data(self
.rot
, self
.sys_params
)
217 # perform sanity checks on the contents of this class_rw_t
218 def check_valid(self
):
219 logger
= lldb
.formatters
.Logger
.Logger()
222 Utilities
.is_valid_pointer(
223 self
.roPointer
, self
.sys_params
.pointer_size
, allow_tagged
=0
226 logger
>> "Marking as invalid - ropointer is invalid"
230 logger
= lldb
.formatters
.Logger
.Logger()
231 return "roPointer = " + hex(self
.roPointer
)
234 logger
= lldb
.formatters
.Logger
.Logger()
236 return self
.data
.is_valid()
241 def __init__(self
, isa_pointer
, params
):
242 logger
= lldb
.formatters
.Logger
.Logger()
243 if (isa_pointer
is not None) and (
244 Utilities
.is_valid_pointer(
245 isa_pointer
.GetValueAsUnsigned(), params
.pointer_size
, allow_tagged
=0
248 self
.sys_params
= params
249 self
.valobj
= isa_pointer
252 logger
>> "Marking as invalid - isa is invalid or None"
255 self
.rwt
= self
.valobj
.CreateValueFromData(
257 lldb
.SBData
.CreateDataFromUInt64Array(
258 self
.sys_params
.endianness
,
259 self
.sys_params
.pointer_size
,
262 self
.sys_params
.types_cache
.addr_ptr_type
,
264 # self.rwt = self.valobj.CreateValueFromAddress("rwt",self.dataPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf()
265 self
.data
= RwT_Data(self
.rwt
, self
.sys_params
)
267 # perform sanity checks on the contents of this class_t
268 # this call tries to minimize the amount of data fetched- as soon as we have "proven"
269 # that we have an invalid object, we stop reading
270 def check_valid(self
):
271 logger
= lldb
.formatters
.Logger
.Logger()
274 self
.isaPointer
= Utilities
.read_child_of(
275 self
.valobj
, 0, self
.sys_params
.types_cache
.addr_ptr_type
278 Utilities
.is_valid_pointer(
279 self
.isaPointer
, self
.sys_params
.pointer_size
, allow_tagged
=0
282 logger
>> "Marking as invalid - isaPointer is invalid"
285 if not (Utilities
.is_allowed_pointer(self
.isaPointer
)):
286 logger
>> "Marking as invalid - isaPointer is not allowed"
290 self
.cachePointer
= Utilities
.read_child_of(
292 2 * self
.sys_params
.pointer_size
,
293 self
.sys_params
.types_cache
.addr_ptr_type
,
296 Utilities
.is_valid_pointer(
297 self
.cachePointer
, self
.sys_params
.pointer_size
, allow_tagged
=0
300 logger
>> "Marking as invalid - cachePointer is invalid"
303 if not (Utilities
.is_allowed_pointer(self
.cachePointer
)):
304 logger
>> "Marking as invalid - cachePointer is not allowed"
307 self
.dataPointer
= Utilities
.read_child_of(
309 4 * self
.sys_params
.pointer_size
,
310 self
.sys_params
.types_cache
.addr_ptr_type
,
313 Utilities
.is_valid_pointer(
314 self
.dataPointer
, self
.sys_params
.pointer_size
, allow_tagged
=0
317 logger
>> "Marking as invalid - dataPointer is invalid"
320 if not (Utilities
.is_allowed_pointer(self
.dataPointer
)):
321 logger
>> "Marking as invalid - dataPointer is not allowed"
325 self
.superclassIsaPointer
= Utilities
.read_child_of(
327 1 * self
.sys_params
.pointer_size
,
328 self
.sys_params
.types_cache
.addr_ptr_type
,
331 Utilities
.is_valid_pointer(
332 self
.superclassIsaPointer
,
333 self
.sys_params
.pointer_size
,
338 logger
>> "Marking as invalid - superclassIsa is invalid"
341 if not (Utilities
.is_allowed_pointer(self
.superclassIsaPointer
)):
342 logger
>> "Marking as invalid - superclassIsa is not allowed"
346 # in general, KVO is implemented by transparently subclassing
347 # however, there could be exceptions where a class does something else
348 # internally to implement the feature - this method will have no clue that a class
349 # has been KVO'ed unless the standard implementation technique is used
351 logger
= lldb
.formatters
.Logger
.Logger()
353 if self
.class_name().startswith("NSKVONotifying_"):
357 # some CF classes have a valid ObjC isa in their CFRuntimeBase
358 # but instead of being class-specific this isa points to a match-'em-all class
359 # which is __NSCFType (the versions without __ also exists and we are matching to it
360 # just to be on the safe side)
362 logger
= lldb
.formatters
.Logger
.Logger()
364 return self
.class_name() == "__NSCFType" or self
.class_name() == "NSCFType"
366 def get_superclass(self
):
367 logger
= lldb
.formatters
.Logger
.Logger()
369 parent_isa_pointer
= self
.valobj
.CreateChildAtOffset(
371 self
.sys_params
.pointer_size
,
372 self
.sys_params
.addr_ptr_type
,
374 return Class_Data_V2(parent_isa_pointer
, self
.sys_params
)
378 def class_name(self
):
379 logger
= lldb
.formatters
.Logger
.Logger()
381 return self
.data
.data
.name
386 logger
= lldb
.formatters
.Logger
.Logger()
388 return self
.data
.is_valid()
392 logger
= lldb
.formatters
.Logger
.Logger()
395 + hex(self
.isaPointer
)
397 + "superclassIsaPointer = "
398 + hex(self
.superclassIsaPointer
)
401 + hex(self
.cachePointer
)
404 + hex(self
.dataPointer
)
410 def instance_size(self
, align
=0):
411 logger
= lldb
.formatters
.Logger
.Logger()
412 if self
.is_valid() == 0:
414 return self
.rwt
.rot
.instance_size(align
)
417 # runtime v1 is much less intricate than v2 and stores relevant
418 # information directly in the class_t object
422 def __init__(self
, isa_pointer
, params
):
423 logger
= lldb
.formatters
.Logger
.Logger()
424 if (isa_pointer
is not None) and (
425 Utilities
.is_valid_pointer(
426 isa_pointer
.GetValueAsUnsigned(), params
.pointer_size
, allow_tagged
=0
430 self
.sys_params
= params
431 self
.valobj
= isa_pointer
434 logger
>> "Marking as invalid - isaPointer is invalid or None"
437 self
.name
= Utilities
.read_ascii(
438 self
.valobj
.GetTarget().GetProcess(), self
.namePointer
440 if not (Utilities
.is_valid_identifier(self
.name
)):
441 logger
>> "Marking as invalid - name is not valid"
444 # perform sanity checks on the contents of this class_t
445 def check_valid(self
):
446 logger
= lldb
.formatters
.Logger
.Logger()
449 self
.isaPointer
= Utilities
.read_child_of(
450 self
.valobj
, 0, self
.sys_params
.types_cache
.addr_ptr_type
453 Utilities
.is_valid_pointer(
454 self
.isaPointer
, self
.sys_params
.pointer_size
, allow_tagged
=0
457 logger
>> "Marking as invalid - isaPointer is invalid"
461 self
.superclassIsaPointer
= Utilities
.read_child_of(
463 1 * self
.sys_params
.pointer_size
,
464 self
.sys_params
.types_cache
.addr_ptr_type
,
467 Utilities
.is_valid_pointer(
468 self
.superclassIsaPointer
,
469 self
.sys_params
.pointer_size
,
474 logger
>> "Marking as invalid - superclassIsa is invalid"
478 self
.namePointer
= Utilities
.read_child_of(
480 2 * self
.sys_params
.pointer_size
,
481 self
.sys_params
.types_cache
.addr_ptr_type
,
483 # if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0,allow_NULL=0)):
487 # in general, KVO is implemented by transparently subclassing
488 # however, there could be exceptions where a class does something else
489 # internally to implement the feature - this method will have no clue that a class
490 # has been KVO'ed unless the standard implementation technique is used
492 logger
= lldb
.formatters
.Logger
.Logger()
494 if self
.class_name().startswith("NSKVONotifying_"):
498 # some CF classes have a valid ObjC isa in their CFRuntimeBase
499 # but instead of being class-specific this isa points to a match-'em-all class
500 # which is __NSCFType (the versions without __ also exists and we are matching to it
501 # just to be on the safe side)
503 logger
= lldb
.formatters
.Logger
.Logger()
505 return self
.class_name() == "__NSCFType" or self
.class_name() == "NSCFType"
507 def get_superclass(self
):
508 logger
= lldb
.formatters
.Logger
.Logger()
510 parent_isa_pointer
= self
.valobj
.CreateChildAtOffset(
512 self
.sys_params
.pointer_size
,
513 self
.sys_params
.addr_ptr_type
,
515 return Class_Data_V1(parent_isa_pointer
, self
.sys_params
)
519 def class_name(self
):
520 logger
= lldb
.formatters
.Logger
.Logger()
530 logger
= lldb
.formatters
.Logger
.Logger()
533 + hex(self
.isaPointer
)
535 + "superclassIsaPointer = "
536 + hex(self
.superclassIsaPointer
)
539 + hex(self
.namePointer
)
543 + hex(self
.instanceSize())
550 def instance_size(self
, align
=0):
551 logger
= lldb
.formatters
.Logger
.Logger()
552 if self
.is_valid() == 0:
554 if self
.instanceSize
is None:
555 self
.instanceSize
= Utilities
.read_child_of(
557 5 * self
.sys_params
.pointer_size
,
558 self
.sys_params
.types_cache
.addr_ptr_type
,
561 unalign
= self
.instance_size(0)
562 if self
.sys_params
.is_64_bit
:
563 return ((unalign
+ 7) & ~
7) % 0x100000000
565 return ((unalign
+ 3) & ~
3) % 0x100000000
567 return self
.instanceSize
570 # these are the only tagged pointers values for current versions
571 # of OSX - they might change in future OS releases, and no-one is
572 # advised to rely on these values, or any of the bitmasking formulas
573 # in TaggedClass_Data. doing otherwise is at your own risk
574 TaggedClass_Values_Lion
= {
576 5: "NSManagedObject",
580 TaggedClass_Values_NMOS
= {
584 5: "NSManagedObject",
589 class TaggedClass_Data
:
590 def __init__(self
, pointer
, params
):
591 logger
= lldb
.formatters
.Logger
.Logger()
592 global TaggedClass_Values_Lion
, TaggedClass_Values_NMOS
595 self
.sys_params
= params
596 self
.valobj
= pointer
597 self
.val
= (pointer
& ~
0x0000000000000000FF) >> 8
598 self
.class_bits
= (pointer
& 0xE) >> 1
599 self
.i_bits
= (pointer
& 0xF0) >> 4
601 if self
.sys_params
.is_lion
:
602 if self
.class_bits
in TaggedClass_Values_Lion
:
603 self
.name
= TaggedClass_Values_Lion
[self
.class_bits
]
605 logger
>> "Marking as invalid - not a good tagged pointer for Lion"
608 if self
.class_bits
in TaggedClass_Values_NMOS
:
609 self
.name
= TaggedClass_Values_NMOS
[self
.class_bits
]
611 logger
>> "Marking as invalid - not a good tagged pointer for NMOS"
617 def class_name(self
):
618 logger
= lldb
.formatters
.Logger
.Logger()
625 return self
.val
if self
.is_valid() else None
628 return self
.i_bits
if self
.is_valid() else None
636 # we would need to go around looking for the superclass or ask the runtime
637 # for now, we seem not to require support for this operation so we will merrily
638 # pretend to be at a root point in the hierarchy
639 def get_superclass(self
):
642 # anything that is handled here is tagged
646 # it seems reasonable to say that a tagged pointer is the size of a pointer
647 def instance_size(self
, align
=0):
648 logger
= lldb
.formatters
.Logger
.Logger()
649 if self
.is_valid() == 0:
651 return self
.sys_params
.pointer_size
654 class InvalidClass_Data
:
663 def __init__(self
, major
, minor
, release
, build_string
):
666 self
._release
= release
667 self
._build
_string
= build_string
675 def get_release(self
):
678 def get_build_string(self
):
679 return self
._build
_string
681 major
= property(get_major
, None)
682 minor
= property(get_minor
, None)
683 release
= property(get_release
, None)
684 build_string
= property(get_build_string
, None)
686 def __lt__(self
, other
):
687 if self
.major
< other
.major
:
689 if self
.minor
< other
.minor
:
691 if self
.release
< other
.release
:
693 # build strings are not compared since they are heavily platform-dependent and might not always
697 def __eq__(self
, other
):
699 (self
.major
== other
.major
)
700 and (self
.minor
== other
.minor
)
701 and (self
.release
== other
.release
)
702 and (self
.build_string
== other
.build_string
)
705 # Python 2.6 doesn't have functools.total_ordering, so we have to implement
707 def __gt__(self
, other
):
710 def __le__(self
, other
):
711 return not other
< self
713 def __ge__(self
, other
):
714 return not self
< other
717 runtime_version
= lldb
.formatters
.cache
.Cache()
718 os_version
= lldb
.formatters
.cache
.Cache()
719 types_caches
= lldb
.formatters
.cache
.Cache()
720 isa_caches
= lldb
.formatters
.cache
.Cache()
723 class SystemParameters
:
724 def __init__(self
, valobj
):
725 logger
= lldb
.formatters
.Logger
.Logger()
726 self
.adjust_for_architecture(valobj
)
727 self
.adjust_for_process(valobj
)
729 def adjust_for_process(self
, valobj
):
730 logger
= lldb
.formatters
.Logger
.Logger()
731 global runtime_version
736 process
= valobj
.GetTarget().GetProcess()
737 # using the unique ID for added guarantees (see svn revision 172628 for
739 self
.pid
= process
.GetUniqueID()
741 if runtime_version
.look_for_key(self
.pid
):
742 self
.runtime_version
= runtime_version
.get_value(self
.pid
)
744 self
.runtime_version
= ObjCRuntime
.runtime_version(process
)
745 runtime_version
.add_item(self
.pid
, self
.runtime_version
)
747 if os_version
.look_for_key(self
.pid
):
748 self
.is_lion
= os_version
.get_value(self
.pid
)
750 self
.is_lion
= Utilities
.check_is_osx_lion(valobj
.GetTarget())
751 os_version
.add_item(self
.pid
, self
.is_lion
)
753 if types_caches
.look_for_key(self
.pid
):
754 self
.types_cache
= types_caches
.get_value(self
.pid
)
756 self
.types_cache
= lldb
.formatters
.attrib_fromdict
.AttributesDictionary(
759 self
.types_cache
.addr_type
= valobj
.GetType().GetBasicType(
760 lldb
.eBasicTypeUnsignedLong
762 self
.types_cache
.addr_ptr_type
= self
.types_cache
.addr_type
.GetPointerType()
763 self
.types_cache
.uint32_t
= valobj
.GetType().GetBasicType(
764 lldb
.eBasicTypeUnsignedInt
766 types_caches
.add_item(self
.pid
, self
.types_cache
)
768 if isa_caches
.look_for_key(self
.pid
):
769 self
.isa_cache
= isa_caches
.get_value(self
.pid
)
771 self
.isa_cache
= lldb
.formatters
.cache
.Cache()
772 isa_caches
.add_item(self
.pid
, self
.isa_cache
)
774 def adjust_for_architecture(self
, valobj
):
775 process
= valobj
.GetTarget().GetProcess()
776 self
.pointer_size
= process
.GetAddressByteSize()
777 self
.is_64_bit
= self
.pointer_size
== 8
778 self
.endianness
= process
.GetByteOrder()
779 self
.is_little
= self
.endianness
== lldb
.eByteOrderLittle
780 self
.cfruntime_size
= 16 if self
.is_64_bit
else 8
782 # a simple helper function that makes it more explicit that one is calculating
783 # an offset that is made up of X pointers and Y bytes of additional data
784 # taking into account pointer size - if you know there is going to be some padding
785 # you can pass that in and it will be taken into account (since padding may be different between
786 # 32 and 64 bit versions, you can pass padding value for both, the right
788 def calculate_offset(self
, num_pointers
=0, bytes_count
=0, padding32
=0, padding64
=0):
789 value
= bytes_count
+ num_pointers
* self
.pointer_size
790 return value
+ padding64
if self
.is_64_bit
else value
+ padding32
794 # the ObjC runtime has no explicit "version" field that we can use
795 # instead, we discriminate v1 from v2 by looking for the presence
796 # of a well-known section only present in v1
798 def runtime_version(process
):
799 logger
= lldb
.formatters
.Logger
.Logger()
800 if process
.IsValid() == 0:
801 logger
>> "No process - bailing out"
803 target
= process
.GetTarget()
804 num_modules
= target
.GetNumModules()
806 for idx
in range(num_modules
):
807 module
= target
.GetModuleAtIndex(idx
)
808 if module
.GetFileSpec().GetFilename() == "libobjc.A.dylib":
811 if module_objc
is None or module_objc
.IsValid() == 0:
812 logger
>> "no libobjc - bailing out"
814 num_sections
= module
.GetNumSections()
816 for idx
in range(num_sections
):
817 section
= module
.GetSectionAtIndex(idx
)
818 if section
.GetName() == "__OBJC":
819 section_objc
= section
821 if section_objc
is not None and section_objc
.IsValid():
822 logger
>> "found __OBJC: v1"
824 logger
>> "no __OBJC: v2"
828 def runtime_from_isa(isa
):
829 logger
= lldb
.formatters
.Logger
.Logger()
830 runtime
= ObjCRuntime(isa
)
834 def __init__(self
, valobj
):
835 logger
= lldb
.formatters
.Logger
.Logger()
837 self
.adjust_for_architecture()
838 self
.sys_params
= SystemParameters(self
.valobj
)
839 self
.unsigned_value
= self
.valobj
.GetValueAsUnsigned()
840 self
.isa_value
= None
842 def adjust_for_architecture(self
):
845 # an ObjC pointer can either be tagged or must be aligned
847 logger
= lldb
.formatters
.Logger
.Logger()
848 if self
.valobj
is None:
850 return Utilities
.is_valid_pointer(
851 self
.unsigned_value
, self
.sys_params
.pointer_size
, allow_tagged
=1
853 Utilities
.is_valid_pointer(
854 self
.unsigned_value
, self
.sys_params
.pointer_size
, allow_tagged
=0
859 logger
= lldb
.formatters
.Logger
.Logger()
860 if self
.valobj
is None:
862 if self
.valobj
.IsInScope() == 0:
864 return Utilities
.is_valid_pointer(
865 self
.unsigned_value
, self
.sys_params
.pointer_size
, allow_tagged
=1
869 return self
.unsigned_value
== 0
872 logger
= lldb
.formatters
.Logger
.Logger()
873 if self
.isa_value
is not None:
874 logger
>> "using cached isa"
875 return self
.isa_value
876 self
.isa_pointer
= self
.valobj
.CreateChildAtOffset(
877 "cfisa", 0, self
.sys_params
.types_cache
.addr_ptr_type
879 if self
.isa_pointer
is None or self
.isa_pointer
.IsValid() == 0:
880 logger
>> "invalid isa - bailing out"
882 self
.isa_value
= self
.isa_pointer
.GetValueAsUnsigned(1)
883 if self
.isa_value
== 1:
884 logger
>> "invalid isa value - bailing out"
888 def read_class_data(self
):
889 logger
= lldb
.formatters
.Logger
.Logger()
892 # tagged pointers only exist in ObjC v2
893 if self
.sys_params
.runtime_version
== 2:
894 logger
>> "on v2 and tagged - maybe"
895 # not every odd-valued pointer is actually tagged. most are just plain wrong
896 # we could try and predetect this before even creating a TaggedClass_Data object
897 # but unless performance requires it, this seems a cleaner way
899 tentative_tagged
= TaggedClass_Data(
900 self
.unsigned_value
, self
.sys_params
902 if tentative_tagged
.is_valid():
903 logger
>> "truly tagged"
904 return tentative_tagged
906 logger
>> "not tagged - error"
907 return InvalidClass_Data()
909 logger
>> "on v1 and tagged - error"
910 return InvalidClass_Data()
911 if self
.is_valid() == 0 or self
.read_isa() is None:
912 return InvalidClass_Data()
913 data
= self
.sys_params
.isa_cache
.get_value(self
.isa_value
, default
=None)
916 if self
.sys_params
.runtime_version
== 2:
917 data
= Class_Data_V2(self
.isa_pointer
, self
.sys_params
)
919 data
= Class_Data_V1(self
.isa_pointer
, self
.sys_params
)
921 return InvalidClass_Data()
923 self
.sys_params
.isa_cache
.add_item(self
.isa_value
, data
, ok_to_replace
=1)
927 # these classes below can be used by the data formatters to provide a
928 # consistent message that describes a given runtime-generated situation
931 class SpecialSituation_Description
:
936 class InvalidPointer_Description(SpecialSituation_Description
):
937 def __init__(self
, nil
):
944 return "<invalid pointer>"
947 class InvalidISA_Description(SpecialSituation_Description
):
952 return "<not an Objective-C object>"
955 class ThisIsZombie_Description(SpecialSituation_Description
):
957 return "<freed object>"