xref: /llvm-project/lldb/examples/summaries/cocoa/CFArray.py (revision de93b6b42fa6bfdfb4abfb15a7cf43b5c8e0e1a9)
1"""
2LLDB AppKit formatters
3
4part of The LLVM Compiler Infrastructure
5This file is distributed under the University of Illinois Open Source
6License. See LICENSE.TXT for details.
7"""
8# synthetic children provider for NSArray
9import lldb
10import ctypes
11import lldb.runtime.objc.objc_runtime
12import lldb.formatters.metrics
13import lldb.formatters.Logger
14
15statistics = lldb.formatters.metrics.Metrics()
16statistics.add_metric('invalid_isa')
17statistics.add_metric('invalid_pointer')
18statistics.add_metric('unknown_class')
19statistics.add_metric('code_notrun')
20
21# much less functional than the other two cases below
22# just runs code to get to the count and then returns
23# no children
24class NSArrayKVC_SynthProvider:
25
26	def adjust_for_architecture(self):
27		pass
28
29	def __init__(self, valobj, dict, params):
30		logger = lldb.formatters.Logger.Logger()
31		self.valobj = valobj;
32		self.update()
33
34	def update(self):
35		logger = lldb.formatters.Logger.Logger()
36		self.adjust_for_architecture();
37
38	def num_children(self):
39		logger = lldb.formatters.Logger.Logger()
40		stream = lldb.SBStream()
41		self.valobj.GetExpressionPath(stream)
42		num_children_vo = self.valobj.CreateValueFromExpression("count","(int)[" + stream.GetData() + " count]");
43		if num_children_vo.IsValid():
44			return num_children_vo.GetValueAsUnsigned(0)
45		return "<variable is not NSArray>"
46
47# much less functional than the other two cases below
48# just runs code to get to the count and then returns
49# no children
50class NSArrayCF_SynthProvider:
51
52	def adjust_for_architecture(self):
53		pass
54
55	def __init__(self, valobj, dict, params):
56		logger = lldb.formatters.Logger.Logger()
57		self.valobj = valobj;
58		self.sys_params = params
59		if not (self.sys_params.types_cache.ulong):
60			self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
61		self.update()
62
63	def update(self):
64		logger = lldb.formatters.Logger.Logger()
65		self.adjust_for_architecture();
66
67	def num_children(self):
68		logger = lldb.formatters.Logger.Logger()
69		num_children_vo = self.valobj.CreateChildAtOffset("count",
70							self.sys_params.cfruntime_size,
71							self.sys_params.types_cache.ulong)
72		return num_children_vo.GetValueAsUnsigned(0)
73
74class NSArrayI_SynthProvider:
75	def adjust_for_architecture(self):
76		pass
77
78	def __init__(self, valobj, dict, params):
79		logger = lldb.formatters.Logger.Logger()
80		self.valobj = valobj;
81		self.sys_params = params
82		if not(self.sys_params.types_cache.long):
83			self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
84		self.update()
85
86	def update(self):
87		logger = lldb.formatters.Logger.Logger()
88		self.adjust_for_architecture();
89
90	# skip the isa pointer and get at the size
91	def num_children(self):
92		logger = lldb.formatters.Logger.Logger()
93		count = self.valobj.CreateChildAtOffset("count",
94				self.sys_params.pointer_size,
95				self.sys_params.types_cache.long);
96		return count.GetValueAsUnsigned(0)
97
98class NSArrayM_SynthProvider:
99	def adjust_for_architecture(self):
100		pass
101
102	def __init__(self, valobj, dict, params):
103		logger = lldb.formatters.Logger.Logger()
104		self.valobj = valobj;
105		self.sys_params = params
106		if not(self.sys_params.types_cache.long):
107			self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
108		self.update()
109
110	def update(self):
111		logger = lldb.formatters.Logger.Logger()
112		self.adjust_for_architecture();
113
114	# skip the isa pointer and get at the size
115	def num_children(self):
116		logger = lldb.formatters.Logger.Logger()
117		count = self.valobj.CreateChildAtOffset("count",
118				self.sys_params.pointer_size,
119				self.sys_params.types_cache.long);
120		return count.GetValueAsUnsigned(0)
121
122# this is the actual synth provider, but is just a wrapper that checks
123# whether valobj is an instance of __NSArrayI or __NSArrayM and sets up an
124# appropriate backend layer to do the computations
125class NSArray_SynthProvider:
126	def adjust_for_architecture(self):
127		pass
128
129	def __init__(self, valobj, dict):
130		logger = lldb.formatters.Logger.Logger()
131		self.valobj = valobj;
132		self.adjust_for_architecture()
133		self.error = False
134		self.wrapper = self.make_wrapper()
135		self.invalid = (self.wrapper == None)
136
137	def num_children(self):
138		logger = lldb.formatters.Logger.Logger()
139		if self.wrapper == None:
140			return 0;
141		return self.wrapper.num_children()
142
143	def update(self):
144		logger = lldb.formatters.Logger.Logger()
145		if self.wrapper == None:
146			return
147		self.wrapper.update()
148
149	# this code acts as our defense against NULL and unitialized
150	# NSArray pointers, which makes it much longer than it would be otherwise
151	def make_wrapper(self):
152		logger = lldb.formatters.Logger.Logger()
153		if self.valobj.GetValueAsUnsigned() == 0:
154			self.error = True
155			return lldb.runtime.objc.objc_runtime.InvalidPointer_Description(True)
156		else:
157			global statistics
158			class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(self.valobj,statistics)
159			if wrapper:
160				self.error = True
161				return wrapper
162
163		name_string = class_data.class_name()
164
165		logger >> "Class name is " + str(name_string)
166
167		if name_string == '__NSArrayI':
168			wrapper = NSArrayI_SynthProvider(self.valobj, dict, class_data.sys_params)
169			statistics.metric_hit('code_notrun',self.valobj.GetName())
170		elif name_string == '__NSArrayM':
171			wrapper = NSArrayM_SynthProvider(self.valobj, dict, class_data.sys_params)
172			statistics.metric_hit('code_notrun',self.valobj.GetName())
173		elif name_string == '__NSCFArray':
174			wrapper = NSArrayCF_SynthProvider(self.valobj, dict, class_data.sys_params)
175			statistics.metric_hit('code_notrun',self.valobj.GetName())
176		else:
177			wrapper = NSArrayKVC_SynthProvider(self.valobj, dict, class_data.sys_params)
178			statistics.metric_hit('unknown_class',str(self.valobj.GetName()) + " seen as " + name_string)
179		return wrapper;
180
181def CFArray_SummaryProvider (valobj,dict):
182	logger = lldb.formatters.Logger.Logger()
183	provider = NSArray_SynthProvider(valobj,dict);
184	if provider.invalid == False:
185		if provider.error == True:
186			return provider.wrapper.message()
187		try:
188			summary = int(provider.num_children());
189		except:
190			summary = None
191		logger >> "provider gave me " + str(summary)
192		if summary == None:
193			summary = '<variable is not NSArray>'
194		elif isinstance(summary,basestring):
195			pass
196		else:
197			# we format it like it were a CFString to make it look the same as the summary from Xcode
198			summary = '@"' + str(summary) + (" objects" if summary != 1 else " object") + '"'
199		return summary
200	return 'Summary Unavailable'
201
202def __lldb_init_module(debugger,dict):
203	debugger.HandleCommand("type summary add -F CFArray.CFArray_SummaryProvider NSArray CFArrayRef CFMutableArrayRef")
204