1 | # ***** BEGIN LICENSE BLOCK *****
|
---|
2 | # Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
---|
3 | #
|
---|
4 | # The contents of this file are subject to the Mozilla Public License Version
|
---|
5 | # 1.1 (the "License"); you may not use this file except in compliance with
|
---|
6 | # the License. You may obtain a copy of the License at
|
---|
7 | # http://www.mozilla.org/MPL/
|
---|
8 | #
|
---|
9 | # Software distributed under the License is distributed on an "AS IS" basis,
|
---|
10 | # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
---|
11 | # for the specific language governing rights and limitations under the
|
---|
12 | # License.
|
---|
13 | #
|
---|
14 | # The Original Code is the Python XPCOM language bindings.
|
---|
15 | #
|
---|
16 | # The Initial Developer of the Original Code is
|
---|
17 | # ActiveState Tool Corp.
|
---|
18 | # Portions created by the Initial Developer are Copyright (C) 2000, 2001
|
---|
19 | # the Initial Developer. All Rights Reserved.
|
---|
20 | #
|
---|
21 | # Contributor(s):
|
---|
22 | # David Ascher <DavidA@ActiveState.com> (original author)
|
---|
23 | # Mark Hammond <mhammond@skippinet.com.au>
|
---|
24 | #
|
---|
25 | # Alternatively, the contents of this file may be used under the terms of
|
---|
26 | # either the GNU General Public License Version 2 or later (the "GPL"), or
|
---|
27 | # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
---|
28 | # in which case the provisions of the GPL or the LGPL are applicable instead
|
---|
29 | # of those above. If you wish to allow use of your version of this file only
|
---|
30 | # under the terms of either the GPL or the LGPL, and not to allow others to
|
---|
31 | # use your version of this file under the terms of the MPL, indicate your
|
---|
32 | # decision by deleting the provisions above and replace them with the notice
|
---|
33 | # and other provisions required by the GPL or the LGPL. If you do not delete
|
---|
34 | # the provisions above, a recipient may use your version of this file under
|
---|
35 | # the terms of any one of the MPL, the GPL or the LGPL.
|
---|
36 | #
|
---|
37 | # ***** END LICENSE BLOCK *****
|
---|
38 |
|
---|
39 | """
|
---|
40 | Program: xpt.py
|
---|
41 |
|
---|
42 | Task: describe interfaces etc using XPCOM reflection.
|
---|
43 |
|
---|
44 | Subtasks:
|
---|
45 | output (nearly) exactly the same stuff as xpt_dump, for verification
|
---|
46 | output Python source code that can be used as a template for an interface
|
---|
47 |
|
---|
48 | Status: Works pretty well if you ask me :-)
|
---|
49 |
|
---|
50 | Author:
|
---|
51 | David Ascher did an original version that parsed XPT files
|
---|
52 | directly. Mark Hammond changed it to use the reflection interfaces,
|
---|
53 | but kept most of the printing logic.
|
---|
54 |
|
---|
55 |
|
---|
56 | Revision:
|
---|
57 |
|
---|
58 | 0.1: March 6, 2000
|
---|
59 | 0.2: April 2000 - Mark removed lots of Davids lovely parsing code in favour
|
---|
60 | of the new xpcom interfaces that provide this info.
|
---|
61 |
|
---|
62 | May 2000 - Moved into Perforce - track the log there!
|
---|
63 | Early 2001 - Moved into the Mozilla CVS tree - track the log there!
|
---|
64 |
|
---|
65 | Todo:
|
---|
66 | Fill out this todo list.
|
---|
67 |
|
---|
68 | """
|
---|
69 |
|
---|
70 | import string, sys
|
---|
71 | import xpcom
|
---|
72 | import xpcom._xpcom
|
---|
73 |
|
---|
74 | from .xpcom_consts import *
|
---|
75 |
|
---|
76 | class Interface:
|
---|
77 | def __init__(self, iid):
|
---|
78 | iim = xpcom._xpcom.XPTI_GetInterfaceInfoManager()
|
---|
79 | if hasattr(iid, "upper"): # Is it a stringy thing.
|
---|
80 | item = iim.GetInfoForName(iid)
|
---|
81 | else:
|
---|
82 | item = iim.GetInfoForIID(iid)
|
---|
83 | self.interface_info = item
|
---|
84 | self.namespace = "" # where does this come from?
|
---|
85 | self.methods = Methods(item)
|
---|
86 | self.constants = Constants(item)
|
---|
87 |
|
---|
88 | # delegate attributes to the real interface
|
---|
89 | def __getattr__(self, attr):
|
---|
90 | return getattr(self.interface_info, attr)
|
---|
91 |
|
---|
92 | def GetParent(self):
|
---|
93 | try:
|
---|
94 | raw_parent = self.interface_info.GetParent()
|
---|
95 | if raw_parent is None:
|
---|
96 | return None
|
---|
97 | return Interface(raw_parent.GetIID())
|
---|
98 | except xpcom.Exception:
|
---|
99 | # Parent interface is probably not scriptable - assume nsISupports.
|
---|
100 | if xpcom.verbose:
|
---|
101 | # The user may be confused as to why this is happening!
|
---|
102 | print("The parent interface of IID '%s' can not be located - assuming nsISupports")
|
---|
103 | return Interface(xpcom._xpcom.IID_nsISupports)
|
---|
104 |
|
---|
105 | def Describe_Python(self):
|
---|
106 | method_reprs = []
|
---|
107 | methods = [m for m in self.methods if not m.IsNotXPCOM()]
|
---|
108 | for m in methods:
|
---|
109 | method_reprs.append(m.Describe_Python())
|
---|
110 | method_joiner = "\n"
|
---|
111 | methods_repr = method_joiner.join(method_reprs)
|
---|
112 | return \
|
---|
113 | """class %s:
|
---|
114 | _com_interfaces_ = xpcom.components.interfaces.%s
|
---|
115 | # If this object needs to be registered, the following 2 are also needed.
|
---|
116 | # _reg_clsid_ = "{a new clsid generated for this object}"
|
---|
117 | # _reg_contractid_ = "The.Object.Name"\n%s""" % (self.GetName(), self.GetIID().name, methods_repr)
|
---|
118 |
|
---|
119 | def Describe(self):
|
---|
120 | # Make the IID look like xtp_dump - "(" instead of "{"
|
---|
121 | iid_use = "(" + str(self.GetIID())[1:-1] + ")"
|
---|
122 | s = ' - '+self.namespace+'::'+ self.GetName() + ' ' + iid_use + ':\n'
|
---|
123 |
|
---|
124 | parent = self.GetParent()
|
---|
125 | if parent is not None:
|
---|
126 | s = s + ' Parent: ' + parent.namespace + '::' + parent.GetName() + '\n'
|
---|
127 | s = s + ' Flags:\n'
|
---|
128 | if self.IsScriptable(): word = 'TRUE'
|
---|
129 | else: word = 'FALSE'
|
---|
130 | s = s + ' Scriptable: ' + word + '\n'
|
---|
131 | s = s + ' Methods:\n'
|
---|
132 | methods = [m for m in self.methods if not m.IsNotXPCOM()]
|
---|
133 | if len(methods):
|
---|
134 | for m in methods:
|
---|
135 | s = s + ' ' + m.Describe() + '\n'
|
---|
136 | else:
|
---|
137 | s = s + ' No Methods\n'
|
---|
138 | s = s + ' Constants:\n'
|
---|
139 | if self.constants:
|
---|
140 | for c in self.constants:
|
---|
141 | s = s + ' ' + c.Describe() + '\n'
|
---|
142 | else:
|
---|
143 | s = s + ' No Constants\n'
|
---|
144 |
|
---|
145 | return s
|
---|
146 |
|
---|
147 | # A class that allows caching and iterating of methods.
|
---|
148 | class Methods:
|
---|
149 | def __init__(self, interface_info):
|
---|
150 | self.interface_info = interface_info
|
---|
151 | try:
|
---|
152 | self.items = [None] * interface_info.GetMethodCount()
|
---|
153 | except xpcom.Exception:
|
---|
154 | if xpcom.verbose:
|
---|
155 | print("** GetMethodCount failed?? - assuming no methods")
|
---|
156 | self.items = []
|
---|
157 | def __len__(self):
|
---|
158 | return len(self.items)
|
---|
159 | def __getitem__(self, index):
|
---|
160 | ret = self.items[index]
|
---|
161 | if ret is None:
|
---|
162 | mi = self.interface_info.GetMethodInfo(index)
|
---|
163 | ret = self.items[index] = Method(mi, index, self.interface_info)
|
---|
164 | return ret
|
---|
165 |
|
---|
166 | class Method:
|
---|
167 |
|
---|
168 | def __init__(self, method_info, method_index, interface_info = None):
|
---|
169 | self.interface_info = interface_info
|
---|
170 | self.method_index = method_index
|
---|
171 | self.flags, self.name, param_descs, self.result_desc = method_info
|
---|
172 | # Build the params.
|
---|
173 | self.params = []
|
---|
174 | pi=0
|
---|
175 | for pd in param_descs:
|
---|
176 | self.params.append( Parameter(pd, pi, method_index, interface_info) )
|
---|
177 | pi = pi + 1
|
---|
178 | # Run over the params setting the "sizeof" params to hidden.
|
---|
179 | for p in self.params:
|
---|
180 | td = p.type_desc
|
---|
181 | tag = XPT_TDP_TAG(td[0])
|
---|
182 | if tag==T_ARRAY and p.IsIn():
|
---|
183 | self.params[td[1]].hidden_indicator = 2
|
---|
184 | elif tag in [T_PSTRING_SIZE_IS, T_PWSTRING_SIZE_IS] and p.IsIn():
|
---|
185 | self.params[td[1]].hidden_indicator = 1
|
---|
186 |
|
---|
187 | def IsGetter(self):
|
---|
188 | return (self.flags & XPT_MD_GETTER)
|
---|
189 | def IsSetter(self):
|
---|
190 | return (self.flags & XPT_MD_SETTER)
|
---|
191 | def IsNotXPCOM(self):
|
---|
192 | return (self.flags & XPT_MD_NOTXPCOM)
|
---|
193 | def IsConstructor(self):
|
---|
194 | return (self.flags & XPT_MD_CTOR)
|
---|
195 | def IsHidden(self):
|
---|
196 | return (self.flags & XPT_MD_HIDDEN)
|
---|
197 |
|
---|
198 | def Describe_Python(self):
|
---|
199 | if self.method_index < 3: # Ignore QI etc
|
---|
200 | return ""
|
---|
201 | base_name = self.name
|
---|
202 | if self.IsGetter():
|
---|
203 | name = "get_%s" % (base_name,)
|
---|
204 | elif self.IsSetter():
|
---|
205 | name = "set_%s" % (base_name,)
|
---|
206 | else:
|
---|
207 | name = base_name
|
---|
208 | param_decls = ["self"]
|
---|
209 | in_comments = []
|
---|
210 | out_descs = []
|
---|
211 | result_comment = "Result: void - None"
|
---|
212 | for p in self.params:
|
---|
213 | in_desc, in_desc_comments, out_desc, this_result_comment = p.Describe_Python()
|
---|
214 | if in_desc is not None:
|
---|
215 | param_decls.append(in_desc)
|
---|
216 | if in_desc_comments is not None:
|
---|
217 | in_comments.append(in_desc_comments)
|
---|
218 | if out_desc is not None:
|
---|
219 | out_descs.append(out_desc)
|
---|
220 | if this_result_comment is not None:
|
---|
221 | result_comment = this_result_comment
|
---|
222 | joiner = "\n # "
|
---|
223 | in_comment = out_desc = ""
|
---|
224 | if in_comments: in_comment = joiner + joiner.join(in_comments)
|
---|
225 | if out_descs: out_desc = joiner + joiner.join(out_descs)
|
---|
226 |
|
---|
227 | return """ def %s( %s ):
|
---|
228 | # %s%s%s
|
---|
229 | pass""" % (name, ", ".join(param_decls), result_comment, in_comment, out_desc)
|
---|
230 |
|
---|
231 | def Describe(self):
|
---|
232 | s = ''
|
---|
233 | if self.IsGetter():
|
---|
234 | G = 'G'
|
---|
235 | else:
|
---|
236 | G = ' '
|
---|
237 | if self.IsSetter():
|
---|
238 | S = 'S'
|
---|
239 | else: S = ' '
|
---|
240 | if self.IsHidden():
|
---|
241 | H = 'H'
|
---|
242 | else:
|
---|
243 | H = ' '
|
---|
244 | if self.IsNotXPCOM():
|
---|
245 | N = 'N'
|
---|
246 | else:
|
---|
247 | N = ' '
|
---|
248 | if self.IsConstructor():
|
---|
249 | C = 'C'
|
---|
250 | else:
|
---|
251 | C = ' '
|
---|
252 |
|
---|
253 | def desc(a): return a.Describe()
|
---|
254 | method_desc = string.join(list(map(desc, self.params)), ', ')
|
---|
255 | result_type = TypeDescriber(self.result_desc[0], None)
|
---|
256 | return_desc = result_type.Describe()
|
---|
257 | i = string.find(return_desc, 'retval ')
|
---|
258 | if i != -1:
|
---|
259 | return_desc = return_desc[:i] + return_desc[i+len('retval '):]
|
---|
260 | return G+S+H+N+C+' '+return_desc+' '+self.name + '('+ method_desc + ');'
|
---|
261 |
|
---|
262 | class Parameter:
|
---|
263 | def __init__(self, param_desc, param_index, method_index, interface_info = None):
|
---|
264 | self.param_flags, self.type_desc = param_desc
|
---|
265 | self.hidden_indicator = 0 # Is this a special "size" type param that will be hidden from Python?
|
---|
266 | self.param_index = param_index
|
---|
267 | self.method_index= method_index
|
---|
268 | self.interface_info = interface_info
|
---|
269 | def __repr__(self):
|
---|
270 | return "<param %(param_index)d (method %(method_index)d) - flags = 0x%(param_flags)x, type = %(type_desc)s>" % self.__dict__
|
---|
271 | def IsIn(self):
|
---|
272 | return XPT_PD_IS_IN(self.param_flags)
|
---|
273 | def IsOut(self):
|
---|
274 | return XPT_PD_IS_OUT(self.param_flags)
|
---|
275 | def IsInOut(self):
|
---|
276 | return self.IsIn() and self.IsOut()
|
---|
277 | def IsRetval(self):
|
---|
278 | return XPT_PD_IS_RETVAL(self.param_flags)
|
---|
279 | def IsShared(self):
|
---|
280 | return XPT_PD_IS_SHARED(self.param_flags)
|
---|
281 | def IsDipper(self):
|
---|
282 | return XPT_PD_IS_DIPPER(self.param_flags)
|
---|
283 |
|
---|
284 | def Describe_Python(self):
|
---|
285 | name = "param%d" % (self.param_index,)
|
---|
286 | if self.hidden_indicator:
|
---|
287 | # Could remove the comment - Im trying to tell the user where that param has
|
---|
288 | # gone from the signature!
|
---|
289 | return None, "%s is a hidden parameter" % (name,), None, None
|
---|
290 | t = TypeDescriber(self.type_desc[0], self)
|
---|
291 | decl = in_comment = out_comment = result_comment = None
|
---|
292 | type_desc = t.Describe()
|
---|
293 | if self.IsIn() and not self.IsDipper():
|
---|
294 | decl = name
|
---|
295 | extra=""
|
---|
296 | if self.IsOut():
|
---|
297 | extra = "Out"
|
---|
298 | in_comment = "In%s: %s: %s" % (extra, name, type_desc)
|
---|
299 | elif self.IsOut() or self.IsDipper():
|
---|
300 | if self.IsRetval():
|
---|
301 | result_comment = "Result: %s" % (type_desc,)
|
---|
302 | else:
|
---|
303 | out_comment = "Out: %s" % (type_desc,)
|
---|
304 | return decl, in_comment, out_comment, result_comment
|
---|
305 |
|
---|
306 | def Describe(self):
|
---|
307 | parts = []
|
---|
308 | if self.IsInOut():
|
---|
309 | parts.append('inout')
|
---|
310 | elif self.IsIn():
|
---|
311 | parts.append('in')
|
---|
312 | elif self.IsOut():
|
---|
313 | parts.append('out')
|
---|
314 |
|
---|
315 | if self.IsDipper(): parts.append("dipper")
|
---|
316 | if self.IsRetval(): parts.append('retval')
|
---|
317 | if self.IsShared(): parts.append('shared')
|
---|
318 | t = TypeDescriber(self.type_desc[0], self)
|
---|
319 | type_str = t.Describe()
|
---|
320 | parts.append(type_str)
|
---|
321 | return string.join(parts)
|
---|
322 |
|
---|
323 | # A class that allows caching and iterating of constants.
|
---|
324 | class Constants:
|
---|
325 | def __init__(self, interface_info):
|
---|
326 | self.interface_info = interface_info
|
---|
327 | try:
|
---|
328 | self.items = [None] * interface_info.GetConstantCount()
|
---|
329 | except xpcom.Exception:
|
---|
330 | if xpcom.verbose:
|
---|
331 | print("** GetConstantCount failed?? - assuming no constants")
|
---|
332 | self.items = []
|
---|
333 | def __len__(self):
|
---|
334 | return len(self.items)
|
---|
335 | def __getitem__(self, index):
|
---|
336 | ret = self.items[index]
|
---|
337 | if ret is None:
|
---|
338 | ci = self.interface_info.GetConstant(index)
|
---|
339 | ret = self.items[index] = Constant(ci)
|
---|
340 | return ret
|
---|
341 |
|
---|
342 | class Constant:
|
---|
343 | def __init__(self, ci):
|
---|
344 | self.name, self.type, self.value = ci
|
---|
345 |
|
---|
346 | def Describe(self):
|
---|
347 | return TypeDescriber(self.type, None).Describe() + ' ' +self.name+' = '+str(self.value)+';'
|
---|
348 |
|
---|
349 | __str__ = Describe
|
---|
350 |
|
---|
351 | def MakeReprForInvoke(param):
|
---|
352 | tag = param.type_desc[0] & XPT_TDP_TAGMASK
|
---|
353 | if tag == T_INTERFACE:
|
---|
354 | i_info = param.interface_info
|
---|
355 | try:
|
---|
356 | iid = i_info.GetIIDForParam(param.method_index, param.param_index)
|
---|
357 | except xpcom.Exception:
|
---|
358 | # IID not available (probably not scriptable) - just use nsISupports.
|
---|
359 | iid = xpcom._xpcom.IID_nsISupports
|
---|
360 | return param.type_desc[0], 0, 0, str(iid)
|
---|
361 | elif tag == T_ARRAY:
|
---|
362 | i_info = param.interface_info
|
---|
363 | array_desc = i_info.GetTypeForParam(param.method_index, param.param_index, 1)
|
---|
364 | return param.type_desc[:-1] + array_desc[:1]
|
---|
365 | return param.type_desc
|
---|
366 |
|
---|
367 |
|
---|
368 | class TypeDescriber:
|
---|
369 | def __init__(self, type_flags, param):
|
---|
370 | self.type_flags = type_flags
|
---|
371 | self.tag = XPT_TDP_TAG(self.type_flags)
|
---|
372 | self.param = param
|
---|
373 | def IsPointer(self):
|
---|
374 | return XPT_TDP_IS_POINTER(self.type_flags)
|
---|
375 | def IsUniquePointer(self):
|
---|
376 | return XPT_TDP_IS_UNIQUE_POINTER(self.type_flags)
|
---|
377 | def IsReference(self):
|
---|
378 | return XPT_TDP_IS_REFERENCE(self.type_flags)
|
---|
379 | def repr_for_invoke(self):
|
---|
380 | return (self.type_flags,)
|
---|
381 | def GetName(self):
|
---|
382 | is_ptr = self.IsPointer()
|
---|
383 | data = type_info_map.get(self.tag)
|
---|
384 | if data is None:
|
---|
385 | data = ("unknown",)
|
---|
386 | if self.IsReference():
|
---|
387 | if len(data) > 2:
|
---|
388 | return data[2]
|
---|
389 | return data[0] + " &"
|
---|
390 | if self.IsPointer():
|
---|
391 | if len(data)>1:
|
---|
392 | return data[1]
|
---|
393 | return data[0] + " *"
|
---|
394 | return data[0]
|
---|
395 |
|
---|
396 | def Describe(self):
|
---|
397 | if self.tag == T_ARRAY:
|
---|
398 | # NOTE - Adding a type specifier to the array is different from xpt_dump.exe
|
---|
399 | if self.param is None or self.param.interface_info is None:
|
---|
400 | type_desc = "" # Dont have explicit info about the array type :-(
|
---|
401 | else:
|
---|
402 | i_info = self.param.interface_info
|
---|
403 | type_code = i_info.GetTypeForParam(self.param.method_index, self.param.param_index, 1)
|
---|
404 | type_desc = TypeDescriber( type_code[0], None).Describe()
|
---|
405 | return self.GetName() + "[" + type_desc + "]"
|
---|
406 | elif self.tag == T_INTERFACE:
|
---|
407 | if self.param is None or self.param.interface_info is None:
|
---|
408 | return "nsISomething" # Dont have explicit info about the IID :-(
|
---|
409 | i_info = self.param.interface_info
|
---|
410 | m_index = self.param.method_index
|
---|
411 | p_index = self.param.param_index
|
---|
412 | try:
|
---|
413 | iid = i_info.GetIIDForParam(m_index, p_index)
|
---|
414 | return iid.name
|
---|
415 | except xpcom.Exception:
|
---|
416 | return "nsISomething"
|
---|
417 | return self.GetName()
|
---|
418 |
|
---|
419 | # These are just for output purposes, so should be
|
---|
420 | # the same as xpt_dump uses
|
---|
421 | type_info_map = {
|
---|
422 | T_I8 : ("int8",),
|
---|
423 | T_I16 : ("int16",),
|
---|
424 | T_I32 : ("int32",),
|
---|
425 | T_I64 : ("int64",),
|
---|
426 | T_U8 : ("uint8",),
|
---|
427 | T_U16 : ("uint16",),
|
---|
428 | T_U32 : ("uint32",),
|
---|
429 | T_U64 : ("uint64",),
|
---|
430 | T_FLOAT : ("float",),
|
---|
431 | T_DOUBLE : ("double",),
|
---|
432 | T_BOOL : ("boolean",),
|
---|
433 | T_CHAR : ("char",),
|
---|
434 | T_WCHAR : ("wchar_t", "wstring"),
|
---|
435 | T_VOID : ("void",),
|
---|
436 | T_IID : ("reserved", "nsIID *", "nsIID &"),
|
---|
437 | T_DOMSTRING : ("DOMString",),
|
---|
438 | T_CHAR_STR : ("reserved", "string"),
|
---|
439 | T_WCHAR_STR : ("reserved", "wstring"),
|
---|
440 | T_INTERFACE : ("reserved", "Interface"),
|
---|
441 | T_INTERFACE_IS : ("reserved", "InterfaceIs *"),
|
---|
442 | T_ARRAY : ("reserved", "Array"),
|
---|
443 | T_PSTRING_SIZE_IS : ("reserved", "string_s"),
|
---|
444 | T_PWSTRING_SIZE_IS : ("reserved", "wstring_s"),
|
---|
445 | }
|
---|
446 |
|
---|
447 | def dump_interface(iid, mode):
|
---|
448 | interface = Interface(iid)
|
---|
449 | describer_name = "Describe"
|
---|
450 | if mode == "xptinfo": mode = None
|
---|
451 | if mode is not None:
|
---|
452 | describer_name = describer_name + "_" + mode.capitalize()
|
---|
453 | describer = getattr(interface, describer_name)
|
---|
454 | print(describer())
|
---|
455 |
|
---|
456 | if __name__=='__main__':
|
---|
457 | if len(sys.argv) == 1:
|
---|
458 | print("Usage: xpt.py [-xptinfo] interface_name, ...")
|
---|
459 | print(" -info: Dump in a style similar to the xptdump tool")
|
---|
460 | print("Dumping nsISupports and nsIInterfaceInfo")
|
---|
461 | sys.argv.append('nsIInterfaceInfo')
|
---|
462 | sys.argv.append('-xptinfo')
|
---|
463 | sys.argv.append('nsISupports')
|
---|
464 | sys.argv.append('nsIInterfaceInfo')
|
---|
465 |
|
---|
466 | mode = "Python"
|
---|
467 | for i in sys.argv[1:]:
|
---|
468 | if i[0] == "-":
|
---|
469 | mode = i[1:]
|
---|
470 | else:
|
---|
471 | dump_interface(i, mode)
|
---|