aboutsummaryrefslogtreecommitdiff
path: root/examples/summaries/cocoa/NSNumber.py
blob: 7edd33803f9cec07fea72cbaa7f21e0d639d7f76 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"""
LLDB AppKit formatters

part of The LLVM Compiler Infrastructure
This file is distributed under the University of Illinois Open Source
License. See LICENSE.TXT for details.
"""
# example summary provider for NSNumber
# the real summary is now C++ code built into LLDB
import lldb
import ctypes
import lldb.runtime.objc.objc_runtime
import lldb.formatters.metrics
import struct
import lldb.formatters.Logger

statistics = lldb.formatters.metrics.Metrics()
statistics.add_metric('invalid_isa')
statistics.add_metric('invalid_pointer')
statistics.add_metric('unknown_class')
statistics.add_metric('code_notrun')

# despite the similary to synthetic children providers, these classes are not
# trying to provide anything but the port number of an NSNumber, so they need not
# obey the interface specification for synthetic children providers
class NSTaggedNumber_SummaryProvider:
	def adjust_for_architecture(self):
		pass

	def __init__(self, valobj, info_bits, data, params):
		logger = lldb.formatters.Logger.Logger()
		self.valobj = valobj;
		self.sys_params = params
		self.info_bits = info_bits
		self.data = data
		self.update();

	def update(self):
		logger = lldb.formatters.Logger.Logger()
		self.adjust_for_architecture();

	def value(self):
		logger = lldb.formatters.Logger.Logger()
		# in spite of the plenty of types made available by the public NSNumber API
		# only a bunch of these are actually used in the internal implementation
		# unfortunately, the original type information appears to be lost
		# so we try to at least recover the proper magnitude of the data
		if self.info_bits == 0:
			return '(char)' + str(ord(ctypes.c_char(chr(self.data % 256)).value))
		if self.info_bits == 4:
			return '(short)' + str(ctypes.c_short(self.data % (256*256)).value)
		if self.info_bits == 8:
			return '(int)' + str(ctypes.c_int(self.data % (256*256*256*256)).value)
		if self.info_bits == 12:
			return '(long)' + str(ctypes.c_long(self.data).value)
		else:
			return 'unexpected value:(info=' + str(self.info_bits) + ", value = " + str(self.data) + ')'


class NSUntaggedNumber_SummaryProvider:
	def adjust_for_architecture(self):
		pass

	def __init__(self, valobj, params):
		logger = lldb.formatters.Logger.Logger()
		self.valobj = valobj;
		self.sys_params = params
		if not(self.sys_params.types_cache.char):
			self.sys_params.types_cache.char = self.valobj.GetType().GetBasicType(lldb.eBasicTypeChar)
		if not(self.sys_params.types_cache.short):
			self.sys_params.types_cache.short = self.valobj.GetType().GetBasicType(lldb.eBasicTypeShort)
		if not(self.sys_params.types_cache.ushort):
			self.sys_params.types_cache.ushort = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedShort)
		if not(self.sys_params.types_cache.int):
			self.sys_params.types_cache.int = self.valobj.GetType().GetBasicType(lldb.eBasicTypeInt)
		if not(self.sys_params.types_cache.long):
			self.sys_params.types_cache.long = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLong)
		if not(self.sys_params.types_cache.ulong):
			self.sys_params.types_cache.ulong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLong)
		if not(self.sys_params.types_cache.longlong):
			self.sys_params.types_cache.longlong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeLongLong)
		if not(self.sys_params.types_cache.ulonglong):
			self.sys_params.types_cache.ulonglong = self.valobj.GetType().GetBasicType(lldb.eBasicTypeUnsignedLongLong)
		if not(self.sys_params.types_cache.float):
			self.sys_params.types_cache.float = self.valobj.GetType().GetBasicType(lldb.eBasicTypeFloat)
		if not(self.sys_params.types_cache.double):
			self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble)
		self.update();

	def update(self):
		logger = lldb.formatters.Logger.Logger()
		self.adjust_for_architecture();

	def value(self):
		logger = lldb.formatters.Logger.Logger()
		global statistics
		# we need to skip the ISA, then the next byte tells us what to read
		# we then skip one other full pointer worth of data and then fetch the contents
		# if we are fetching an int64 value, one more pointer must be skipped to get at our data
		data_type_vo = self.valobj.CreateChildAtOffset("dt",
							self.sys_params.pointer_size,
							self.sys_params.types_cache.char)
		data_type = ((data_type_vo.GetValueAsUnsigned(0) % 256) & 0x1F)
		data_offset = 2 * self.sys_params.pointer_size
		if data_type == 0B00001:
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.char)
			statistics.metric_hit('code_notrun',self.valobj)
			return '(char)' + str(ord(ctypes.c_char(chr(data_vo.GetValueAsUnsigned(0))).value))
		elif data_type == 0B0010:
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.short)
			statistics.metric_hit('code_notrun',self.valobj)
			return '(short)' + str(ctypes.c_short(data_vo.GetValueAsUnsigned(0) % (256*256)).value)
		# IF tagged pointers are possible on 32bit+v2 runtime
		# (of which the only existing instance should be iOS)
		# then values of this type might be tagged
		elif data_type == 0B0011:
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.int)
			statistics.metric_hit('code_notrun',self.valobj)
			return '(int)' + str(ctypes.c_int(data_vo.GetValueAsUnsigned(0)% (256*256*256*256)).value)
		# apparently, on is_64_bit architectures, these are the only values that will ever
		# be represented by a non tagged pointers
		elif data_type == 0B10001:
			data_offset = data_offset + 8 # 8 is needed even if we are on 32bit
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.longlong)
			statistics.metric_hit('code_notrun',self.valobj)
			return '(long)' + str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value)
		elif data_type == 0B0100:
			if self.sys_params.is_64_bit:
				data_offset = data_offset + self.sys_params.pointer_size
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.longlong)
			statistics.metric_hit('code_notrun',self.valobj)
			return '(long)' + str(ctypes.c_long(data_vo.GetValueAsUnsigned(0)).value)
		elif data_type == 0B0101:
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.longlong)
			data_plain = int(str(data_vo.GetValueAsUnsigned(0) & 0x00000000FFFFFFFF))
			packed = struct.pack('I', data_plain)
			data_float = struct.unpack('f', packed)[0]
			statistics.metric_hit('code_notrun',self.valobj)
			return '(float)' + str(data_float)
		elif data_type == 0B0110:
			data_vo = self.valobj.CreateChildAtOffset("data",
								data_offset,
								self.sys_params.types_cache.longlong)
			data_plain = data_vo.GetValueAsUnsigned(0)
			data_double = struct.unpack('d', struct.pack('Q', data_plain))[0]
			statistics.metric_hit('code_notrun',self.valobj)
			return '(double)' + str(data_double)
		statistics.metric_hit('unknown_class',str(valobj.GetName()) + " had unknown data_type " + str(data_type))
		return 'unexpected: dt = ' + str(data_type)


class NSUnknownNumber_SummaryProvider:
	def adjust_for_architecture(self):
		pass

	def __init__(self, valobj, params):
		logger = lldb.formatters.Logger.Logger()
		self.valobj = valobj;
		self.sys_params = params
		self.update();

	def update(self):
		logger = lldb.formatters.Logger.Logger()
		self.adjust_for_architecture();

	def value(self):
		logger = lldb.formatters.Logger.Logger()
		stream = lldb.SBStream()
		self.valobj.GetExpressionPath(stream)
		expr = "(NSString*)[" + stream.GetData() + " stringValue]"
		num_children_vo = self.valobj.CreateValueFromExpression("str",expr)
		if num_children_vo.IsValid():
			return num_children_vo.GetSummary()
		return '<variable is not NSNumber>'

def GetSummary_Impl(valobj):
	logger = lldb.formatters.Logger.Logger()
	global statistics
	class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
	if wrapper:
		return wrapper
	
	name_string = class_data.class_name()
	logger >> "class name is: " + str(name_string)

	if name_string == 'NSNumber' or name_string == '__NSCFNumber':
		if class_data.is_tagged():
			wrapper = NSTaggedNumber_SummaryProvider(valobj,class_data.info_bits(),class_data.value(), class_data.sys_params)
			statistics.metric_hit('code_notrun',valobj)
		else:
			# the wrapper might be unable to decipher what is into the NSNumber
			# and then have to run code on it
			wrapper = NSUntaggedNumber_SummaryProvider(valobj, class_data.sys_params)
	else:
		wrapper = NSUnknownNumber_SummaryProvider(valobj, class_data.sys_params)
		statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string)
	return wrapper;


def NSNumber_SummaryProvider (valobj,dict):
	logger = lldb.formatters.Logger.Logger()
	provider = GetSummary_Impl(valobj);
	if provider != None:
		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
			return provider.message()
		try:
			summary = provider.value();
		except Exception as foo:
			print foo
#		except:
			summary = None
		logger >> "got summary " + str(summary)
		if summary == None:
			summary = '<variable is not NSNumber>'
		return str(summary)
	return 'Summary Unavailable'


def __lldb_init_module(debugger,dict):
	debugger.HandleCommand("type summary add -F NSNumber.NSNumber_SummaryProvider NSNumber")
	debugger.HandleCommand("type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFBoolean")
	debugger.HandleCommand("type summary add -F NSNumber.NSNumber_SummaryProvider __NSCFNumber")