xref: /openbsd-src/gnu/llvm/lldb/examples/summaries/cocoa/objc_runtime.py (revision 061da546b983eb767bad15e67af1174fb0bcf31c)
1*061da546Spatrick"""
2*061da546SpatrickObjective-C runtime wrapper for use by LLDB Python formatters
3*061da546Spatrick
4*061da546SpatrickPart of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5*061da546SpatrickSee https://llvm.org/LICENSE.txt for license information.
6*061da546SpatrickSPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7*061da546Spatrick"""
8*061da546Spatrickimport lldb
9*061da546Spatrickimport lldb.formatters.cache
10*061da546Spatrickimport lldb.formatters.attrib_fromdict
11*061da546Spatrickimport functools
12*061da546Spatrickimport lldb.formatters.Logger
13*061da546Spatrick
14*061da546Spatrick
15*061da546Spatrickclass Utilities:
16*061da546Spatrick
17*061da546Spatrick    @staticmethod
18*061da546Spatrick    def read_ascii(process, pointer, max_len=128):
19*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
20*061da546Spatrick        error = lldb.SBError()
21*061da546Spatrick        content = None
22*061da546Spatrick        try:
23*061da546Spatrick            content = process.ReadCStringFromMemory(pointer, max_len, error)
24*061da546Spatrick        except:
25*061da546Spatrick            pass
26*061da546Spatrick        if content is None or len(content) == 0 or error.fail:
27*061da546Spatrick            return None
28*061da546Spatrick        return content
29*061da546Spatrick
30*061da546Spatrick    @staticmethod
31*061da546Spatrick    def is_valid_pointer(pointer, pointer_size, allow_tagged=0, allow_NULL=0):
32*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
33*061da546Spatrick        if pointer is None:
34*061da546Spatrick            return 0
35*061da546Spatrick        if pointer == 0:
36*061da546Spatrick            return allow_NULL
37*061da546Spatrick        if allow_tagged and (pointer % 2) == 1:
38*061da546Spatrick            return 1
39*061da546Spatrick        return ((pointer % pointer_size) == 0)
40*061da546Spatrick
41*061da546Spatrick    # Objective-C runtime has a rule that pointers in a class_t will only have bits 0 thru 46 set
42*061da546Spatrick    # so if any pointer has bits 47 thru 63 high we know that this is not a
43*061da546Spatrick    # valid isa
44*061da546Spatrick    @staticmethod
45*061da546Spatrick    def is_allowed_pointer(pointer):
46*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
47*061da546Spatrick        if pointer is None:
48*061da546Spatrick            return 0
49*061da546Spatrick        return ((pointer & 0xFFFF800000000000) == 0)
50*061da546Spatrick
51*061da546Spatrick    @staticmethod
52*061da546Spatrick    def read_child_of(valobj, offset, type):
53*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
54*061da546Spatrick        if offset == 0 and type.GetByteSize() == valobj.GetByteSize():
55*061da546Spatrick            return valobj.GetValueAsUnsigned()
56*061da546Spatrick        child = valobj.CreateChildAtOffset("childUNK", offset, type)
57*061da546Spatrick        if child is None or child.IsValid() == 0:
58*061da546Spatrick            return None
59*061da546Spatrick        return child.GetValueAsUnsigned()
60*061da546Spatrick
61*061da546Spatrick    @staticmethod
62*061da546Spatrick    def is_valid_identifier(name):
63*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
64*061da546Spatrick        if name is None:
65*061da546Spatrick            return None
66*061da546Spatrick        if len(name) == 0:
67*061da546Spatrick            return None
68*061da546Spatrick        # technically, the ObjC runtime does not enforce any rules about what name a class can have
69*061da546Spatrick        # in practice, the commonly used byte values for a class name are the letters, digits and some
70*061da546Spatrick        # symbols: $, %, -, _, .
71*061da546Spatrick        # WARNING: this means that you cannot use this runtime implementation if you need to deal
72*061da546Spatrick        # with class names that use anything but what is allowed here
73*061da546Spatrick        ok_values = dict.fromkeys(
74*061da546Spatrick            "$%_.-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890")
75*061da546Spatrick        return all(c in ok_values for c in name)
76*061da546Spatrick
77*061da546Spatrick    @staticmethod
78*061da546Spatrick    def check_is_osx_lion(target):
79*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
80*061da546Spatrick        # assume the only thing that has a Foundation.framework is a Mac
81*061da546Spatrick        # assume anything < Lion does not even exist
82*061da546Spatrick        try:
83*061da546Spatrick            mod = target.module['Foundation']
84*061da546Spatrick        except:
85*061da546Spatrick            mod = None
86*061da546Spatrick        if mod is None or mod.IsValid() == 0:
87*061da546Spatrick            return None
88*061da546Spatrick        ver = mod.GetVersion()
89*061da546Spatrick        if ver is None or ver == []:
90*061da546Spatrick            return None
91*061da546Spatrick        return (ver[0] < 900)
92*061da546Spatrick
93*061da546Spatrick    # a utility method that factors out code common to almost all the formatters
94*061da546Spatrick    # takes in an SBValue and a metrics object
95*061da546Spatrick    # returns a class_data and a wrapper (or None, if the runtime alone can't
96*061da546Spatrick    # decide on a wrapper)
97*061da546Spatrick    @staticmethod
98*061da546Spatrick    def prepare_class_detection(valobj, statistics):
99*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
100*061da546Spatrick        class_data = ObjCRuntime(valobj)
101*061da546Spatrick        if class_data.is_valid() == 0:
102*061da546Spatrick            statistics.metric_hit('invalid_pointer', valobj)
103*061da546Spatrick            wrapper = InvalidPointer_Description(
104*061da546Spatrick                valobj.GetValueAsUnsigned(0) == 0)
105*061da546Spatrick            return class_data, wrapper
106*061da546Spatrick        class_data = class_data.read_class_data()
107*061da546Spatrick        if class_data.is_valid() == 0:
108*061da546Spatrick            statistics.metric_hit('invalid_isa', valobj)
109*061da546Spatrick            wrapper = InvalidISA_Description()
110*061da546Spatrick            return class_data, wrapper
111*061da546Spatrick        if class_data.is_kvo():
112*061da546Spatrick            class_data = class_data.get_superclass()
113*061da546Spatrick        if class_data.class_name() == '_NSZombie_OriginalClass':
114*061da546Spatrick            wrapper = ThisIsZombie_Description()
115*061da546Spatrick            return class_data, wrapper
116*061da546Spatrick        return class_data, None
117*061da546Spatrick
118*061da546Spatrick
119*061da546Spatrickclass RoT_Data:
120*061da546Spatrick
121*061da546Spatrick    def __init__(self, rot_pointer, params):
122*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
123*061da546Spatrick        if (Utilities.is_valid_pointer(rot_pointer.GetValueAsUnsigned(),
124*061da546Spatrick                                       params.pointer_size, allow_tagged=0)):
125*061da546Spatrick            self.sys_params = params
126*061da546Spatrick            self.valobj = rot_pointer
127*061da546Spatrick            #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t)
128*061da546Spatrick            #self.instanceStart = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t)
129*061da546Spatrick            self.instanceSize = None  # lazy fetching
130*061da546Spatrick            offset = 24 if self.sys_params.is_64_bit else 16
131*061da546Spatrick            #self.ivarLayoutPtr = Utilities.read_child_of(self.valobj,offset,self.sys_params.addr_ptr_type)
132*061da546Spatrick            self.namePointer = Utilities.read_child_of(
133*061da546Spatrick                self.valobj, offset, self.sys_params.types_cache.addr_ptr_type)
134*061da546Spatrick            self.valid = 1  # self.check_valid()
135*061da546Spatrick        else:
136*061da546Spatrick            logger >> "Marking as invalid - rot is invalid"
137*061da546Spatrick            self.valid = 0
138*061da546Spatrick        if self.valid:
139*061da546Spatrick            self.name = Utilities.read_ascii(
140*061da546Spatrick                self.valobj.GetTarget().GetProcess(), self.namePointer)
141*061da546Spatrick            if not(Utilities.is_valid_identifier(self.name)):
142*061da546Spatrick                logger >> "Marking as invalid - name is invalid"
143*061da546Spatrick                self.valid = 0
144*061da546Spatrick
145*061da546Spatrick    # perform sanity checks on the contents of this class_ro_t
146*061da546Spatrick    def check_valid(self):
147*061da546Spatrick        self.valid = 1
148*061da546Spatrick        # misaligned pointers seem to be possible for this field
149*061da546Spatrick        # if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0)):
150*061da546Spatrick        #	self.valid = 0
151*061da546Spatrick        #	pass
152*061da546Spatrick
153*061da546Spatrick    def __str__(self):
154*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
155*061da546Spatrick        return \
156*061da546Spatrick            "instanceSize = " + hex(self.instance_size()) + "\n" + \
157*061da546Spatrick            "namePointer = " + hex(self.namePointer) + " --> " + self.name
158*061da546Spatrick
159*061da546Spatrick    def is_valid(self):
160*061da546Spatrick        return self.valid
161*061da546Spatrick
162*061da546Spatrick    def instance_size(self, align=0):
163*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
164*061da546Spatrick        if self.is_valid() == 0:
165*061da546Spatrick            return None
166*061da546Spatrick        if self.instanceSize is None:
167*061da546Spatrick            self.instanceSize = Utilities.read_child_of(
168*061da546Spatrick                self.valobj, 8, self.sys_params.types_cache.uint32_t)
169*061da546Spatrick        if align:
170*061da546Spatrick            unalign = self.instance_size(0)
171*061da546Spatrick            if self.sys_params.is_64_bit:
172*061da546Spatrick                return ((unalign + 7) & ~7) % 0x100000000
173*061da546Spatrick            else:
174*061da546Spatrick                return ((unalign + 3) & ~3) % 0x100000000
175*061da546Spatrick        else:
176*061da546Spatrick            return self.instanceSize
177*061da546Spatrick
178*061da546Spatrick
179*061da546Spatrickclass RwT_Data:
180*061da546Spatrick
181*061da546Spatrick    def __init__(self, rwt_pointer, params):
182*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
183*061da546Spatrick        if (Utilities.is_valid_pointer(rwt_pointer.GetValueAsUnsigned(),
184*061da546Spatrick                                       params.pointer_size, allow_tagged=0)):
185*061da546Spatrick            self.sys_params = params
186*061da546Spatrick            self.valobj = rwt_pointer
187*061da546Spatrick            #self.flags = Utilities.read_child_of(self.valobj,0,self.sys_params.uint32_t)
188*061da546Spatrick            #self.version = Utilities.read_child_of(self.valobj,4,self.sys_params.uint32_t)
189*061da546Spatrick            self.roPointer = Utilities.read_child_of(
190*061da546Spatrick                self.valobj, 8, self.sys_params.types_cache.addr_ptr_type)
191*061da546Spatrick            self.check_valid()
192*061da546Spatrick        else:
193*061da546Spatrick            logger >> "Marking as invalid - rwt is invald"
194*061da546Spatrick            self.valid = 0
195*061da546Spatrick        if self.valid:
196*061da546Spatrick            self.rot = self.valobj.CreateValueFromData(
197*061da546Spatrick                "rot", lldb.SBData.CreateDataFromUInt64Array(
198*061da546Spatrick                    self.sys_params.endianness, self.sys_params.pointer_size, [
199*061da546Spatrick                        self.roPointer]), self.sys_params.types_cache.addr_ptr_type)
200*061da546Spatrick#			self.rot = self.valobj.CreateValueFromAddress("rot",self.roPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf()
201*061da546Spatrick            self.data = RoT_Data(self.rot, self.sys_params)
202*061da546Spatrick
203*061da546Spatrick    # perform sanity checks on the contents of this class_rw_t
204*061da546Spatrick    def check_valid(self):
205*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
206*061da546Spatrick        self.valid = 1
207*061da546Spatrick        if not(
208*061da546Spatrick            Utilities.is_valid_pointer(
209*061da546Spatrick                self.roPointer,
210*061da546Spatrick                self.sys_params.pointer_size,
211*061da546Spatrick                allow_tagged=0)):
212*061da546Spatrick            logger >> "Marking as invalid - ropointer is invalid"
213*061da546Spatrick            self.valid = 0
214*061da546Spatrick
215*061da546Spatrick    def __str__(self):
216*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
217*061da546Spatrick        return \
218*061da546Spatrick            "roPointer = " + hex(self.roPointer)
219*061da546Spatrick
220*061da546Spatrick    def is_valid(self):
221*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
222*061da546Spatrick        if self.valid:
223*061da546Spatrick            return self.data.is_valid()
224*061da546Spatrick        return 0
225*061da546Spatrick
226*061da546Spatrick
227*061da546Spatrickclass Class_Data_V2:
228*061da546Spatrick
229*061da546Spatrick    def __init__(self, isa_pointer, params):
230*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
231*061da546Spatrick        if (isa_pointer is not None) and (Utilities.is_valid_pointer(
232*061da546Spatrick                isa_pointer.GetValueAsUnsigned(), params.pointer_size, allow_tagged=0)):
233*061da546Spatrick            self.sys_params = params
234*061da546Spatrick            self.valobj = isa_pointer
235*061da546Spatrick            self.check_valid()
236*061da546Spatrick        else:
237*061da546Spatrick            logger >> "Marking as invalid - isa is invalid or None"
238*061da546Spatrick            self.valid = 0
239*061da546Spatrick        if self.valid:
240*061da546Spatrick            self.rwt = self.valobj.CreateValueFromData(
241*061da546Spatrick                "rwt", lldb.SBData.CreateDataFromUInt64Array(
242*061da546Spatrick                    self.sys_params.endianness, self.sys_params.pointer_size, [
243*061da546Spatrick                        self.dataPointer]), self.sys_params.types_cache.addr_ptr_type)
244*061da546Spatrick#			self.rwt = self.valobj.CreateValueFromAddress("rwt",self.dataPointer,self.sys_params.types_cache.addr_ptr_type).AddressOf()
245*061da546Spatrick            self.data = RwT_Data(self.rwt, self.sys_params)
246*061da546Spatrick
247*061da546Spatrick    # perform sanity checks on the contents of this class_t
248*061da546Spatrick    # this call tries to minimize the amount of data fetched- as soon as we have "proven"
249*061da546Spatrick    # that we have an invalid object, we stop reading
250*061da546Spatrick    def check_valid(self):
251*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
252*061da546Spatrick        self.valid = 1
253*061da546Spatrick
254*061da546Spatrick        self.isaPointer = Utilities.read_child_of(
255*061da546Spatrick            self.valobj, 0, self.sys_params.types_cache.addr_ptr_type)
256*061da546Spatrick        if not(
257*061da546Spatrick            Utilities.is_valid_pointer(
258*061da546Spatrick                self.isaPointer,
259*061da546Spatrick                self.sys_params.pointer_size,
260*061da546Spatrick                allow_tagged=0)):
261*061da546Spatrick            logger >> "Marking as invalid - isaPointer is invalid"
262*061da546Spatrick            self.valid = 0
263*061da546Spatrick            return
264*061da546Spatrick        if not(Utilities.is_allowed_pointer(self.isaPointer)):
265*061da546Spatrick            logger >> "Marking as invalid - isaPointer is not allowed"
266*061da546Spatrick            self.valid = 0
267*061da546Spatrick            return
268*061da546Spatrick
269*061da546Spatrick        self.cachePointer = Utilities.read_child_of(
270*061da546Spatrick            self.valobj,
271*061da546Spatrick            2 * self.sys_params.pointer_size,
272*061da546Spatrick            self.sys_params.types_cache.addr_ptr_type)
273*061da546Spatrick        if not(
274*061da546Spatrick            Utilities.is_valid_pointer(
275*061da546Spatrick                self.cachePointer,
276*061da546Spatrick                self.sys_params.pointer_size,
277*061da546Spatrick                allow_tagged=0)):
278*061da546Spatrick            logger >> "Marking as invalid - cachePointer is invalid"
279*061da546Spatrick            self.valid = 0
280*061da546Spatrick            return
281*061da546Spatrick        if not(Utilities.is_allowed_pointer(self.cachePointer)):
282*061da546Spatrick            logger >> "Marking as invalid - cachePointer is not allowed"
283*061da546Spatrick            self.valid = 0
284*061da546Spatrick            return
285*061da546Spatrick        self.dataPointer = Utilities.read_child_of(
286*061da546Spatrick            self.valobj,
287*061da546Spatrick            4 * self.sys_params.pointer_size,
288*061da546Spatrick            self.sys_params.types_cache.addr_ptr_type)
289*061da546Spatrick        if not(
290*061da546Spatrick            Utilities.is_valid_pointer(
291*061da546Spatrick                self.dataPointer,
292*061da546Spatrick                self.sys_params.pointer_size,
293*061da546Spatrick                allow_tagged=0)):
294*061da546Spatrick            logger >> "Marking as invalid - dataPointer is invalid"
295*061da546Spatrick            self.valid = 0
296*061da546Spatrick            return
297*061da546Spatrick        if not(Utilities.is_allowed_pointer(self.dataPointer)):
298*061da546Spatrick            logger >> "Marking as invalid - dataPointer is not allowed"
299*061da546Spatrick            self.valid = 0
300*061da546Spatrick            return
301*061da546Spatrick
302*061da546Spatrick        self.superclassIsaPointer = Utilities.read_child_of(
303*061da546Spatrick            self.valobj,
304*061da546Spatrick            1 * self.sys_params.pointer_size,
305*061da546Spatrick            self.sys_params.types_cache.addr_ptr_type)
306*061da546Spatrick        if not(
307*061da546Spatrick            Utilities.is_valid_pointer(
308*061da546Spatrick                self.superclassIsaPointer,
309*061da546Spatrick                self.sys_params.pointer_size,
310*061da546Spatrick                allow_tagged=0,
311*061da546Spatrick                allow_NULL=1)):
312*061da546Spatrick            logger >> "Marking as invalid - superclassIsa is invalid"
313*061da546Spatrick            self.valid = 0
314*061da546Spatrick            return
315*061da546Spatrick        if not(Utilities.is_allowed_pointer(self.superclassIsaPointer)):
316*061da546Spatrick            logger >> "Marking as invalid - superclassIsa is not allowed"
317*061da546Spatrick            self.valid = 0
318*061da546Spatrick            return
319*061da546Spatrick
320*061da546Spatrick    # in general, KVO is implemented by transparently subclassing
321*061da546Spatrick    # however, there could be exceptions where a class does something else
322*061da546Spatrick    # internally to implement the feature - this method will have no clue that a class
323*061da546Spatrick    # has been KVO'ed unless the standard implementation technique is used
324*061da546Spatrick    def is_kvo(self):
325*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
326*061da546Spatrick        if self.is_valid():
327*061da546Spatrick            if self.class_name().startswith("NSKVONotifying_"):
328*061da546Spatrick                return 1
329*061da546Spatrick        return 0
330*061da546Spatrick
331*061da546Spatrick    # some CF classes have a valid ObjC isa in their CFRuntimeBase
332*061da546Spatrick    # but instead of being class-specific this isa points to a match-'em-all class
333*061da546Spatrick    # which is __NSCFType (the versions without __ also exists and we are matching to it
334*061da546Spatrick    #                      just to be on the safe side)
335*061da546Spatrick    def is_cftype(self):
336*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
337*061da546Spatrick        if self.is_valid():
338*061da546Spatrick            return self.class_name() == '__NSCFType' or self.class_name() == 'NSCFType'
339*061da546Spatrick
340*061da546Spatrick    def get_superclass(self):
341*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
342*061da546Spatrick        if self.is_valid():
343*061da546Spatrick            parent_isa_pointer = self.valobj.CreateChildAtOffset(
344*061da546Spatrick                "parent_isa", self.sys_params.pointer_size, self.sys_params.addr_ptr_type)
345*061da546Spatrick            return Class_Data_V2(parent_isa_pointer, self.sys_params)
346*061da546Spatrick        else:
347*061da546Spatrick            return None
348*061da546Spatrick
349*061da546Spatrick    def class_name(self):
350*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
351*061da546Spatrick        if self.is_valid():
352*061da546Spatrick            return self.data.data.name
353*061da546Spatrick        else:
354*061da546Spatrick            return None
355*061da546Spatrick
356*061da546Spatrick    def is_valid(self):
357*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
358*061da546Spatrick        if self.valid:
359*061da546Spatrick            return self.data.is_valid()
360*061da546Spatrick        return 0
361*061da546Spatrick
362*061da546Spatrick    def __str__(self):
363*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
364*061da546Spatrick        return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \
365*061da546Spatrick            "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \
366*061da546Spatrick            "cachePointer = " + hex(self.cachePointer) + "\n" + \
367*061da546Spatrick            "data = " + hex(self.dataPointer)
368*061da546Spatrick
369*061da546Spatrick    def is_tagged(self):
370*061da546Spatrick        return 0
371*061da546Spatrick
372*061da546Spatrick    def instance_size(self, align=0):
373*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
374*061da546Spatrick        if self.is_valid() == 0:
375*061da546Spatrick            return None
376*061da546Spatrick        return self.rwt.rot.instance_size(align)
377*061da546Spatrick
378*061da546Spatrick# runtime v1 is much less intricate than v2 and stores relevant
379*061da546Spatrick# information directly in the class_t object
380*061da546Spatrick
381*061da546Spatrick
382*061da546Spatrickclass Class_Data_V1:
383*061da546Spatrick
384*061da546Spatrick    def __init__(self, isa_pointer, params):
385*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
386*061da546Spatrick        if (isa_pointer is not None) and (Utilities.is_valid_pointer(
387*061da546Spatrick                isa_pointer.GetValueAsUnsigned(), params.pointer_size, allow_tagged=0)):
388*061da546Spatrick            self.valid = 1
389*061da546Spatrick            self.sys_params = params
390*061da546Spatrick            self.valobj = isa_pointer
391*061da546Spatrick            self.check_valid()
392*061da546Spatrick        else:
393*061da546Spatrick            logger >> "Marking as invalid - isaPointer is invalid or None"
394*061da546Spatrick            self.valid = 0
395*061da546Spatrick        if self.valid:
396*061da546Spatrick            self.name = Utilities.read_ascii(
397*061da546Spatrick                self.valobj.GetTarget().GetProcess(), self.namePointer)
398*061da546Spatrick            if not(Utilities.is_valid_identifier(self.name)):
399*061da546Spatrick                logger >> "Marking as invalid - name is not valid"
400*061da546Spatrick                self.valid = 0
401*061da546Spatrick
402*061da546Spatrick    # perform sanity checks on the contents of this class_t
403*061da546Spatrick    def check_valid(self):
404*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
405*061da546Spatrick        self.valid = 1
406*061da546Spatrick
407*061da546Spatrick        self.isaPointer = Utilities.read_child_of(
408*061da546Spatrick            self.valobj, 0, self.sys_params.types_cache.addr_ptr_type)
409*061da546Spatrick        if not(
410*061da546Spatrick            Utilities.is_valid_pointer(
411*061da546Spatrick                self.isaPointer,
412*061da546Spatrick                self.sys_params.pointer_size,
413*061da546Spatrick                allow_tagged=0)):
414*061da546Spatrick            logger >> "Marking as invalid - isaPointer is invalid"
415*061da546Spatrick            self.valid = 0
416*061da546Spatrick            return
417*061da546Spatrick
418*061da546Spatrick        self.superclassIsaPointer = Utilities.read_child_of(
419*061da546Spatrick            self.valobj,
420*061da546Spatrick            1 * self.sys_params.pointer_size,
421*061da546Spatrick            self.sys_params.types_cache.addr_ptr_type)
422*061da546Spatrick        if not(
423*061da546Spatrick            Utilities.is_valid_pointer(
424*061da546Spatrick                self.superclassIsaPointer,
425*061da546Spatrick                self.sys_params.pointer_size,
426*061da546Spatrick                allow_tagged=0,
427*061da546Spatrick                allow_NULL=1)):
428*061da546Spatrick            logger >> "Marking as invalid - superclassIsa is invalid"
429*061da546Spatrick            self.valid = 0
430*061da546Spatrick            return
431*061da546Spatrick
432*061da546Spatrick        self.namePointer = Utilities.read_child_of(
433*061da546Spatrick            self.valobj,
434*061da546Spatrick            2 * self.sys_params.pointer_size,
435*061da546Spatrick            self.sys_params.types_cache.addr_ptr_type)
436*061da546Spatrick        # if not(Utilities.is_valid_pointer(self.namePointer,self.sys_params.pointer_size,allow_tagged=0,allow_NULL=0)):
437*061da546Spatrick        #	self.valid = 0
438*061da546Spatrick        #	return
439*061da546Spatrick
440*061da546Spatrick    # in general, KVO is implemented by transparently subclassing
441*061da546Spatrick    # however, there could be exceptions where a class does something else
442*061da546Spatrick    # internally to implement the feature - this method will have no clue that a class
443*061da546Spatrick    # has been KVO'ed unless the standard implementation technique is used
444*061da546Spatrick    def is_kvo(self):
445*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
446*061da546Spatrick        if self.is_valid():
447*061da546Spatrick            if self.class_name().startswith("NSKVONotifying_"):
448*061da546Spatrick                return 1
449*061da546Spatrick        return 0
450*061da546Spatrick
451*061da546Spatrick    # some CF classes have a valid ObjC isa in their CFRuntimeBase
452*061da546Spatrick    # but instead of being class-specific this isa points to a match-'em-all class
453*061da546Spatrick    # which is __NSCFType (the versions without __ also exists and we are matching to it
454*061da546Spatrick    #                      just to be on the safe side)
455*061da546Spatrick    def is_cftype(self):
456*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
457*061da546Spatrick        if self.is_valid():
458*061da546Spatrick            return self.class_name() == '__NSCFType' or self.class_name() == 'NSCFType'
459*061da546Spatrick
460*061da546Spatrick    def get_superclass(self):
461*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
462*061da546Spatrick        if self.is_valid():
463*061da546Spatrick            parent_isa_pointer = self.valobj.CreateChildAtOffset(
464*061da546Spatrick                "parent_isa", self.sys_params.pointer_size, self.sys_params.addr_ptr_type)
465*061da546Spatrick            return Class_Data_V1(parent_isa_pointer, self.sys_params)
466*061da546Spatrick        else:
467*061da546Spatrick            return None
468*061da546Spatrick
469*061da546Spatrick    def class_name(self):
470*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
471*061da546Spatrick        if self.is_valid():
472*061da546Spatrick            return self.name
473*061da546Spatrick        else:
474*061da546Spatrick            return None
475*061da546Spatrick
476*061da546Spatrick    def is_valid(self):
477*061da546Spatrick        return self.valid
478*061da546Spatrick
479*061da546Spatrick    def __str__(self):
480*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
481*061da546Spatrick        return 'isaPointer = ' + hex(self.isaPointer) + "\n" + \
482*061da546Spatrick            "superclassIsaPointer = " + hex(self.superclassIsaPointer) + "\n" + \
483*061da546Spatrick            "namePointer = " + hex(self.namePointer) + " --> " + self.name + \
484*061da546Spatrick            "instanceSize = " + hex(self.instanceSize()) + "\n"
485*061da546Spatrick
486*061da546Spatrick    def is_tagged(self):
487*061da546Spatrick        return 0
488*061da546Spatrick
489*061da546Spatrick    def instance_size(self, align=0):
490*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
491*061da546Spatrick        if self.is_valid() == 0:
492*061da546Spatrick            return None
493*061da546Spatrick        if self.instanceSize is None:
494*061da546Spatrick            self.instanceSize = Utilities.read_child_of(
495*061da546Spatrick                self.valobj,
496*061da546Spatrick                5 * self.sys_params.pointer_size,
497*061da546Spatrick                self.sys_params.types_cache.addr_ptr_type)
498*061da546Spatrick        if align:
499*061da546Spatrick            unalign = self.instance_size(0)
500*061da546Spatrick            if self.sys_params.is_64_bit:
501*061da546Spatrick                return ((unalign + 7) & ~7) % 0x100000000
502*061da546Spatrick            else:
503*061da546Spatrick                return ((unalign + 3) & ~3) % 0x100000000
504*061da546Spatrick        else:
505*061da546Spatrick            return self.instanceSize
506*061da546Spatrick
507*061da546Spatrick# these are the only tagged pointers values for current versions
508*061da546Spatrick# of OSX - they might change in future OS releases, and no-one is
509*061da546Spatrick# advised to rely on these values, or any of the bitmasking formulas
510*061da546Spatrick# in TaggedClass_Data. doing otherwise is at your own risk
511*061da546SpatrickTaggedClass_Values_Lion = {1: 'NSNumber',
512*061da546Spatrick                           5: 'NSManagedObject',
513*061da546Spatrick                           6: 'NSDate',
514*061da546Spatrick                           7: 'NSDateTS'}
515*061da546SpatrickTaggedClass_Values_NMOS = {0: 'NSAtom',
516*061da546Spatrick                           3: 'NSNumber',
517*061da546Spatrick                           4: 'NSDateTS',
518*061da546Spatrick                           5: 'NSManagedObject',
519*061da546Spatrick                           6: 'NSDate'}
520*061da546Spatrick
521*061da546Spatrick
522*061da546Spatrickclass TaggedClass_Data:
523*061da546Spatrick
524*061da546Spatrick    def __init__(self, pointer, params):
525*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
526*061da546Spatrick        global TaggedClass_Values_Lion, TaggedClass_Values_NMOS
527*061da546Spatrick        self.valid = 1
528*061da546Spatrick        self.name = None
529*061da546Spatrick        self.sys_params = params
530*061da546Spatrick        self.valobj = pointer
531*061da546Spatrick        self.val = (pointer & ~0x0000000000000000FF) >> 8
532*061da546Spatrick        self.class_bits = (pointer & 0xE) >> 1
533*061da546Spatrick        self.i_bits = (pointer & 0xF0) >> 4
534*061da546Spatrick
535*061da546Spatrick        if self.sys_params.is_lion:
536*061da546Spatrick            if self.class_bits in TaggedClass_Values_Lion:
537*061da546Spatrick                self.name = TaggedClass_Values_Lion[self.class_bits]
538*061da546Spatrick            else:
539*061da546Spatrick                logger >> "Marking as invalid - not a good tagged pointer for Lion"
540*061da546Spatrick                self.valid = 0
541*061da546Spatrick        else:
542*061da546Spatrick            if self.class_bits in TaggedClass_Values_NMOS:
543*061da546Spatrick                self.name = TaggedClass_Values_NMOS[self.class_bits]
544*061da546Spatrick            else:
545*061da546Spatrick                logger >> "Marking as invalid - not a good tagged pointer for NMOS"
546*061da546Spatrick                self.valid = 0
547*061da546Spatrick
548*061da546Spatrick    def is_valid(self):
549*061da546Spatrick        return self.valid
550*061da546Spatrick
551*061da546Spatrick    def class_name(self):
552*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
553*061da546Spatrick        if self.is_valid():
554*061da546Spatrick            return self.name
555*061da546Spatrick        else:
556*061da546Spatrick            return 0
557*061da546Spatrick
558*061da546Spatrick    def value(self):
559*061da546Spatrick        return self.val if self.is_valid() else None
560*061da546Spatrick
561*061da546Spatrick    def info_bits(self):
562*061da546Spatrick        return self.i_bits if self.is_valid() else None
563*061da546Spatrick
564*061da546Spatrick    def is_kvo(self):
565*061da546Spatrick        return 0
566*061da546Spatrick
567*061da546Spatrick    def is_cftype(self):
568*061da546Spatrick        return 0
569*061da546Spatrick
570*061da546Spatrick    # we would need to go around looking for the superclass or ask the runtime
571*061da546Spatrick    # for now, we seem not to require support for this operation so we will merrily
572*061da546Spatrick    # pretend to be at a root point in the hierarchy
573*061da546Spatrick    def get_superclass(self):
574*061da546Spatrick        return None
575*061da546Spatrick
576*061da546Spatrick    # anything that is handled here is tagged
577*061da546Spatrick    def is_tagged(self):
578*061da546Spatrick        return 1
579*061da546Spatrick
580*061da546Spatrick    # it seems reasonable to say that a tagged pointer is the size of a pointer
581*061da546Spatrick    def instance_size(self, align=0):
582*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
583*061da546Spatrick        if self.is_valid() == 0:
584*061da546Spatrick            return None
585*061da546Spatrick        return self.sys_params.pointer_size
586*061da546Spatrick
587*061da546Spatrick
588*061da546Spatrickclass InvalidClass_Data:
589*061da546Spatrick
590*061da546Spatrick    def __init__(self):
591*061da546Spatrick        pass
592*061da546Spatrick
593*061da546Spatrick    def is_valid(self):
594*061da546Spatrick        return 0
595*061da546Spatrick
596*061da546Spatrick
597*061da546Spatrickclass Version:
598*061da546Spatrick
599*061da546Spatrick    def __init__(self, major, minor, release, build_string):
600*061da546Spatrick        self._major = major
601*061da546Spatrick        self._minor = minor
602*061da546Spatrick        self._release = release
603*061da546Spatrick        self._build_string = build_string
604*061da546Spatrick
605*061da546Spatrick    def get_major(self):
606*061da546Spatrick        return self._major
607*061da546Spatrick
608*061da546Spatrick    def get_minor(self):
609*061da546Spatrick        return self._minor
610*061da546Spatrick
611*061da546Spatrick    def get_release(self):
612*061da546Spatrick        return self._release
613*061da546Spatrick
614*061da546Spatrick    def get_build_string(self):
615*061da546Spatrick        return self._build_string
616*061da546Spatrick
617*061da546Spatrick    major = property(get_major, None)
618*061da546Spatrick    minor = property(get_minor, None)
619*061da546Spatrick    release = property(get_release, None)
620*061da546Spatrick    build_string = property(get_build_string, None)
621*061da546Spatrick
622*061da546Spatrick    def __lt__(self, other):
623*061da546Spatrick        if (self.major < other.major):
624*061da546Spatrick            return 1
625*061da546Spatrick        if (self.minor < other.minor):
626*061da546Spatrick            return 1
627*061da546Spatrick        if (self.release < other.release):
628*061da546Spatrick            return 1
629*061da546Spatrick        # build strings are not compared since they are heavily platform-dependent and might not always
630*061da546Spatrick        # be available
631*061da546Spatrick        return 0
632*061da546Spatrick
633*061da546Spatrick    def __eq__(self, other):
634*061da546Spatrick        return (self.major == other.major) and \
635*061da546Spatrick               (self.minor == other.minor) and \
636*061da546Spatrick               (self.release == other.release) and \
637*061da546Spatrick               (self.build_string == other.build_string)
638*061da546Spatrick
639*061da546Spatrick    # Python 2.6 doesn't have functools.total_ordering, so we have to implement
640*061da546Spatrick    # other comparators
641*061da546Spatrick    def __gt__(self, other):
642*061da546Spatrick        return other < self
643*061da546Spatrick
644*061da546Spatrick    def __le__(self, other):
645*061da546Spatrick        return not other < self
646*061da546Spatrick
647*061da546Spatrick    def __ge__(self, other):
648*061da546Spatrick        return not self < other
649*061da546Spatrick
650*061da546Spatrick
651*061da546Spatrickruntime_version = lldb.formatters.cache.Cache()
652*061da546Spatrickos_version = lldb.formatters.cache.Cache()
653*061da546Spatricktypes_caches = lldb.formatters.cache.Cache()
654*061da546Spatrickisa_caches = lldb.formatters.cache.Cache()
655*061da546Spatrick
656*061da546Spatrick
657*061da546Spatrickclass SystemParameters:
658*061da546Spatrick
659*061da546Spatrick    def __init__(self, valobj):
660*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
661*061da546Spatrick        self.adjust_for_architecture(valobj)
662*061da546Spatrick        self.adjust_for_process(valobj)
663*061da546Spatrick
664*061da546Spatrick    def adjust_for_process(self, valobj):
665*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
666*061da546Spatrick        global runtime_version
667*061da546Spatrick        global os_version
668*061da546Spatrick        global types_caches
669*061da546Spatrick        global isa_caches
670*061da546Spatrick
671*061da546Spatrick        process = valobj.GetTarget().GetProcess()
672*061da546Spatrick        # using the unique ID for added guarantees (see svn revision 172628 for
673*061da546Spatrick        # further details)
674*061da546Spatrick        self.pid = process.GetUniqueID()
675*061da546Spatrick
676*061da546Spatrick        if runtime_version.look_for_key(self.pid):
677*061da546Spatrick            self.runtime_version = runtime_version.get_value(self.pid)
678*061da546Spatrick        else:
679*061da546Spatrick            self.runtime_version = ObjCRuntime.runtime_version(process)
680*061da546Spatrick            runtime_version.add_item(self.pid, self.runtime_version)
681*061da546Spatrick
682*061da546Spatrick        if os_version.look_for_key(self.pid):
683*061da546Spatrick            self.is_lion = os_version.get_value(self.pid)
684*061da546Spatrick        else:
685*061da546Spatrick            self.is_lion = Utilities.check_is_osx_lion(valobj.GetTarget())
686*061da546Spatrick            os_version.add_item(self.pid, self.is_lion)
687*061da546Spatrick
688*061da546Spatrick        if types_caches.look_for_key(self.pid):
689*061da546Spatrick            self.types_cache = types_caches.get_value(self.pid)
690*061da546Spatrick        else:
691*061da546Spatrick            self.types_cache = lldb.formatters.attrib_fromdict.AttributesDictionary(
692*061da546Spatrick                allow_reset=0)
693*061da546Spatrick            self.types_cache.addr_type = valobj.GetType(
694*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeUnsignedLong)
695*061da546Spatrick            self.types_cache.addr_ptr_type = self.types_cache.addr_type.GetPointerType()
696*061da546Spatrick            self.types_cache.uint32_t = valobj.GetType(
697*061da546Spatrick            ).GetBasicType(lldb.eBasicTypeUnsignedInt)
698*061da546Spatrick            types_caches.add_item(self.pid, self.types_cache)
699*061da546Spatrick
700*061da546Spatrick        if isa_caches.look_for_key(self.pid):
701*061da546Spatrick            self.isa_cache = isa_caches.get_value(self.pid)
702*061da546Spatrick        else:
703*061da546Spatrick            self.isa_cache = lldb.formatters.cache.Cache()
704*061da546Spatrick            isa_caches.add_item(self.pid, self.isa_cache)
705*061da546Spatrick
706*061da546Spatrick    def adjust_for_architecture(self, valobj):
707*061da546Spatrick        process = valobj.GetTarget().GetProcess()
708*061da546Spatrick        self.pointer_size = process.GetAddressByteSize()
709*061da546Spatrick        self.is_64_bit = (self.pointer_size == 8)
710*061da546Spatrick        self.endianness = process.GetByteOrder()
711*061da546Spatrick        self.is_little = (self.endianness == lldb.eByteOrderLittle)
712*061da546Spatrick        self.cfruntime_size = 16 if self.is_64_bit else 8
713*061da546Spatrick
714*061da546Spatrick    # a simple helper function that makes it more explicit that one is calculating
715*061da546Spatrick    # an offset that is made up of X pointers and Y bytes of additional data
716*061da546Spatrick    # taking into account pointer size - if you know there is going to be some padding
717*061da546Spatrick    # you can pass that in and it will be taken into account (since padding may be different between
718*061da546Spatrick    # 32 and 64 bit versions, you can pass padding value for both, the right
719*061da546Spatrick    # one will be used)
720*061da546Spatrick    def calculate_offset(
721*061da546Spatrick            self,
722*061da546Spatrick            num_pointers=0,
723*061da546Spatrick            bytes_count=0,
724*061da546Spatrick            padding32=0,
725*061da546Spatrick            padding64=0):
726*061da546Spatrick        value = bytes_count + num_pointers * self.pointer_size
727*061da546Spatrick        return value + padding64 if self.is_64_bit else value + padding32
728*061da546Spatrick
729*061da546Spatrick
730*061da546Spatrickclass ObjCRuntime:
731*061da546Spatrick
732*061da546Spatrick    # the ObjC runtime has no explicit "version" field that we can use
733*061da546Spatrick    # instead, we discriminate v1 from v2 by looking for the presence
734*061da546Spatrick    # of a well-known section only present in v1
735*061da546Spatrick    @staticmethod
736*061da546Spatrick    def runtime_version(process):
737*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
738*061da546Spatrick        if process.IsValid() == 0:
739*061da546Spatrick            logger >> "No process - bailing out"
740*061da546Spatrick            return None
741*061da546Spatrick        target = process.GetTarget()
742*061da546Spatrick        num_modules = target.GetNumModules()
743*061da546Spatrick        module_objc = None
744*061da546Spatrick        for idx in range(num_modules):
745*061da546Spatrick            module = target.GetModuleAtIndex(idx)
746*061da546Spatrick            if module.GetFileSpec().GetFilename() == 'libobjc.A.dylib':
747*061da546Spatrick                module_objc = module
748*061da546Spatrick                break
749*061da546Spatrick        if module_objc is None or module_objc.IsValid() == 0:
750*061da546Spatrick            logger >> "no libobjc - bailing out"
751*061da546Spatrick            return None
752*061da546Spatrick        num_sections = module.GetNumSections()
753*061da546Spatrick        section_objc = None
754*061da546Spatrick        for idx in range(num_sections):
755*061da546Spatrick            section = module.GetSectionAtIndex(idx)
756*061da546Spatrick            if section.GetName() == '__OBJC':
757*061da546Spatrick                section_objc = section
758*061da546Spatrick                break
759*061da546Spatrick        if section_objc is not None and section_objc.IsValid():
760*061da546Spatrick            logger >> "found __OBJC: v1"
761*061da546Spatrick            return 1
762*061da546Spatrick        logger >> "no __OBJC: v2"
763*061da546Spatrick        return 2
764*061da546Spatrick
765*061da546Spatrick    @staticmethod
766*061da546Spatrick    def runtime_from_isa(isa):
767*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
768*061da546Spatrick        runtime = ObjCRuntime(isa)
769*061da546Spatrick        runtime.isa = isa
770*061da546Spatrick        return runtime
771*061da546Spatrick
772*061da546Spatrick    def __init__(self, valobj):
773*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
774*061da546Spatrick        self.valobj = valobj
775*061da546Spatrick        self.adjust_for_architecture()
776*061da546Spatrick        self.sys_params = SystemParameters(self.valobj)
777*061da546Spatrick        self.unsigned_value = self.valobj.GetValueAsUnsigned()
778*061da546Spatrick        self.isa_value = None
779*061da546Spatrick
780*061da546Spatrick    def adjust_for_architecture(self):
781*061da546Spatrick        pass
782*061da546Spatrick
783*061da546Spatrick# an ObjC pointer can either be tagged or must be aligned
784*061da546Spatrick    def is_tagged(self):
785*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
786*061da546Spatrick        if self.valobj is None:
787*061da546Spatrick            return 0
788*061da546Spatrick        return (
789*061da546Spatrick            Utilities.is_valid_pointer(
790*061da546Spatrick                self.unsigned_value,
791*061da546Spatrick                self.sys_params.pointer_size,
792*061da546Spatrick                allow_tagged=1) and not(
793*061da546Spatrick                Utilities.is_valid_pointer(
794*061da546Spatrick                    self.unsigned_value,
795*061da546Spatrick                    self.sys_params.pointer_size,
796*061da546Spatrick                    allow_tagged=0)))
797*061da546Spatrick
798*061da546Spatrick    def is_valid(self):
799*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
800*061da546Spatrick        if self.valobj is None:
801*061da546Spatrick            return 0
802*061da546Spatrick        if self.valobj.IsInScope() == 0:
803*061da546Spatrick            return 0
804*061da546Spatrick        return Utilities.is_valid_pointer(
805*061da546Spatrick            self.unsigned_value,
806*061da546Spatrick            self.sys_params.pointer_size,
807*061da546Spatrick            allow_tagged=1)
808*061da546Spatrick
809*061da546Spatrick    def is_nil(self):
810*061da546Spatrick        return self.unsigned_value == 0
811*061da546Spatrick
812*061da546Spatrick    def read_isa(self):
813*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
814*061da546Spatrick        if self.isa_value is not None:
815*061da546Spatrick            logger >> "using cached isa"
816*061da546Spatrick            return self.isa_value
817*061da546Spatrick        self.isa_pointer = self.valobj.CreateChildAtOffset(
818*061da546Spatrick            "cfisa", 0, self.sys_params.types_cache.addr_ptr_type)
819*061da546Spatrick        if self.isa_pointer is None or self.isa_pointer.IsValid() == 0:
820*061da546Spatrick            logger >> "invalid isa - bailing out"
821*061da546Spatrick            return None
822*061da546Spatrick        self.isa_value = self.isa_pointer.GetValueAsUnsigned(1)
823*061da546Spatrick        if self.isa_value == 1:
824*061da546Spatrick            logger >> "invalid isa value - bailing out"
825*061da546Spatrick            return None
826*061da546Spatrick        return Ellipsis
827*061da546Spatrick
828*061da546Spatrick    def read_class_data(self):
829*061da546Spatrick        logger = lldb.formatters.Logger.Logger()
830*061da546Spatrick        global isa_cache
831*061da546Spatrick        if self.is_tagged():
832*061da546Spatrick            # tagged pointers only exist in ObjC v2
833*061da546Spatrick            if self.sys_params.runtime_version == 2:
834*061da546Spatrick                logger >> "on v2 and tagged - maybe"
835*061da546Spatrick                # not every odd-valued pointer is actually tagged. most are just plain wrong
836*061da546Spatrick                # we could try and predetect this before even creating a TaggedClass_Data object
837*061da546Spatrick                # but unless performance requires it, this seems a cleaner way
838*061da546Spatrick                # to tackle the task
839*061da546Spatrick                tentative_tagged = TaggedClass_Data(
840*061da546Spatrick                    self.unsigned_value, self.sys_params)
841*061da546Spatrick                if tentative_tagged.is_valid():
842*061da546Spatrick                    logger >> "truly tagged"
843*061da546Spatrick                    return tentative_tagged
844*061da546Spatrick                else:
845*061da546Spatrick                    logger >> "not tagged - error"
846*061da546Spatrick                    return InvalidClass_Data()
847*061da546Spatrick            else:
848*061da546Spatrick                logger >> "on v1 and tagged - error"
849*061da546Spatrick                return InvalidClass_Data()
850*061da546Spatrick        if self.is_valid() == 0 or self.read_isa() is None:
851*061da546Spatrick            return InvalidClass_Data()
852*061da546Spatrick        data = self.sys_params.isa_cache.get_value(
853*061da546Spatrick            self.isa_value, default=None)
854*061da546Spatrick        if data is not None:
855*061da546Spatrick            return data
856*061da546Spatrick        if self.sys_params.runtime_version == 2:
857*061da546Spatrick            data = Class_Data_V2(self.isa_pointer, self.sys_params)
858*061da546Spatrick        else:
859*061da546Spatrick            data = Class_Data_V1(self.isa_pointer, self.sys_params)
860*061da546Spatrick        if data is None:
861*061da546Spatrick            return InvalidClass_Data()
862*061da546Spatrick        if data.is_valid():
863*061da546Spatrick            self.sys_params.isa_cache.add_item(
864*061da546Spatrick                self.isa_value, data, ok_to_replace=1)
865*061da546Spatrick        return data
866*061da546Spatrick
867*061da546Spatrick# these classes below can be used by the data formatters to provide a
868*061da546Spatrick# consistent message that describes a given runtime-generated situation
869*061da546Spatrick
870*061da546Spatrick
871*061da546Spatrickclass SpecialSituation_Description:
872*061da546Spatrick
873*061da546Spatrick    def message(self):
874*061da546Spatrick        return ''
875*061da546Spatrick
876*061da546Spatrick
877*061da546Spatrickclass InvalidPointer_Description(SpecialSituation_Description):
878*061da546Spatrick
879*061da546Spatrick    def __init__(self, nil):
880*061da546Spatrick        self.is_nil = nil
881*061da546Spatrick
882*061da546Spatrick    def message(self):
883*061da546Spatrick        if self.is_nil:
884*061da546Spatrick            return '@"<nil>"'
885*061da546Spatrick        else:
886*061da546Spatrick            return '<invalid pointer>'
887*061da546Spatrick
888*061da546Spatrick
889*061da546Spatrickclass InvalidISA_Description(SpecialSituation_Description):
890*061da546Spatrick
891*061da546Spatrick    def __init__(self):
892*061da546Spatrick        pass
893*061da546Spatrick
894*061da546Spatrick    def message(self):
895*061da546Spatrick        return '<not an Objective-C object>'
896*061da546Spatrick
897*061da546Spatrick
898*061da546Spatrickclass ThisIsZombie_Description(SpecialSituation_Description):
899*061da546Spatrick
900*061da546Spatrick    def message(self):
901*061da546Spatrick        return '<freed object>'
902