Back to index

lightning-sunbird  0.9+nobinonly
xpt.py
Go to the documentation of this file.
00001 # ***** BEGIN LICENSE BLOCK *****
00002 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003 #
00004 # The contents of this file are subject to the Mozilla Public License Version
00005 # 1.1 (the "License"); you may not use this file except in compliance with
00006 # the License. You may obtain a copy of the License at
00007 # http://www.mozilla.org/MPL/
00008 #
00009 # Software distributed under the License is distributed on an "AS IS" basis,
00010 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011 # for the specific language governing rights and limitations under the
00012 # License.
00013 #
00014 # The Original Code is the Python XPCOM language bindings.
00015 #
00016 # The Initial Developer of the Original Code is
00017 # ActiveState Tool Corp.
00018 # Portions created by the Initial Developer are Copyright (C) 2000, 2001
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 #   David Ascher <DavidA@ActiveState.com> (original author)
00023 #   Mark Hammond <mhammond@skippinet.com.au>
00024 #
00025 # Alternatively, the contents of this file may be used under the terms of
00026 # either the GNU General Public License Version 2 or later (the "GPL"), or
00027 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028 # in which case the provisions of the GPL or the LGPL are applicable instead
00029 # of those above. If you wish to allow use of your version of this file only
00030 # under the terms of either the GPL or the LGPL, and not to allow others to
00031 # use your version of this file under the terms of the MPL, indicate your
00032 # decision by deleting the provisions above and replace them with the notice
00033 # and other provisions required by the GPL or the LGPL. If you do not delete
00034 # the provisions above, a recipient may use your version of this file under
00035 # the terms of any one of the MPL, the GPL or the LGPL.
00036 #
00037 # ***** END LICENSE BLOCK *****
00038 
00039 """
00040 Program: xpt.py
00041 
00042 Task: describe interfaces etc using XPCOM reflection.
00043 
00044 Subtasks:
00045      output (nearly) exactly the same stuff as xpt_dump, for verification
00046      output Python source code that can be used as a template for an interface
00047 
00048 Status: Works pretty well if you ask me :-)
00049 
00050 Author:
00051    David Ascher did an original version that parsed XPT files
00052    directly.  Mark Hammond changed it to use the reflection interfaces,
00053    but kept most of the printing logic.
00054 
00055 
00056 Revision:
00057 
00058   0.1: March 6, 2000
00059   0.2: April 2000 - Mark removed lots of Davids lovely parsing code in favour
00060                     of the new xpcom interfaces that provide this info.
00061 
00062   May 2000 - Moved into Perforce - track the log there!
00063   Early 2001 - Moved into the Mozilla CVS tree - track the log there!  
00064 
00065 Todo:
00066   Fill out this todo list.
00067 
00068 """
00069 
00070 import string, sys
00071 import xpcom
00072 import xpcom._xpcom
00073 
00074 from xpcom_consts import *
00075 
00076 class Interface:
00077     def __init__(self, iid):
00078         iim = xpcom._xpcom.XPTI_GetInterfaceInfoManager()
00079         try:
00080             if hasattr(iid, "upper"): # Is it a stringy thing.
00081                 item = iim.GetInfoForName(iid)
00082             else:
00083                 item = iim.GetInfoForIID(iid)
00084         except xpcom.COMException:
00085             name = getattr(iid, "name", str(iid))
00086             print "Failed to get info for IID '%s'" % (name,)
00087             raise
00088         self.interface_info = item
00089         self.namespace = "" # where does this come from?
00090         self.methods = Methods(item)
00091         self.constants = Constants(item)
00092 
00093     # delegate attributes to the real interface
00094     def __getattr__(self, attr):
00095         return getattr(self.interface_info, attr)
00096 
00097     def GetParent(self):
00098         try:
00099             raw_parent = self.interface_info.GetParent()
00100             if raw_parent is None:
00101                 return None
00102             return Interface(raw_parent.GetIID())
00103         except xpcom.Exception:
00104             # Parent interface is probably not scriptable - assume nsISupports.
00105             if xpcom.verbose:
00106                 # The user may be confused as to why this is happening!
00107                 print "The parent interface of IID '%s' can not be located - assuming nsISupports"
00108             return Interface(xpcom._xpcom.IID_nsISupports)
00109 
00110     def Describe_Python(self):
00111         method_reprs = []
00112         methods = filter(lambda m: not m.IsNotXPCOM(), self.methods)
00113         for m in methods:
00114             method_reprs.append(m.Describe_Python())
00115         method_joiner = "\n"
00116         methods_repr = method_joiner.join(method_reprs)
00117         return \
00118 """class %s:
00119     _com_interfaces_ = xpcom.components.interfaces.%s
00120     # If this object needs to be registered, the following 2 are also needed.
00121     # _reg_clsid_ = "{a new clsid generated for this object}"
00122     # _reg_contractid_ = "The.Object.Name"\n%s""" % (self.GetName(), self.GetIID().name, methods_repr)
00123 
00124     def Describe(self):
00125         # Make the IID look like xtp_dump - "(" instead of "{"
00126         iid_use = "(" + str(self.GetIID())[1:-1] + ")"
00127         s = '   - '+self.namespace+'::'+ self.GetName() + ' ' + iid_use + ':\n'
00128 
00129         parent = self.GetParent()
00130         if parent is not None:
00131             s = s + '      Parent: ' + parent.namespace + '::' + parent.GetName() + '\n'
00132         s = s + '      Flags:\n'
00133         if self.IsScriptable(): word = 'TRUE'
00134         else: word = 'FALSE'
00135         s = s + '         Scriptable: ' + word + '\n'
00136         s = s + '      Methods:\n'
00137         methods = filter(lambda m: not m.IsNotXPCOM(), self.methods)
00138         if len(methods):
00139             for m in methods:
00140                 s = s + '   ' + m.Describe() + '\n'
00141         else:
00142             s = s + '         No Methods\n'
00143         s = s + '      Constants:\n'
00144         if self.constants:
00145             for c in self.constants:
00146                 s = s + '         ' + c.Describe() + '\n'
00147         else:
00148             s = s + '         No Constants\n'
00149 
00150         return s
00151 
00152 # A class that allows caching and iterating of methods.
00153 class Methods:
00154     def __init__(self, interface_info):
00155         self.interface_info = interface_info
00156         try:
00157             self.items = [None] * interface_info.GetMethodCount()
00158         except xpcom.Exception:
00159             if xpcom.verbose:
00160                 print "** GetMethodCount failed?? - assuming no methods"
00161             self.items = []
00162     def __len__(self):
00163         return len(self.items)
00164     def __getitem__(self, index):
00165         ret = self.items[index]
00166         if ret is None:
00167             mi = self.interface_info.GetMethodInfo(index)
00168             ret = self.items[index] = Method(mi, index, self.interface_info)
00169         return ret
00170 
00171 class Method:
00172 
00173     def __init__(self, method_info, method_index, interface_info = None):
00174         self.interface_info = interface_info
00175         self.method_index = method_index
00176         self.flags, self.name, param_descs, self.result_desc = method_info
00177         # Build the params.
00178         self.params = []
00179         pi=0
00180         for pd in param_descs:
00181             self.params.append( Parameter(pd, pi, method_index, interface_info) )
00182             pi = pi + 1
00183         # Run over the params setting the "sizeof" params to hidden.
00184         for p in self.params:
00185             td = p.type_desc
00186             tag = XPT_TDP_TAG(td[0])
00187             if tag==T_ARRAY and p.IsIn():
00188                 self.params[td[1]].hidden_indicator = 2
00189             elif tag in [T_PSTRING_SIZE_IS, T_PWSTRING_SIZE_IS] and p.IsIn():
00190                 self.params[td[1]].hidden_indicator = 1
00191 
00192     def IsGetter(self):
00193         return (self.flags & XPT_MD_GETTER)
00194     def IsSetter(self):
00195         return (self.flags & XPT_MD_SETTER)
00196     def IsNotXPCOM(self):
00197         return (self.flags & XPT_MD_NOTXPCOM)
00198     def IsConstructor(self):
00199         return (self.flags & XPT_MD_CTOR)
00200     def IsHidden(self):
00201         return (self.flags & XPT_MD_HIDDEN)
00202 
00203     def Describe_Python(self):
00204         if self.method_index < 3: # Ignore QI etc
00205             return ""
00206         base_name = self.name
00207         if self.IsGetter():
00208             name = "get_%s" % (base_name,)
00209         elif self.IsSetter():
00210             name = "set_%s" % (base_name,)
00211         else:
00212             name = base_name
00213         param_decls = ["self"]
00214         in_comments = []
00215         out_descs = []
00216         result_comment = "Result: void - None"
00217         for p in self.params:
00218             in_desc, in_desc_comments, out_desc, this_result_comment = p.Describe_Python()
00219             if in_desc is not None:
00220                 param_decls.append(in_desc)
00221             if in_desc_comments is not None:
00222                 in_comments.append(in_desc_comments)
00223             if out_desc is not None:
00224                 out_descs.append(out_desc)
00225             if this_result_comment is not None:
00226                 result_comment = this_result_comment
00227         joiner = "\n        # "
00228         in_comment = out_desc = ""
00229         if in_comments: in_comment = joiner + joiner.join(in_comments)
00230         if out_descs: out_desc = joiner + joiner.join(out_descs)
00231 
00232         return """    def %s( %s ):
00233         # %s%s%s
00234         pass""" % (name, ", ".join(param_decls), result_comment, in_comment, out_desc)
00235 
00236     def Describe(self):
00237         s = ''
00238         if self.IsGetter():
00239             G = 'G'
00240         else:
00241             G = ' '
00242         if self.IsSetter():
00243             S = 'S'
00244         else: S = ' '
00245         if self.IsHidden():
00246             H = 'H'
00247         else:
00248             H = ' '
00249         if self.IsNotXPCOM():
00250             N = 'N'
00251         else:
00252             N = ' '
00253         if self.IsConstructor():
00254             C = 'C'
00255         else:
00256             C = ' '
00257 
00258         def desc(a): return a.Describe()
00259         method_desc = string.join(map(desc, self.params), ', ')
00260         result_type = TypeDescriber(self.result_desc[0], None)
00261         return_desc = result_type.Describe()
00262         i = string.find(return_desc, 'retval ')
00263         if i != -1:
00264             return_desc = return_desc[:i] + return_desc[i+len('retval '):]
00265         return G+S+H+N+C+' '+return_desc+' '+self.name + '('+ method_desc + ');'
00266 
00267 class Parameter:
00268     def __init__(self,  param_desc, param_index, method_index, interface_info = None):
00269         self.param_flags, self.type_desc = param_desc
00270         self.hidden_indicator = 0 # Is this a special "size" type param that will be hidden from Python?
00271         self.param_index = param_index
00272         self.method_index= method_index
00273         self.interface_info = interface_info
00274     def __repr__(self):
00275         return "<param %(param_index)d (method %(method_index)d) - flags = 0x%(param_flags)x, type = %(type_desc)s>" % self.__dict__
00276     def IsIn(self):
00277         return XPT_PD_IS_IN(self.param_flags)
00278     def IsOut(self):
00279         return XPT_PD_IS_OUT(self.param_flags)
00280     def IsInOut(self):
00281         return self.IsIn() and self.IsOut()
00282     def IsRetval(self):
00283         return XPT_PD_IS_RETVAL(self.param_flags)
00284     def IsShared(self):
00285         return XPT_PD_IS_SHARED(self.param_flags)
00286     def IsDipper(self):
00287         return XPT_PD_IS_DIPPER(self.param_flags)
00288 
00289     def Describe_Python(self):
00290         name = "param%d" % (self.param_index,)
00291         if self.hidden_indicator:
00292             # Could remove the comment - Im trying to tell the user where that param has
00293             # gone from the signature!
00294             return None, "%s is a hidden parameter" % (name,), None, None
00295         t = TypeDescriber(self.type_desc[0], self)
00296         decl = in_comment = out_comment = result_comment = None
00297         type_desc = t.Describe()
00298         if self.IsIn() and not self.IsDipper():
00299             decl = name
00300             extra=""
00301             if self.IsOut():
00302                 extra = "Out"
00303             in_comment = "In%s: %s: %s" % (extra, name, type_desc)
00304         elif self.IsOut() or self.IsDipper():
00305             if self.IsRetval():
00306                 result_comment = "Result: %s" % (type_desc,)
00307             else:
00308                 out_comment = "Out: %s" % (type_desc,)
00309         return decl, in_comment, out_comment, result_comment
00310 
00311     def Describe(self):
00312         parts = []
00313         if self.IsInOut():
00314             parts.append('inout')
00315         elif self.IsIn():
00316             parts.append('in')
00317         elif self.IsOut():
00318             parts.append('out')
00319 
00320         if self.IsDipper(): parts.append("dipper")
00321         if self.IsRetval(): parts.append('retval')
00322         if self.IsShared(): parts.append('shared')
00323         t = TypeDescriber(self.type_desc[0], self)
00324         type_str = t.Describe()
00325         parts.append(type_str)
00326         return string.join(parts)
00327 
00328 # A class that allows caching and iterating of constants.
00329 class Constants:
00330     def __init__(self, interface_info):
00331         self.interface_info = interface_info
00332         try:
00333             self.items = [None] * interface_info.GetConstantCount()
00334         except xpcom.Exception:
00335             if xpcom.verbose:
00336                 print "** GetConstantCount failed?? - assuming no constants"
00337             self.items = []
00338     def __len__(self):
00339         return len(self.items)
00340     def __getitem__(self, index):
00341         ret = self.items[index]
00342         if ret is None:
00343             ci = self.interface_info.GetConstant(index)
00344             ret = self.items[index] = Constant(ci)
00345         return ret
00346 
00347 class Constant:
00348     def __init__(self, ci):
00349         self.name, self.type, self.value = ci
00350 
00351     def Describe(self):
00352         return TypeDescriber(self.type, None).Describe() + ' ' +self.name+' = '+str(self.value)+';'
00353 
00354     __str__ = Describe
00355 
00356 def MakeReprForInvoke(param):
00357     tag = param.type_desc[0] & XPT_TDP_TAGMASK
00358     if tag == T_INTERFACE:
00359         i_info = param.interface_info
00360         try:
00361             iid = i_info.GetIIDForParam(param.method_index, param.param_index)
00362         except xpcom.Exception:
00363             # IID not available (probably not scriptable) - just use nsISupports.
00364             iid = xpcom._xpcom.IID_nsISupports
00365         return param.type_desc[0], 0, 0, str(iid)
00366     elif tag == T_ARRAY:
00367         i_info = param.interface_info
00368         array_desc = i_info.GetTypeForParam(param.method_index, param.param_index, 1)
00369         return param.type_desc[:-1] + array_desc[:1]
00370     return param.type_desc
00371 
00372 
00373 class TypeDescriber:
00374     def __init__(self, type_flags, param):
00375         self.type_flags = type_flags
00376         self.tag = XPT_TDP_TAG(self.type_flags)
00377         self.param = param
00378     def IsPointer(self):
00379         return XPT_TDP_IS_POINTER(self.type_flags)
00380     def IsUniquePointer(self):
00381         return XPT_TDP_IS_UNIQUE_POINTER(self.type_flags)
00382     def IsReference(self):
00383         return XPT_TDP_IS_REFERENCE(self.type_flags)
00384     def repr_for_invoke(self):
00385         return (self.type_flags,)
00386     def GetName(self):
00387         is_ptr = self.IsPointer()
00388         data = type_info_map.get(self.tag)
00389         if data is None:
00390             data = ("unknown",)
00391         if self.IsReference():
00392             if len(data) > 2:
00393                 return data[2]
00394             return data[0] + " &"
00395         if self.IsPointer():
00396             if len(data)>1:
00397                 return data[1]
00398             return data[0] + " *"
00399         return data[0]
00400 
00401     def Describe(self):
00402         if self.tag == T_ARRAY:
00403             # NOTE - Adding a type specifier to the array is different from xpt_dump.exe
00404             if self.param is None or self.param.interface_info is None:
00405                 type_desc = "" # Dont have explicit info about the array type :-(
00406             else:
00407                 i_info = self.param.interface_info
00408                 type_code = i_info.GetTypeForParam(self.param.method_index, self.param.param_index, 1)
00409                 type_desc = TypeDescriber( type_code[0], None).Describe()
00410             return self.GetName() + "[" + type_desc + "]" 
00411         elif self.tag == T_INTERFACE:
00412             if self.param is None or self.param.interface_info is None:
00413                 return "nsISomething" # Dont have explicit info about the IID :-(
00414             i_info = self.param.interface_info
00415             m_index = self.param.method_index
00416             p_index = self.param.param_index
00417             try:
00418                 iid = i_info.GetIIDForParam(m_index, p_index)
00419                 return iid.name
00420             except xpcom.Exception:
00421                 return "nsISomething"
00422         return self.GetName()
00423 
00424 # These are just for output purposes, so should be
00425 # the same as xpt_dump uses
00426 type_info_map = {
00427     T_I8                : ("int8",),
00428     T_I16               : ("int16",),
00429     T_I32               : ("int32",),
00430     T_I64               : ("int64",),
00431     T_U8                : ("uint8",),
00432     T_U16               : ("uint16",),
00433     T_U32               : ("uint32",),
00434     T_U64               : ("uint64",),
00435     T_FLOAT             : ("float",),
00436     T_DOUBLE            : ("double",),
00437     T_BOOL              : ("boolean",),
00438     T_CHAR              : ("char",),
00439     T_WCHAR             : ("wchar_t", "wstring"),
00440     T_VOID              : ("void",),
00441     T_IID               : ("reserved", "nsIID *", "nsIID &"),
00442     T_DOMSTRING         : ("DOMString",),
00443     T_CHAR_STR          : ("reserved", "string"),
00444     T_WCHAR_STR         : ("reserved", "wstring"),
00445     T_INTERFACE         : ("reserved", "Interface"),
00446     T_INTERFACE_IS      : ("reserved", "InterfaceIs *"),
00447     T_ARRAY             : ("reserved", "Array"),
00448     T_PSTRING_SIZE_IS   : ("reserved", "string_s"),
00449     T_PWSTRING_SIZE_IS  : ("reserved", "wstring_s"),
00450 }
00451 
00452 def dump_interface(iid, mode):
00453     interface = Interface(iid)
00454     describer_name = "Describe"
00455     if mode == "xptinfo": mode = None
00456     if mode is not None:
00457         describer_name = describer_name + "_" + mode.capitalize()
00458     describer = getattr(interface, describer_name)
00459     print describer()
00460 
00461 if __name__=='__main__':
00462     if len(sys.argv) == 1:
00463         print "Usage: xpt.py [-xptinfo] interface_name, ..."
00464         print "  -info: Dump in a style similar to the xptdump tool"
00465         print "Dumping nsISupports and nsIInterfaceInfo"
00466         sys.argv.append('nsIInterfaceInfo')
00467         sys.argv.append('-xptinfo')
00468         sys.argv.append('nsISupports')
00469         sys.argv.append('nsIInterfaceInfo')
00470 
00471     mode = "Python"
00472     for i in sys.argv[1:]:
00473         if i[0] == "-":
00474             mode = i[1:]
00475         else:
00476             dump_interface(i, mode)