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# example summary provider for NSDate
9# the real summary is now C++ code built into LLDB
10import lldb
11import ctypes
12import lldb.runtime.objc.objc_runtime
13import lldb.formatters.metrics
14import struct
15import time
16import datetime
17import CFString
18import lldb.formatters.Logger
19
20statistics = lldb.formatters.metrics.Metrics()
21statistics.add_metric('invalid_isa')
22statistics.add_metric('invalid_pointer')
23statistics.add_metric('unknown_class')
24statistics.add_metric('code_notrun')
25
26# Python promises to start counting time at midnight on Jan 1st on the epoch year
27# hence, all we need to know is the epoch year
28python_epoch = time.gmtime(0).tm_year
29
30osx_epoch = datetime.date(2001,1,1).timetuple()
31
32def mkgmtime(t):
33	logger = lldb.formatters.Logger.Logger()
34	return time.mktime(t)-time.timezone
35
36osx_epoch = mkgmtime(osx_epoch)
37
38def osx_to_python_time(osx):
39	logger = lldb.formatters.Logger.Logger()
40	if python_epoch <= 2001:
41		return osx + osx_epoch
42	else:
43		return osx - osx_epoch
44
45# represent a struct_time as a string in the format used by Xcode
46def xcode_format_time(X):
47	logger = lldb.formatters.Logger.Logger()
48	return time.strftime('%Y-%m-%d %H:%M:%S %Z',X)
49
50# represent a count-since-epoch as a string in the format used by Xcode
51def xcode_format_count(X):
52	logger = lldb.formatters.Logger.Logger()
53	return xcode_format_time(time.localtime(X))
54
55# despite the similary to synthetic children providers, these classes are not
56# trying to provide anything but the summary for NSDate, so they need not
57# obey the interface specification for synthetic children providers
58class NSTaggedDate_SummaryProvider:
59	def adjust_for_architecture(self):
60		pass
61
62	def __init__(self, valobj, info_bits, data, params):
63		logger = lldb.formatters.Logger.Logger()
64		self.valobj = valobj;
65		self.sys_params = params
66		self.update();
67		# NSDate is not using its info_bits for info like NSNumber is
68		# so we need to regroup info_bits and data
69		self.data = ((data << 8) | (info_bits << 4))
70
71	def update(self):
72		logger = lldb.formatters.Logger.Logger()
73		self.adjust_for_architecture();
74
75	def value(self):
76		logger = lldb.formatters.Logger.Logger()
77		# the value of the date-time object is wrapped into the pointer value
78		# unfortunately, it is made as a time-delta after Jan 1 2001 midnight GMT
79		# while all Python knows about is the "epoch", which is a platform-dependent
80		# year (1970 of *nix) whose Jan 1 at midnight is taken as reference
81		value_double = struct.unpack('d', struct.pack('Q', self.data))[0]
82		if value_double == -63114076800.0:
83			return '0001-12-30 00:00:00 +0000'
84		return xcode_format_count(osx_to_python_time(value_double))
85
86
87class NSUntaggedDate_SummaryProvider:
88	def adjust_for_architecture(self):
89		pass
90
91	def __init__(self, valobj, params):
92		logger = lldb.formatters.Logger.Logger()
93		self.valobj = valobj;
94		self.sys_params = params
95		if not (self.sys_params.types_cache.double):
96			self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble)
97		self.update()
98
99	def update(self):
100		logger = lldb.formatters.Logger.Logger()
101		self.adjust_for_architecture();
102
103	def offset(self):
104		logger = lldb.formatters.Logger.Logger()
105		return self.sys_params.pointer_size
106
107	def value(self):
108		logger = lldb.formatters.Logger.Logger()
109		value = self.valobj.CreateChildAtOffset("value",
110							self.offset(),
111							self.sys_params.types_cache.double)
112		value_double = struct.unpack('d', struct.pack('Q', value.GetData().uint64[0]))[0]
113		if value_double == -63114076800.0:
114			return '0001-12-30 00:00:00 +0000'
115		return xcode_format_count(osx_to_python_time(value_double))
116
117class NSCalendarDate_SummaryProvider:
118	def adjust_for_architecture(self):
119		pass
120
121	def __init__(self, valobj, params):
122		logger = lldb.formatters.Logger.Logger()
123		self.valobj = valobj;
124		self.sys_params = params
125		if not (self.sys_params.types_cache.double):
126			self.sys_params.types_cache.double = self.valobj.GetType().GetBasicType(lldb.eBasicTypeDouble)
127		self.update()
128
129	def update(self):
130		logger = lldb.formatters.Logger.Logger()
131		self.adjust_for_architecture();
132
133	def offset(self):
134		logger = lldb.formatters.Logger.Logger()
135		return 2*self.sys_params.pointer_size
136
137	def value(self):
138		logger = lldb.formatters.Logger.Logger()
139		value = self.valobj.CreateChildAtOffset("value",
140							self.offset(),
141							self.sys_params.types_cache.double)
142		value_double = struct.unpack('d', struct.pack('Q', value.GetData().uint64[0]))[0]
143		return xcode_format_count(osx_to_python_time(value_double))
144
145class NSTimeZoneClass_SummaryProvider:
146	def adjust_for_architecture(self):
147		pass
148
149	def __init__(self, valobj, params):
150		logger = lldb.formatters.Logger.Logger()
151		self.valobj = valobj;
152		self.sys_params = params
153		if not (self.sys_params.types_cache.voidptr):
154			self.sys_params.types_cache.voidptr = self.valobj.GetType().GetBasicType(lldb.eBasicTypeVoid).GetPointerType()
155		self.update()
156
157	def update(self):
158		logger = lldb.formatters.Logger.Logger()
159		self.adjust_for_architecture();
160
161	def offset(self):
162		logger = lldb.formatters.Logger.Logger()
163		return self.sys_params.pointer_size
164
165	def timezone(self):
166		logger = lldb.formatters.Logger.Logger()
167		tz_string = self.valobj.CreateChildAtOffset("tz_name",
168							self.offset(),
169							self.sys_params.types_cache.voidptr)
170		return CFString.CFString_SummaryProvider(tz_string,None)
171
172class NSUnknownDate_SummaryProvider:
173	def adjust_for_architecture(self):
174		pass
175
176	def __init__(self, valobj):
177		logger = lldb.formatters.Logger.Logger()
178		self.valobj = valobj;
179		self.update()
180
181	def update(self):
182		logger = lldb.formatters.Logger.Logger()
183		self.adjust_for_architecture();
184
185	def value(self):
186		logger = lldb.formatters.Logger.Logger()
187		stream = lldb.SBStream()
188		self.valobj.GetExpressionPath(stream)
189		expr = "(NSString*)[" + stream.GetData() + " description]"
190		num_children_vo = self.valobj.CreateValueFromExpression("str",expr);
191		if num_children_vo.IsValid():
192			return num_children_vo.GetSummary()
193		return '<variable is not NSDate>'
194
195def GetSummary_Impl(valobj):
196	logger = lldb.formatters.Logger.Logger()
197	global statistics
198	class_data,wrapper =lldb.runtime.objc.objc_runtime.Utilities.prepare_class_detection(valobj,statistics)
199	if wrapper:
200		return wrapper
201
202	name_string = class_data.class_name()
203	logger >> "class name is: " + str(name_string)
204
205	if name_string == 'NSDate' or name_string == '__NSDate' or name_string == '__NSTaggedDate':
206		if class_data.is_tagged():
207			wrapper = NSTaggedDate_SummaryProvider(valobj,class_data.info_bits(),class_data.value(), class_data.sys_params)
208			statistics.metric_hit('code_notrun',valobj)
209		else:
210			wrapper = NSUntaggedDate_SummaryProvider(valobj, class_data.sys_params)
211			statistics.metric_hit('code_notrun',valobj)
212	elif name_string == 'NSCalendarDate':
213		wrapper = NSCalendarDate_SummaryProvider(valobj, class_data.sys_params)
214		statistics.metric_hit('code_notrun',valobj)
215	elif name_string == '__NSTimeZone':
216		wrapper = NSTimeZoneClass_SummaryProvider(valobj, class_data.sys_params)
217		statistics.metric_hit('code_notrun',valobj)
218	else:
219		wrapper = NSUnknownDate_SummaryProvider(valobj)
220		statistics.metric_hit('unknown_class',valobj.GetName() + " seen as " + name_string)
221	return wrapper;
222
223
224def NSDate_SummaryProvider (valobj,dict):
225	logger = lldb.formatters.Logger.Logger()
226	provider = GetSummary_Impl(valobj);
227	if provider != None:
228		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
229			return provider.message()
230		try:
231			summary = provider.value();
232		except:
233			summary = None
234		if summary == None:
235			summary = '<variable is not NSDate>'
236		return str(summary)
237	return 'Summary Unavailable'
238
239def NSTimeZone_SummaryProvider (valobj,dict):
240	logger = lldb.formatters.Logger.Logger()
241	provider = GetSummary_Impl(valobj);
242	if provider != None:
243		if isinstance(provider,lldb.runtime.objc.objc_runtime.SpecialSituation_Description):
244			return provider.message()
245		try:
246			summary = provider.timezone();
247		except:
248			summary = None
249		logger >> "got summary " + str(summary)
250		if summary == None:
251			summary = '<variable is not NSTimeZone>'
252		return str(summary)
253	return 'Summary Unavailable'
254
255
256def CFAbsoluteTime_SummaryProvider (valobj,dict):
257	logger = lldb.formatters.Logger.Logger()
258	try:
259		value_double = struct.unpack('d', struct.pack('Q', valobj.GetData().uint64[0]))[0]
260		return xcode_format_count(osx_to_python_time(value_double))
261	except:
262		return 'Summary Unavailable'
263
264
265def __lldb_init_module(debugger,dict):
266	debugger.HandleCommand("type summary add -F NSDate.NSDate_SummaryProvider NSDate")
267	debugger.HandleCommand("type summary add -F NSDate.CFAbsoluteTime_SummaryProvider CFAbsoluteTime")
268	debugger.HandleCommand("type summary add -F NSDate.NSTimeZone_SummaryProvider NSTimeZone CFTimeZoneRef")
269
270