--- a/xpidl.py
+++ b/xpidl.py
@@ -26,22 +26,44 @@ def attlistToIDL(attlist):
return ''
attlist = list(attlist)
attlist.sort(cmp=lambda a,b: cmp(a[0], b[0]))
return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
for name, value, aloc in attlist])
+_paramsHardcode = {
+ 2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
+ 3: ('array', 'size_is', 'const'),
+}
+
def paramAttlistToIDL(attlist):
if len(attlist) == 0:
return ''
+ # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
+ # quirk
+ attlist = list(attlist)
+ sorted = []
+ if len(attlist) in _paramsHardcode:
+ for p in _paramsHardcode[len(attlist)]:
+ i = 0
+ while i < len(attlist):
+ if attlist[i][0] == p:
+ sorted.append(attlist[i])
+ del attlist[i]
+ continue
+
+ i += 1
+
+ sorted.extend(attlist)
+
return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
- for name, value, aloc in attlist])
+ for name, value, aloc in sorted])
class BuiltinLocation(object):
def get(self):
return "<builtin type>"
def __str__(self):
return self.get()
@@ -67,17 +89,17 @@ class Builtin(object):
else:
const = ''
return "%s%s %s" % (const, self.nativename,
calltype != 'in' and '*' or '')
builtinNames = [
Builtin('boolean', 'PRBool'),
Builtin('void', 'void'),
- Builtin('octet', 'unsigned char'),
+ Builtin('octet', 'PRUint8'),
Builtin('short', 'PRInt16', True, True),
Builtin('long', 'PRInt32', True, True),
Builtin('long long', 'PRInt64', True, False),
Builtin('unsigned short', 'PRUint16', False, True),
Builtin('unsigned long', 'PRUint32', False, True),
Builtin('unsigned long long', 'PRUint64', False, False),
Builtin('float', 'float', True, False),
Builtin('double', 'double', True, False),
@@ -139,38 +161,49 @@ class NameMap(object):
def __getitem__(self, key):
if key in builtinMap:
return builtinMap[key]
return self._d[key]
def __iter__(self):
return self._d.itervalues()
+ def __contains__(self, key):
+ return key in builtinMap or key in self._d
+
def set(self, object):
if object.name in builtinMap:
raise IDLError("name '%s' is a builtin and cannot be redeclared" % (object.name), object.location)
if object.name in self._d:
old = self._d[object.name]
if old == object: return
if isinstance(old, Forward) and isinstance(object, Interface):
self._d[object.name] = object
elif isinstance(old, Interface) and isinstance(object, Forward):
pass
else:
raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name, self._d[object.name].location), object.location)
else:
self._d[object.name] = object
+ def get(self, id, location):
+ try:
+ return self[id]
+ except KeyError:
+ raise IDLError("Name '%s' not found", location)
+
class IDLError(Exception):
- def __init__(self, message, location):
+ def __init__(self, message, location, warning=False):
self.message = message
self.location = location
+ self.warning = warning
def __str__(self):
- return "Error: %s, %s" % (self.message, self.location)
+ return "%s: %s, %s" % (self.warning and 'warning' or 'error',
+ self.message, self.location)
class Include(object):
kind = 'include'
def __init__(self, filename, location):
self.filename = filename
self.location = location
@@ -192,39 +225,29 @@ class Include(object):
parent.setName(type)
return
raise IDLError("File '%s' not found" % self.filename, self.location)
class IDL(object):
def __init__(self, productions):
self.productions = productions
- # Hack alert: libidl parses doccomments on the non-first interface
- # incorrectly, assigning them to the first member of the interface
- # instead of the interface itself. Good god.
-
- found = False
- for p in productions:
- if p.kind == 'interface':
- dc = p.doccomments
- p.doccomments = []
- if len(p.members):
- p.members[0].doccomments[0:0] = dc
-
- if p.kind in ('interface', 'cdata'): found = True
def setName(self, object):
self.namemap.set(object)
def getName(self, id, location):
try:
return self.namemap[id]
except KeyError:
raise IDLError("type '%s' not found" % id, location)
+ def hasName(self, id):
+ return id in self.namemap
+
def getNames(self):
return iter(self.namemap)
def __str__(self):
return "".join([str(p) for p in self.productions])
def resolve(self, incdirs):
self.namemap = NameMap()
@@ -249,45 +272,49 @@ class CDATA(object):
pass
def __str__(self):
return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
class Typedef(object):
kind = 'typedef'
- def __init__(self, type, name, location):
+ def __init__(self, type, name, location, doccomments):
self.type = type
self.name = name
self.location = location
- self.nativename = name
+ self.doccomments = doccomments
def __eq__(self, other):
return self.name == other.name and self.type == other.type
def resolve(self, parent):
parent.setName(self)
self.realtype = parent.getName(self.type, self.location)
+ if isinstance(self.realtype, Native):
+ raise IDLError("Typedefs of native types are not supported.",
+ self.location)
def isScriptable(self):
return self.realtype.isScriptable()
def nativeType(self, calltype):
return "%s %s" % (self.name,
calltype != 'in' and '*' or '')
def __str__(self):
return "typedef %s %s\n" % (self.type, self.name)
class Forward(object):
kind = 'forward'
- def __init__(self, name, location):
+ def __init__(self, name, location, doccomments):
self.name = name
self.location = location
+ self.doccomments = doccomments
def __eq__(self, other):
return other.kind == 'forward' and other.name == self.name
def resolve(self, parent):
parent.setName(self)
def isScriptable(self):
@@ -348,29 +375,32 @@ class Native(object):
if self.specialtype is None:
return False
if self.specialtype == 'nsid':
return self.modifier is not None
return self.modifier == 'ref'
- def nativeType(self, calltype):
+ def nativeType(self, calltype, const=False, shared=False):
+ if shared:
+ if calltype != 'out':
+ raise IDLError("[shared] only applies to out parameters.")
+ const = True
+
if self.specialtype is not None and calltype == 'in':
- const = 'const '
- else:
- const = ''
+ const = True
if self.modifier == 'ptr':
m = '*' + (calltype != 'in' and '*' or '')
elif self.modifier == 'ref':
m = '& '
else:
m = calltype != 'in' and '*' or ''
- return "%s%s %s" % (const, self.nativename, m)
+ return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
def __str__(self):
return "native %s(%s)\n" % (self.name, self.nativename)
class Interface(object):
kind = 'interface'
def __init__(self, name, attlist, base, members, location, doccomments):
@@ -387,28 +417,39 @@ class Interface(object):
if not isinstance(m, CDATA):
self.namemap.set(m)
def __eq__(self, other):
return self.name == other.name and self.location == other.location
def resolve(self, parent):
self.idl = parent
+
+ # Hack alert: if an identifier is already present, libIDL assigns
+ # doc comments incorrectly. This is quirks-mode extraordinaire!
+ if parent.hasName(self.name):
+ for member in self.members:
+ if hasattr(member, 'doccomments'):
+ print >>sys.stderr, "Moving comments to %s" % member
+ member.doccomments[0:0] = self.doccomments
+ break
+ self.doccomments = parent.getName(self.name, None).doccomments
+
parent.setName(self)
if self.base is None:
if self.name != 'nsISupports':
- raise IDLError("interface '%s' not derived from nsISupports",
- self.location)
+ print >>sys.stderr, IDLError("interface '%s' not derived from nsISupports",
+ self.location, warning=True)
else:
realbase = parent.getName(self.base, self.location)
if realbase.kind != 'interface':
raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location)
if self.attributes.scriptable and not realbase.attributes.scriptable:
- raise IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base))
+ print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
for member in self.members:
member.resolve(self)
def isScriptable(self):
# NOTE: this is not whether *this* interface is scriptable... it's
# whether, when used as a type, it's scriptable, which is true of all
# interfaces.
@@ -427,21 +468,21 @@ class Interface(object):
if self.members is None:
l.append("\tincomplete type\n")
else:
for m in self.members:
l.append(str(m))
return "".join(l)
def getConst(self, name, location):
- c = self.get(name)
- if not isistance(c, ConstMember):
+ c = self.namemap.get(name, location)
+ if c.kind != 'const':
raise IDLError("symbol '%s' is not a constant", c.location)
- return c.resolve(self)
+ return c.getValue()
class InterfaceAttributes(object):
uuid = None
scriptable = False
function = False
deprecated = False
def setuuid(self, value):
@@ -460,16 +501,17 @@ class InterfaceAttributes(object):
self.deprecated = True
actions = {
'uuid': (True, setuuid),
'scriptable': (False, setscriptable),
'function': (False, setfunction),
'noscript': (False, setnoscript),
'deprecated': (False, setdeprecated),
+ 'object': (False, lambda self: True),
}
def __init__(self, attlist, location):
def badattribute(self):
raise IDLError("Unexpected interface attribute '%s'" % name, location)
for name, val, aloc in attlist:
hasval, action = self.actions.get(name, (False, badattribute))
@@ -505,26 +547,27 @@ class ConstMember(object):
self.type = type
self.name = name
self.value = value
self.location = location
self.doccomments = doccomments
def resolve(self, parent):
self.realtype = parent.idl.getName(self.type, self.location)
+ self.iface = parent
basetype = self.realtype
while isinstance(basetype, Typedef):
basetype = basetype.realtype
if not isinstance(basetype, Builtin) or not basetype.maybeConst:
raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
self.basetype = basetype
- def getValue(self, iface):
- return self.value(iface)
+ def getValue(self):
+ return self.value(self.iface)
class Attribute(object):
kind = 'attribute'
noscript = False
notxpcom = False
def __init__(self, type, name, attlist, readonly, location, doccomments):
self.type = type
@@ -682,18 +725,18 @@ class Param(object):
kwargs = {}
if self.shared: kwargs['shared'] = True
if self.const: kwargs['const'] = True
try:
return self.realtype.nativeType(self.paramtype, **kwargs)
except IDLError, e:
raise IDLError(e.message, self.location)
- # except TypeError, e:
- # raise IDLError("Unexpected parameter attribute", self.location)
+ except TypeError, e:
+ raise IDLError("Unexpected parameter attribute", self.location)
def toIDL(self):
return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
self.paramtype,
self.type,
self.name)
class Array(object):
@@ -829,17 +872,18 @@ class IDLParser(object):
| native productions"""
p[0] = list(p[2])
p[0].insert(0, p[1])
def p_typedef(self, p):
"""typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
p[0] = Typedef(type=p[2],
name=p[3],
- location=self.getLocation(p, 1))
+ location=self.getLocation(p, 1),
+ doccomments=p.slice[1].doccomments)
def p_native(self, p):
"""native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
p[0] = Native(name=p[3],
nativename=p[6],
attlist=p[1]['attlist'],
location=self.getLocation(p, 2))
@@ -883,33 +927,33 @@ class IDLParser(object):
| """
if len(p) > 1:
p[0] = p[2]
def p_interface(self, p):
"""interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
atts, INTERFACE, name, base, body, SEMI = p[1:]
attlist = atts['attlist']
+ doccomments = []
if 'doccomments' in atts:
- doccomments = atts['doccomments']
- else:
- doccomments = p.slice[2].doccomments
+ doccomments.extend(atts['doccomments'])
+ doccomments.extend(p.slice[2].doccomments)
l = lambda: self.getLocation(p, 2)
if body is None:
# forward-declared interface... must not have attributes!
if len(attlist) != 0:
raise IDLError("Forward-declared interface must not have attributes",
list[0][3])
if base is not None:
raise IDLError("Forward-declared interface must not have a base",
l())
- p[0] = Forward(name=name, location=l())
+ p[0] = Forward(name=name, location=l(), doccomments=doccomments)
else:
p[0] = Interface(name=name,
attlist=attlist,
base=base,
members=body,
location=l(),
doccomments=doccomments)
@@ -954,17 +998,18 @@ class IDLParser(object):
def p_number_hex(self, p):
"""number : HEXNUM"""
n = int(p[1], 16)
p[0] = lambda i: n
def p_number_identifier(self, p):
"""number : IDENTIFIER"""
id = p[1]
- p[0] = lambda i: i.getConst(id, self.getLocation(p, 1))
+ loc = self.getLocation(p, 1)
+ p[0] = lambda i: i.getConst(id, loc)
def p_number_paren(self, p):
"""number : '(' number ')'"""
p[0] = p[2]
def p_number_neg(self, p):
"""number : '-' number %prec UMINUS"""
n = p[2]
@@ -988,17 +1033,17 @@ class IDLParser(object):
| number RSHIFT number"""
n1 = p[1]
n2 = p[3]
if p[2] == '<<':
p[0] = lambda i: n1(i) << n2(i)
else:
p[0] = lambda i: n1(i) >> n2(i)
- def p_number_or(self, p):
+ def p_number_bitor(self, p):
"""number : number '|' number"""
n1 = p[1]
n2 = p[3]
p[0] = lambda i: n1(i) | n2(i)
def p_member_att(self, p):
"""member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
if 'doccomments' in p[1]:
@@ -1089,17 +1134,17 @@ class IDLParser(object):
self.lexer = lex.lex(object=self)
self.parser = yacc.yacc(module=self)
def clearComments(self):
self._doccomments = []
def token(self):
t = self.lexer.token()
- if t is not None:
+ if t is not None and t.type != 'CDATA':
t.doccomments = self._doccomments
self._doccomments = []
return t
def parse(self, data, filename=None):
if filename is not None:
self.lexer.filename = filename
self.lexer.lineno = 1