mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
477 lines
17 KiB
Python
477 lines
17 KiB
Python
# ***** BEGIN LICENSE BLOCK *****
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
# http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
#
|
|
# The Original Code is the Python XPCOM language bindings.
|
|
#
|
|
# The Initial Developer of the Original Code is
|
|
# ActiveState Tool Corp.
|
|
# Portions created by the Initial Developer are Copyright (C) 2000, 2001
|
|
# the Initial Developer. All Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
# David Ascher <DavidA@ActiveState.com> (original author)
|
|
# Mark Hammond <mhammond@skippinet.com.au>
|
|
#
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this file under
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
#
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
"""
|
|
Program: xpt.py
|
|
|
|
Task: describe interfaces etc using XPCOM reflection.
|
|
|
|
Subtasks:
|
|
output (nearly) exactly the same stuff as xpt_dump, for verification
|
|
output Python source code that can be used as a template for an interface
|
|
|
|
Status: Works pretty well if you ask me :-)
|
|
|
|
Author:
|
|
David Ascher did an original version that parsed XPT files
|
|
directly. Mark Hammond changed it to use the reflection interfaces,
|
|
but kept most of the printing logic.
|
|
|
|
|
|
Revision:
|
|
|
|
0.1: March 6, 2000
|
|
0.2: April 2000 - Mark removed lots of Davids lovely parsing code in favour
|
|
of the new xpcom interfaces that provide this info.
|
|
|
|
May 2000 - Moved into Perforce - track the log there!
|
|
Early 2001 - Moved into the Mozilla CVS tree - track the log there!
|
|
|
|
Todo:
|
|
Fill out this todo list.
|
|
|
|
"""
|
|
|
|
import string, sys
|
|
import xpcom
|
|
import xpcom._xpcom
|
|
|
|
from xpcom_consts import *
|
|
|
|
class Interface:
|
|
def __init__(self, iid):
|
|
iim = xpcom._xpcom.XPTI_GetInterfaceInfoManager()
|
|
try:
|
|
if hasattr(iid, "upper"): # Is it a stringy thing.
|
|
item = iim.GetInfoForName(iid)
|
|
else:
|
|
item = iim.GetInfoForIID(iid)
|
|
except xpcom.COMException:
|
|
name = getattr(iid, "name", str(iid))
|
|
print "Failed to get info for IID '%s'" % (name,)
|
|
raise
|
|
self.interface_info = item
|
|
self.namespace = "" # where does this come from?
|
|
self.methods = Methods(item)
|
|
self.constants = Constants(item)
|
|
|
|
# delegate attributes to the real interface
|
|
def __getattr__(self, attr):
|
|
return getattr(self.interface_info, attr)
|
|
|
|
def GetParent(self):
|
|
try:
|
|
raw_parent = self.interface_info.GetParent()
|
|
if raw_parent is None:
|
|
return None
|
|
return Interface(raw_parent.GetIID())
|
|
except xpcom.Exception:
|
|
# Parent interface is probably not scriptable - assume nsISupports.
|
|
if xpcom.verbose:
|
|
# The user may be confused as to why this is happening!
|
|
print "The parent interface of IID '%s' can not be located - assuming nsISupports"
|
|
return Interface(xpcom._xpcom.IID_nsISupports)
|
|
|
|
def Describe_Python(self):
|
|
method_reprs = []
|
|
methods = filter(lambda m: not m.IsNotXPCOM(), self.methods)
|
|
for m in methods:
|
|
method_reprs.append(m.Describe_Python())
|
|
method_joiner = "\n"
|
|
methods_repr = method_joiner.join(method_reprs)
|
|
return \
|
|
"""class %s:
|
|
_com_interfaces_ = xpcom.components.interfaces.%s
|
|
# If this object needs to be registered, the following 2 are also needed.
|
|
# _reg_clsid_ = "{a new clsid generated for this object}"
|
|
# _reg_contractid_ = "The.Object.Name"\n%s""" % (self.GetName(), self.GetIID().name, methods_repr)
|
|
|
|
def Describe(self):
|
|
# Make the IID look like xtp_dump - "(" instead of "{"
|
|
iid_use = "(" + str(self.GetIID())[1:-1] + ")"
|
|
s = ' - '+self.namespace+'::'+ self.GetName() + ' ' + iid_use + ':\n'
|
|
|
|
parent = self.GetParent()
|
|
if parent is not None:
|
|
s = s + ' Parent: ' + parent.namespace + '::' + parent.GetName() + '\n'
|
|
s = s + ' Flags:\n'
|
|
if self.IsScriptable(): word = 'TRUE'
|
|
else: word = 'FALSE'
|
|
s = s + ' Scriptable: ' + word + '\n'
|
|
s = s + ' Methods:\n'
|
|
methods = filter(lambda m: not m.IsNotXPCOM(), self.methods)
|
|
if len(methods):
|
|
for m in methods:
|
|
s = s + ' ' + m.Describe() + '\n'
|
|
else:
|
|
s = s + ' No Methods\n'
|
|
s = s + ' Constants:\n'
|
|
if self.constants:
|
|
for c in self.constants:
|
|
s = s + ' ' + c.Describe() + '\n'
|
|
else:
|
|
s = s + ' No Constants\n'
|
|
|
|
return s
|
|
|
|
# A class that allows caching and iterating of methods.
|
|
class Methods:
|
|
def __init__(self, interface_info):
|
|
self.interface_info = interface_info
|
|
try:
|
|
self.items = [None] * interface_info.GetMethodCount()
|
|
except xpcom.Exception:
|
|
if xpcom.verbose:
|
|
print "** GetMethodCount failed?? - assuming no methods"
|
|
self.items = []
|
|
def __len__(self):
|
|
return len(self.items)
|
|
def __getitem__(self, index):
|
|
ret = self.items[index]
|
|
if ret is None:
|
|
mi = self.interface_info.GetMethodInfo(index)
|
|
ret = self.items[index] = Method(mi, index, self.interface_info)
|
|
return ret
|
|
|
|
class Method:
|
|
|
|
def __init__(self, method_info, method_index, interface_info = None):
|
|
self.interface_info = interface_info
|
|
self.method_index = method_index
|
|
self.flags, self.name, param_descs, self.result_desc = method_info
|
|
# Build the params.
|
|
self.params = []
|
|
pi=0
|
|
for pd in param_descs:
|
|
self.params.append( Parameter(pd, pi, method_index, interface_info) )
|
|
pi = pi + 1
|
|
# Run over the params setting the "sizeof" params to hidden.
|
|
for p in self.params:
|
|
td = p.type_desc
|
|
tag = XPT_TDP_TAG(td[0])
|
|
if tag==T_ARRAY and p.IsIn():
|
|
self.params[td[1]].hidden_indicator = 2
|
|
elif tag in [T_PSTRING_SIZE_IS, T_PWSTRING_SIZE_IS] and p.IsIn():
|
|
self.params[td[1]].hidden_indicator = 1
|
|
|
|
def IsGetter(self):
|
|
return (self.flags & XPT_MD_GETTER)
|
|
def IsSetter(self):
|
|
return (self.flags & XPT_MD_SETTER)
|
|
def IsNotXPCOM(self):
|
|
return (self.flags & XPT_MD_NOTXPCOM)
|
|
def IsConstructor(self):
|
|
return (self.flags & XPT_MD_CTOR)
|
|
def IsHidden(self):
|
|
return (self.flags & XPT_MD_HIDDEN)
|
|
|
|
def Describe_Python(self):
|
|
if self.method_index < 3: # Ignore QI etc
|
|
return ""
|
|
base_name = self.name
|
|
if self.IsGetter():
|
|
name = "get_%s" % (base_name,)
|
|
elif self.IsSetter():
|
|
name = "set_%s" % (base_name,)
|
|
else:
|
|
name = base_name
|
|
param_decls = ["self"]
|
|
in_comments = []
|
|
out_descs = []
|
|
result_comment = "Result: void - None"
|
|
for p in self.params:
|
|
in_desc, in_desc_comments, out_desc, this_result_comment = p.Describe_Python()
|
|
if in_desc is not None:
|
|
param_decls.append(in_desc)
|
|
if in_desc_comments is not None:
|
|
in_comments.append(in_desc_comments)
|
|
if out_desc is not None:
|
|
out_descs.append(out_desc)
|
|
if this_result_comment is not None:
|
|
result_comment = this_result_comment
|
|
joiner = "\n # "
|
|
in_comment = out_desc = ""
|
|
if in_comments: in_comment = joiner + joiner.join(in_comments)
|
|
if out_descs: out_desc = joiner + joiner.join(out_descs)
|
|
|
|
return """ def %s( %s ):
|
|
# %s%s%s
|
|
pass""" % (name, ", ".join(param_decls), result_comment, in_comment, out_desc)
|
|
|
|
def Describe(self):
|
|
s = ''
|
|
if self.IsGetter():
|
|
G = 'G'
|
|
else:
|
|
G = ' '
|
|
if self.IsSetter():
|
|
S = 'S'
|
|
else: S = ' '
|
|
if self.IsHidden():
|
|
H = 'H'
|
|
else:
|
|
H = ' '
|
|
if self.IsNotXPCOM():
|
|
N = 'N'
|
|
else:
|
|
N = ' '
|
|
if self.IsConstructor():
|
|
C = 'C'
|
|
else:
|
|
C = ' '
|
|
|
|
def desc(a): return a.Describe()
|
|
method_desc = string.join(map(desc, self.params), ', ')
|
|
result_type = TypeDescriber(self.result_desc[0], None)
|
|
return_desc = result_type.Describe()
|
|
i = string.find(return_desc, 'retval ')
|
|
if i != -1:
|
|
return_desc = return_desc[:i] + return_desc[i+len('retval '):]
|
|
return G+S+H+N+C+' '+return_desc+' '+self.name + '('+ method_desc + ');'
|
|
|
|
class Parameter:
|
|
def __init__(self, param_desc, param_index, method_index, interface_info = None):
|
|
self.param_flags, self.type_desc = param_desc
|
|
self.hidden_indicator = 0 # Is this a special "size" type param that will be hidden from Python?
|
|
self.param_index = param_index
|
|
self.method_index= method_index
|
|
self.interface_info = interface_info
|
|
def __repr__(self):
|
|
return "<param %(param_index)d (method %(method_index)d) - flags = 0x%(param_flags)x, type = %(type_desc)s>" % self.__dict__
|
|
def IsIn(self):
|
|
return XPT_PD_IS_IN(self.param_flags)
|
|
def IsOut(self):
|
|
return XPT_PD_IS_OUT(self.param_flags)
|
|
def IsInOut(self):
|
|
return self.IsIn() and self.IsOut()
|
|
def IsRetval(self):
|
|
return XPT_PD_IS_RETVAL(self.param_flags)
|
|
def IsShared(self):
|
|
return XPT_PD_IS_SHARED(self.param_flags)
|
|
def IsDipper(self):
|
|
return XPT_PD_IS_DIPPER(self.param_flags)
|
|
|
|
def Describe_Python(self):
|
|
name = "param%d" % (self.param_index,)
|
|
if self.hidden_indicator:
|
|
# Could remove the comment - Im trying to tell the user where that param has
|
|
# gone from the signature!
|
|
return None, "%s is a hidden parameter" % (name,), None, None
|
|
t = TypeDescriber(self.type_desc[0], self)
|
|
decl = in_comment = out_comment = result_comment = None
|
|
type_desc = t.Describe()
|
|
if self.IsIn() and not self.IsDipper():
|
|
decl = name
|
|
extra=""
|
|
if self.IsOut():
|
|
extra = "Out"
|
|
in_comment = "In%s: %s: %s" % (extra, name, type_desc)
|
|
elif self.IsOut() or self.IsDipper():
|
|
if self.IsRetval():
|
|
result_comment = "Result: %s" % (type_desc,)
|
|
else:
|
|
out_comment = "Out: %s" % (type_desc,)
|
|
return decl, in_comment, out_comment, result_comment
|
|
|
|
def Describe(self):
|
|
parts = []
|
|
if self.IsInOut():
|
|
parts.append('inout')
|
|
elif self.IsIn():
|
|
parts.append('in')
|
|
elif self.IsOut():
|
|
parts.append('out')
|
|
|
|
if self.IsDipper(): parts.append("dipper")
|
|
if self.IsRetval(): parts.append('retval')
|
|
if self.IsShared(): parts.append('shared')
|
|
t = TypeDescriber(self.type_desc[0], self)
|
|
type_str = t.Describe()
|
|
parts.append(type_str)
|
|
return string.join(parts)
|
|
|
|
# A class that allows caching and iterating of constants.
|
|
class Constants:
|
|
def __init__(self, interface_info):
|
|
self.interface_info = interface_info
|
|
try:
|
|
self.items = [None] * interface_info.GetConstantCount()
|
|
except xpcom.Exception:
|
|
if xpcom.verbose:
|
|
print "** GetConstantCount failed?? - assuming no constants"
|
|
self.items = []
|
|
def __len__(self):
|
|
return len(self.items)
|
|
def __getitem__(self, index):
|
|
ret = self.items[index]
|
|
if ret is None:
|
|
ci = self.interface_info.GetConstant(index)
|
|
ret = self.items[index] = Constant(ci)
|
|
return ret
|
|
|
|
class Constant:
|
|
def __init__(self, ci):
|
|
self.name, self.type, self.value = ci
|
|
|
|
def Describe(self):
|
|
return TypeDescriber(self.type, None).Describe() + ' ' +self.name+' = '+str(self.value)+';'
|
|
|
|
__str__ = Describe
|
|
|
|
def MakeReprForInvoke(param):
|
|
tag = param.type_desc[0] & XPT_TDP_TAGMASK
|
|
if tag == T_INTERFACE:
|
|
i_info = param.interface_info
|
|
try:
|
|
iid = i_info.GetIIDForParam(param.method_index, param.param_index)
|
|
except xpcom.Exception:
|
|
# IID not available (probably not scriptable) - just use nsISupports.
|
|
iid = xpcom._xpcom.IID_nsISupports
|
|
return param.type_desc[0], 0, 0, str(iid)
|
|
elif tag == T_ARRAY:
|
|
i_info = param.interface_info
|
|
array_desc = i_info.GetTypeForParam(param.method_index, param.param_index, 1)
|
|
return param.type_desc[:-1] + array_desc[:1]
|
|
return param.type_desc
|
|
|
|
|
|
class TypeDescriber:
|
|
def __init__(self, type_flags, param):
|
|
self.type_flags = type_flags
|
|
self.tag = XPT_TDP_TAG(self.type_flags)
|
|
self.param = param
|
|
def IsPointer(self):
|
|
return XPT_TDP_IS_POINTER(self.type_flags)
|
|
def IsUniquePointer(self):
|
|
return XPT_TDP_IS_UNIQUE_POINTER(self.type_flags)
|
|
def IsReference(self):
|
|
return XPT_TDP_IS_REFERENCE(self.type_flags)
|
|
def repr_for_invoke(self):
|
|
return (self.type_flags,)
|
|
def GetName(self):
|
|
is_ptr = self.IsPointer()
|
|
data = type_info_map.get(self.tag)
|
|
if data is None:
|
|
data = ("unknown",)
|
|
if self.IsReference():
|
|
if len(data) > 2:
|
|
return data[2]
|
|
return data[0] + " &"
|
|
if self.IsPointer():
|
|
if len(data)>1:
|
|
return data[1]
|
|
return data[0] + " *"
|
|
return data[0]
|
|
|
|
def Describe(self):
|
|
if self.tag == T_ARRAY:
|
|
# NOTE - Adding a type specifier to the array is different from xpt_dump.exe
|
|
if self.param is None or self.param.interface_info is None:
|
|
type_desc = "" # Dont have explicit info about the array type :-(
|
|
else:
|
|
i_info = self.param.interface_info
|
|
type_code = i_info.GetTypeForParam(self.param.method_index, self.param.param_index, 1)
|
|
type_desc = TypeDescriber( type_code[0], None).Describe()
|
|
return self.GetName() + "[" + type_desc + "]"
|
|
elif self.tag == T_INTERFACE:
|
|
if self.param is None or self.param.interface_info is None:
|
|
return "nsISomething" # Dont have explicit info about the IID :-(
|
|
i_info = self.param.interface_info
|
|
m_index = self.param.method_index
|
|
p_index = self.param.param_index
|
|
try:
|
|
iid = i_info.GetIIDForParam(m_index, p_index)
|
|
return iid.name
|
|
except xpcom.Exception:
|
|
return "nsISomething"
|
|
return self.GetName()
|
|
|
|
# These are just for output purposes, so should be
|
|
# the same as xpt_dump uses
|
|
type_info_map = {
|
|
T_I8 : ("int8",),
|
|
T_I16 : ("int16",),
|
|
T_I32 : ("int32",),
|
|
T_I64 : ("int64",),
|
|
T_U8 : ("uint8",),
|
|
T_U16 : ("uint16",),
|
|
T_U32 : ("uint32",),
|
|
T_U64 : ("uint64",),
|
|
T_FLOAT : ("float",),
|
|
T_DOUBLE : ("double",),
|
|
T_BOOL : ("boolean",),
|
|
T_CHAR : ("char",),
|
|
T_WCHAR : ("wchar_t", "wstring"),
|
|
T_VOID : ("void",),
|
|
T_IID : ("reserved", "nsIID *", "nsIID &"),
|
|
T_DOMSTRING : ("DOMString",),
|
|
T_CHAR_STR : ("reserved", "string"),
|
|
T_WCHAR_STR : ("reserved", "wstring"),
|
|
T_INTERFACE : ("reserved", "Interface"),
|
|
T_INTERFACE_IS : ("reserved", "InterfaceIs *"),
|
|
T_ARRAY : ("reserved", "Array"),
|
|
T_PSTRING_SIZE_IS : ("reserved", "string_s"),
|
|
T_PWSTRING_SIZE_IS : ("reserved", "wstring_s"),
|
|
}
|
|
|
|
def dump_interface(iid, mode):
|
|
interface = Interface(iid)
|
|
describer_name = "Describe"
|
|
if mode == "xptinfo": mode = None
|
|
if mode is not None:
|
|
describer_name = describer_name + "_" + mode.capitalize()
|
|
describer = getattr(interface, describer_name)
|
|
print describer()
|
|
|
|
if __name__=='__main__':
|
|
if len(sys.argv) == 1:
|
|
print "Usage: xpt.py [-xptinfo] interface_name, ..."
|
|
print " -info: Dump in a style similar to the xptdump tool"
|
|
print "Dumping nsISupports and nsIInterfaceInfo"
|
|
sys.argv.append('nsIInterfaceInfo')
|
|
sys.argv.append('-xptinfo')
|
|
sys.argv.append('nsISupports')
|
|
sys.argv.append('nsIInterfaceInfo')
|
|
|
|
mode = "Python"
|
|
for i in sys.argv[1:]:
|
|
if i[0] == "-":
|
|
mode = i[1:]
|
|
else:
|
|
dump_interface(i, mode)
|