gecko-dev/extensions/python/xpcom/xpt.py
2004-08-25 23:02:50 +00:00

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)