summaryrefslogtreecommitdiffstats
path: root/xpcom/idl-parser
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/idl-parser')
-rw-r--r--xpcom/idl-parser/setup.py15
-rw-r--r--xpcom/idl-parser/xpidl/__init__.py0
-rw-r--r--xpcom/idl-parser/xpidl/header.py566
-rw-r--r--xpcom/idl-parser/xpidl/moz.build29
-rw-r--r--xpcom/idl-parser/xpidl/runtests.py114
-rw-r--r--xpcom/idl-parser/xpidl/typelib.py307
-rwxr-xr-xxpcom/idl-parser/xpidl/xpidl.py1465
7 files changed, 2496 insertions, 0 deletions
diff --git a/xpcom/idl-parser/setup.py b/xpcom/idl-parser/setup.py
new file mode 100644
index 000000000..f42edbb99
--- /dev/null
+++ b/xpcom/idl-parser/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup, find_packages
+
+
+setup(
+ name='xpidl',
+ version='1.0',
+ description='Parser and header generator for xpidl files.',
+ author='Mozilla Foundation',
+ license='MPL 2.0',
+ packages=find_packages(),
+ install_requires=['ply>=3.6,<4.0'],
+ url='https://github.com/pelmers/xpidl',
+ entry_points={'console_scripts': ['header.py = xpidl.header:main']},
+ keywords=['xpidl', 'parser']
+)
diff --git a/xpcom/idl-parser/xpidl/__init__.py b/xpcom/idl-parser/xpidl/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/__init__.py
diff --git a/xpcom/idl-parser/xpidl/header.py b/xpcom/idl-parser/xpidl/header.py
new file mode 100644
index 000000000..8e02a7d11
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/header.py
@@ -0,0 +1,566 @@
+#!/usr/bin/env python
+# header.py - Generate C++ header files from IDL.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""Print a C++ header file for the IDL files specified on the command line"""
+
+import sys
+import os.path
+import re
+import xpidl
+import itertools
+import glob
+
+printdoccomments = False
+
+if printdoccomments:
+ def printComments(fd, clist, indent):
+ for c in clist:
+ fd.write("%s%s\n" % (indent, c))
+else:
+ def printComments(fd, clist, indent):
+ pass
+
+
+def firstCap(str):
+ return str[0].upper() + str[1:]
+
+
+def attributeParamName(a):
+ return "a" + firstCap(a.name)
+
+
+def attributeParamNames(a):
+ l = [attributeParamName(a)]
+ if a.implicit_jscontext:
+ l.insert(0, "cx")
+ return ", ".join(l)
+
+
+def attributeNativeName(a, getter):
+ binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
+ return "%s%s" % (getter and 'Get' or 'Set', binaryname)
+
+
+def attributeReturnType(a, macro):
+ """macro should be NS_IMETHOD or NS_IMETHODIMP"""
+ if a.nostdcall:
+ ret = macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
+ else:
+ ret = macro
+ if a.must_use:
+ ret = "MOZ_MUST_USE " + ret
+ return ret
+
+
+def attributeParamlist(a, getter):
+ l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
+ attributeParamName(a))]
+ if a.implicit_jscontext:
+ l.insert(0, "JSContext* cx")
+
+ return ", ".join(l)
+
+
+def attributeAsNative(a, getter, declType = 'NS_IMETHOD'):
+ deprecated = a.deprecated and "NS_DEPRECATED " or ""
+ params = {'deprecated': deprecated,
+ 'returntype': attributeReturnType(a, declType),
+ 'binaryname': attributeNativeName(a, getter),
+ 'paramlist': attributeParamlist(a, getter)}
+ return "%(deprecated)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
+
+
+def methodNativeName(m):
+ return m.binaryname is not None and m.binaryname or firstCap(m.name)
+
+
+def methodReturnType(m, macro):
+ """macro should be NS_IMETHOD or NS_IMETHODIMP"""
+ if m.nostdcall and m.notxpcom:
+ ret = "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
+ m.realtype.nativeType('in').strip())
+ elif m.nostdcall:
+ ret = "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
+ elif m.notxpcom:
+ ret = "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
+ else:
+ ret = macro
+ if m.must_use:
+ ret = "MOZ_MUST_USE " + ret
+ return ret
+
+
+def methodAsNative(m, declType = 'NS_IMETHOD'):
+ return "%s %s(%s)" % (methodReturnType(m, declType),
+ methodNativeName(m),
+ paramlistAsNative(m))
+
+
+def paramlistAsNative(m, empty='void'):
+ l = [paramAsNative(p) for p in m.params]
+
+ if m.implicit_jscontext:
+ l.append("JSContext* cx")
+
+ if m.optional_argc:
+ l.append('uint8_t _argc')
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ l.append(paramAsNative(xpidl.Param(paramtype='out',
+ type=None,
+ name='_retval',
+ attlist=[],
+ location=None,
+ realtype=m.realtype)))
+
+ # Set any optional out params to default to nullptr. Skip if we just added
+ # extra non-optional args to l.
+ if len(l) == len(m.params):
+ paramIter = len(m.params) - 1
+ while (paramIter >= 0 and m.params[paramIter].optional and
+ m.params[paramIter].paramtype == "out"):
+ t = m.params[paramIter].type
+ # Strings can't be optional, so this shouldn't happen, but let's make sure:
+ if t == "AString" or t == "ACString" or t == "DOMString" or t == "AUTF8String":
+ break
+ l[paramIter] += " = nullptr"
+ paramIter -= 1
+
+ if len(l) == 0:
+ return empty
+
+ return ", ".join(l)
+
+
+def paramAsNative(p):
+ return "%s%s" % (p.nativeType(),
+ p.name)
+
+
+def paramlistNames(m):
+ names = [p.name for p in m.params]
+
+ if m.implicit_jscontext:
+ names.append('cx')
+
+ if m.optional_argc:
+ names.append('_argc')
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ names.append('_retval')
+
+ if len(names) == 0:
+ return ''
+ return ', '.join(names)
+
+header = """/*
+ * DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s
+ */
+
+#ifndef __gen_%(basename)s_h__
+#define __gen_%(basename)s_h__
+"""
+
+include = """
+#ifndef __gen_%(basename)s_h__
+#include "%(basename)s.h"
+#endif
+"""
+
+jsvalue_include = """
+#include "js/Value.h"
+"""
+
+infallible_includes = """
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+"""
+
+header_end = """/* For IDL files that don't want to include root IDL files. */
+#ifndef NS_NO_VTABLE
+#define NS_NO_VTABLE
+#endif
+"""
+
+footer = """
+#endif /* __gen_%(basename)s_h__ */
+"""
+
+forward_decl = """class %(name)s; /* forward declaration */
+
+"""
+
+
+def idl_basename(f):
+ """returns the base name of a file with the last extension stripped"""
+ return os.path.basename(f).rpartition('.')[0]
+
+
+def print_header(idl, fd, filename):
+ fd.write(header % {'filename': filename,
+ 'basename': idl_basename(filename)})
+
+ foundinc = False
+ for inc in idl.includes():
+ if not foundinc:
+ foundinc = True
+ fd.write('\n')
+ fd.write(include % {'basename': idl_basename(inc.filename)})
+
+ if idl.needsJSTypes():
+ fd.write(jsvalue_include)
+
+ # Include some extra files if any attributes are infallible.
+ for iface in [p for p in idl.productions if p.kind == 'interface']:
+ for attr in [m for m in iface.members if isinstance(m, xpidl.Attribute)]:
+ if attr.infallible:
+ fd.write(infallible_includes)
+ break
+
+ fd.write('\n')
+ fd.write(header_end)
+
+ for p in idl.productions:
+ if p.kind == 'include':
+ continue
+ if p.kind == 'cdata':
+ fd.write(p.data)
+ continue
+
+ if p.kind == 'forward':
+ fd.write(forward_decl % {'name': p.name})
+ continue
+ if p.kind == 'interface':
+ write_interface(p, fd)
+ continue
+ if p.kind == 'typedef':
+ printComments(fd, p.doccomments, '')
+ fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
+ p.name))
+
+ fd.write(footer % {'basename': idl_basename(filename)})
+
+iface_header = r"""
+/* starting interface: %(name)s */
+#define %(defname)s_IID_STR "%(iid)s"
+
+#define %(defname)s_IID \
+ {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
+ { %(m3joined)s }}
+
+"""
+
+uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
+ (?P<m1>[a-f0-9]{4})-
+ (?P<m2>[a-f0-9]{4})-
+ (?P<m3>[a-f0-9]{4})-
+ (?P<m4>[a-f0-9]{12})$""", re.X)
+
+iface_prolog = """ {
+ public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
+
+"""
+
+iface_epilog = """};
+
+ NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
+
+/* Use this macro when declaring classes that implement this interface. */
+#define NS_DECL_%(macroname)s """
+
+iface_nonvirtual = """
+
+/* Use this macro when declaring the members of this interface when the
+ class doesn't implement the interface. This is useful for forwarding. */
+#define NS_DECL_NON_VIRTUAL_%(macroname)s """
+
+iface_forward = """
+
+/* Use this macro to declare functions that forward the behavior of this interface to another object. */
+#define NS_FORWARD_%(macroname)s(_to) """
+
+iface_forward_safe = """
+
+/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
+#define NS_FORWARD_SAFE_%(macroname)s(_to) """
+
+iface_template_prolog = """
+
+#if 0
+/* Use the code below as a template for the implementation class for this interface. */
+
+/* Header file */
+class %(implclass)s : public %(name)s
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_%(macroname)s
+
+ %(implclass)s();
+
+private:
+ ~%(implclass)s();
+
+protected:
+ /* additional members */
+};
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(%(implclass)s, %(name)s)
+
+%(implclass)s::%(implclass)s()
+{
+ /* member initializers and constructor code */
+}
+
+%(implclass)s::~%(implclass)s()
+{
+ /* destructor code */
+}
+
+"""
+
+example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+"""
+
+iface_template_epilog = """/* End of implementation class template. */
+#endif
+
+"""
+
+attr_infallible_tmpl = """\
+ inline %(realtype)s%(nativename)s(%(args)s)
+ {
+ %(realtype)sresult;
+ mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return result;
+ }
+"""
+
+
+def write_interface(iface, fd):
+ if iface.namemap is None:
+ raise Exception("Interface was not resolved.")
+
+ # Confirm that no names of methods will overload in this interface
+ names = set()
+ def record_name(name):
+ if name in names:
+ raise Exception("Unexpected overloaded virtual method %s in interface %s"
+ % (name, iface.name))
+ names.add(name)
+ for m in iface.members:
+ if type(m) == xpidl.Attribute:
+ record_name(attributeNativeName(m, getter=True))
+ if not m.readonly:
+ record_name(attributeNativeName(m, getter=False))
+ elif type(m) == xpidl.Method:
+ record_name(methodNativeName(m))
+
+ def write_const_decls(g):
+ fd.write(" enum {\n")
+ enums = []
+ for c in g:
+ printComments(fd, c.doccomments, ' ')
+ basetype = c.basetype
+ value = c.getValue()
+ enums.append(" %(name)s = %(value)s%(signed)s" % {
+ 'name': c.name,
+ 'value': value,
+ 'signed': (not basetype.signed) and 'U' or ''})
+ fd.write(",\n".join(enums))
+ fd.write("\n };\n\n")
+
+ def write_method_decl(m):
+ printComments(fd, m.doccomments, ' ')
+
+ fd.write(" /* %s */\n" % m.toIDL())
+ fd.write(" %s = 0;\n\n" % methodAsNative(m))
+
+ def write_attr_decl(a):
+ printComments(fd, a.doccomments, ' ')
+
+ fd.write(" /* %s */\n" % a.toIDL())
+
+ fd.write(" %s = 0;\n" % attributeAsNative(a, True))
+ if a.infallible:
+ fd.write(attr_infallible_tmpl %
+ {'realtype': a.realtype.nativeType('in'),
+ 'nativename': attributeNativeName(a, getter=True),
+ 'args': '' if not a.implicit_jscontext else 'JSContext* cx',
+ 'argnames': '' if not a.implicit_jscontext else 'cx, '})
+
+ if not a.readonly:
+ fd.write(" %s = 0;\n" % attributeAsNative(a, False))
+ fd.write("\n")
+
+ defname = iface.name.upper()
+ if iface.name[0:2] == 'ns':
+ defname = 'NS_' + defname[2:]
+
+ names = uuid_decoder.match(iface.attributes.uuid).groupdict()
+ m3str = names['m3'] + names['m4']
+ names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
+
+ if iface.name[2] == 'I':
+ implclass = iface.name[:2] + iface.name[3:]
+ else:
+ implclass = '_MYCLASS_'
+
+ names.update({'defname': defname,
+ 'macroname': iface.name.upper(),
+ 'name': iface.name,
+ 'iid': iface.attributes.uuid,
+ 'implclass': implclass})
+
+ fd.write(iface_header % names)
+
+ printComments(fd, iface.doccomments, '')
+
+ fd.write("class ")
+ foundcdata = False
+ for m in iface.members:
+ if isinstance(m, xpidl.CDATA):
+ foundcdata = True
+
+ if not foundcdata:
+ fd.write("NS_NO_VTABLE ")
+
+ if iface.attributes.deprecated:
+ fd.write("MOZ_DEPRECATED ")
+ fd.write(iface.name)
+ if iface.base:
+ fd.write(" : public %s" % iface.base)
+ fd.write(iface_prolog % names)
+
+ for key, group in itertools.groupby(iface.members, key=type):
+ if key == xpidl.ConstMember:
+ write_const_decls(group) # iterator of all the consts
+ else:
+ for member in group:
+ if key == xpidl.Attribute:
+ write_attr_decl(member)
+ elif key == xpidl.Method:
+ write_method_decl(member)
+ elif key == xpidl.CDATA:
+ fd.write(" %s" % member.data)
+ else:
+ raise Exception("Unexpected interface member: %s" % member)
+
+ fd.write(iface_epilog % names)
+
+ def writeDeclaration(fd, iface, virtual):
+ declType = "NS_IMETHOD" if virtual else "NS_METHOD"
+ suffix = " override" if virtual else ""
+ for member in iface.members:
+ if isinstance(member, xpidl.Attribute):
+ if member.infallible:
+ fd.write("\\\n using %s::%s; " % (iface.name, attributeNativeName(member, True)))
+ fd.write("\\\n %s%s; " % (attributeAsNative(member, True, declType), suffix))
+ if not member.readonly:
+ fd.write("\\\n %s%s; " % (attributeAsNative(member, False, declType), suffix))
+ elif isinstance(member, xpidl.Method):
+ fd.write("\\\n %s%s; " % (methodAsNative(member, declType), suffix))
+ if len(iface.members) == 0:
+ fd.write('\\\n /* no methods! */')
+ elif not member.kind in ('attribute', 'method'):
+ fd.write('\\')
+
+ writeDeclaration(fd, iface, True);
+ fd.write(iface_nonvirtual % names)
+ writeDeclaration(fd, iface, False);
+ fd.write(iface_forward % names)
+
+ def emitTemplate(forward_infallible, tmpl, tmpl_notxpcom=None):
+ if tmpl_notxpcom is None:
+ tmpl_notxpcom = tmpl
+ for member in iface.members:
+ if isinstance(member, xpidl.Attribute):
+ if forward_infallible and member.infallible:
+ fd.write("\\\n using %s::%s; " % (iface.name, attributeNativeName(member, True)))
+ fd.write(tmpl % {'asNative': attributeAsNative(member, True),
+ 'nativeName': attributeNativeName(member, True),
+ 'paramList': attributeParamNames(member)})
+ if not member.readonly:
+ fd.write(tmpl % {'asNative': attributeAsNative(member, False),
+ 'nativeName': attributeNativeName(member, False),
+ 'paramList': attributeParamNames(member)})
+ elif isinstance(member, xpidl.Method):
+ if member.notxpcom:
+ fd.write(tmpl_notxpcom % {'asNative': methodAsNative(member),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistNames(member)})
+ else:
+ fd.write(tmpl % {'asNative': methodAsNative(member),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistNames(member)})
+ if len(iface.members) == 0:
+ fd.write('\\\n /* no methods! */')
+ elif not member.kind in ('attribute', 'method'):
+ fd.write('\\')
+
+ emitTemplate(True,
+ "\\\n %(asNative)s override { return _to %(nativeName)s(%(paramList)s); } ")
+
+ fd.write(iface_forward_safe % names)
+
+ # Don't try to safely forward notxpcom functions, because we have no
+ # sensible default error return. Instead, the caller will have to
+ # implement them.
+ emitTemplate(False,
+ "\\\n %(asNative)s override { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ",
+ "\\\n %(asNative)s override; ")
+
+ fd.write(iface_template_prolog % names)
+
+ for member in iface.members:
+ if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA):
+ continue
+ fd.write("/* %s */\n" % member.toIDL())
+ if isinstance(member, xpidl.Attribute):
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': attributeNativeName(member, True),
+ 'paramList': attributeParamlist(member, True)})
+ if not member.readonly:
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': attributeNativeName(member, False),
+ 'paramList': attributeParamlist(member, False)})
+ elif isinstance(member, xpidl.Method):
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistAsNative(member, empty='')})
+ fd.write('\n')
+
+ fd.write(iface_template_epilog)
+
+
+def main(outputfile):
+ cachedir = '.'
+ if not os.path.isdir(cachedir):
+ os.mkdir(cachedir)
+ sys.path.append(cachedir)
+
+ # Delete the lex/yacc files. Ply is too stupid to regenerate them
+ # properly
+ for fileglobs in [os.path.join(cachedir, f) for f in ["xpidllex.py*", "xpidlyacc.py*"]]:
+ for filename in glob.glob(fileglobs):
+ os.remove(filename)
+
+ # Instantiate the parser.
+ p = xpidl.IDLParser(outputdir=cachedir)
+
+if __name__ == '__main__':
+ main(None)
diff --git a/xpcom/idl-parser/xpidl/moz.build b/xpcom/idl-parser/xpidl/moz.build
new file mode 100644
index 000000000..33c1e7b13
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/moz.build
@@ -0,0 +1,29 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+PYTHON_UNIT_TESTS += [
+ 'runtests.py',
+]
+
+GENERATED_FILES += [
+ ('xpidl.stub', 'xpidllex.py', 'xpidlyacc.py'),
+]
+
+GENERATED_FILES[('xpidl.stub', 'xpidllex.py', 'xpidlyacc.py')].script = 'header.py:main'
+
+SDK_FILES.bin += [
+ '!xpidllex.py',
+ '!xpidlyacc.py',
+ 'header.py',
+ 'typelib.py',
+ 'xpidl.py',
+]
+
+SDK_FILES.bin.ply += [
+ '/other-licenses/ply/ply/__init__.py',
+ '/other-licenses/ply/ply/lex.py',
+ '/other-licenses/ply/ply/yacc.py',
+]
diff --git a/xpcom/idl-parser/xpidl/runtests.py b/xpcom/idl-parser/xpidl/runtests.py
new file mode 100644
index 000000000..89222d546
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/runtests.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+#
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+#
+# Unit tests for xpidl.py
+
+import mozunit
+import unittest
+import xpidl
+import header
+
+
+class TestParser(unittest.TestCase):
+ def setUp(self):
+ self.p = xpidl.IDLParser()
+
+ def testEmpty(self):
+ i = self.p.parse("", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertEqual([], i.productions)
+
+ def testForwardInterface(self):
+ i = self.p.parse("interface foo;", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Forward))
+ self.assertEqual("foo", i.productions[0].name)
+
+ def testInterface(self):
+ i = self.p.parse("[uuid(abc)] interface foo {};", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ self.assertEqual("foo", i.productions[0].name)
+
+ def testAttributes(self):
+ i = self.p.parse("[scriptable, builtinclass, function, deprecated, uuid(abc)] interface foo {};", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ self.assertEqual("foo", iface.name)
+ self.assertTrue(iface.attributes.scriptable)
+ self.assertTrue(iface.attributes.builtinclass)
+ self.assertTrue(iface.attributes.function)
+ self.assertTrue(iface.attributes.deprecated)
+
+ i = self.p.parse("[noscript, uuid(abc)] interface foo {};", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ self.assertEqual("foo", iface.name)
+ self.assertTrue(iface.attributes.noscript)
+
+ def testMethod(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+void bar();
+};""", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ m = iface.members[0]
+ self.assertTrue(isinstance(m, xpidl.Method))
+ self.assertEqual("bar", m.name)
+ self.assertEqual("void", m.type)
+
+ def testMethodParams(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+long bar(in long a, in float b, [array] in long c);
+};""", filename='f')
+ i.resolve([], self.p)
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ m = iface.members[0]
+ self.assertTrue(isinstance(m, xpidl.Method))
+ self.assertEqual("bar", m.name)
+ self.assertEqual("long", m.type)
+ self.assertEqual(3, len(m.params))
+ self.assertEqual("long", m.params[0].type)
+ self.assertEqual("in", m.params[0].paramtype)
+ self.assertEqual("float", m.params[1].type)
+ self.assertEqual("in", m.params[1].paramtype)
+ self.assertEqual("long", m.params[2].type)
+ self.assertEqual("in", m.params[2].paramtype)
+ self.assertTrue(isinstance(m.params[2].realtype, xpidl.Array))
+ self.assertEqual("long", m.params[2].realtype.type.name)
+
+ def testAttribute(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+attribute long bar;
+};""", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ self.assertTrue(isinstance(i.productions[0], xpidl.Interface))
+ iface = i.productions[0]
+ a = iface.members[0]
+ self.assertTrue(isinstance(a, xpidl.Attribute))
+ self.assertEqual("bar", a.name)
+ self.assertEqual("long", a.type)
+
+ def testOverloadedVirtual(self):
+ i = self.p.parse("""[uuid(abc)] interface foo {
+attribute long bar;
+void getBar();
+};""", filename='f')
+ self.assertTrue(isinstance(i, xpidl.IDL))
+ class FdMock:
+ def write(self, s):
+ pass
+ try:
+ header.print_header(i, FdMock(), filename='f')
+ except Exception as e:
+ self.assertEqual(e.args[0], "Unexpected overloaded virtual method GetBar in interface foo")
+
+if __name__ == '__main__':
+ mozunit.main()
diff --git a/xpcom/idl-parser/xpidl/typelib.py b/xpcom/idl-parser/xpidl/typelib.py
new file mode 100644
index 000000000..911e3873d
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/typelib.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+# typelib.py - Generate XPCOM typelib files from IDL.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""Generate an XPIDL typelib for the IDL files specified on the command line"""
+
+import os
+import sys
+import xpidl
+import xpt
+
+# A map of xpidl.py types to xpt.py types
+TypeMap = {
+ # nsresult is not strictly an xpidl.py type, but it's useful here
+ 'nsresult': xpt.Type.Tags.uint32,
+ # builtins
+ 'boolean': xpt.Type.Tags.boolean,
+ 'void': xpt.Type.Tags.void,
+ 'int16_t': xpt.Type.Tags.int16,
+ 'int32_t': xpt.Type.Tags.int32,
+ 'int64_t': xpt.Type.Tags.int64,
+ 'uint8_t': xpt.Type.Tags.uint8,
+ 'uint16_t': xpt.Type.Tags.uint16,
+ 'uint32_t': xpt.Type.Tags.uint32,
+ 'uint64_t': xpt.Type.Tags.uint64,
+ 'octet': xpt.Type.Tags.uint8,
+ 'short': xpt.Type.Tags.int16,
+ 'long': xpt.Type.Tags.int32,
+ 'long long': xpt.Type.Tags.int64,
+ 'unsigned short': xpt.Type.Tags.uint16,
+ 'unsigned long': xpt.Type.Tags.uint32,
+ 'unsigned long long': xpt.Type.Tags.uint64,
+ 'float': xpt.Type.Tags.float,
+ 'double': xpt.Type.Tags.double,
+ 'char': xpt.Type.Tags.char,
+ 'string': xpt.Type.Tags.char_ptr,
+ 'wchar': xpt.Type.Tags.wchar_t,
+ 'wstring': xpt.Type.Tags.wchar_t_ptr,
+ # special types
+ 'nsid': xpt.Type.Tags.nsIID,
+ 'domstring': xpt.Type.Tags.DOMString,
+ 'astring': xpt.Type.Tags.AString,
+ 'utf8string': xpt.Type.Tags.UTF8String,
+ 'cstring': xpt.Type.Tags.CString,
+ 'jsval': xpt.Type.Tags.jsval
+}
+
+
+# XXXkhuey dipper types should go away (bug 677784)
+def isDipperType(type):
+ return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String
+
+
+def build_interface(iface, ifaces):
+ def get_type(type, calltype, iid_is=None, size_is=None):
+ """ Return the appropriate xpt.Type object for this param """
+
+ while isinstance(type, xpidl.Typedef):
+ type = type.realtype
+
+ if isinstance(type, xpidl.Builtin):
+ if type.name == 'string' and size_is is not None:
+ return xpt.StringWithSizeType(size_is, size_is)
+ elif type.name == 'wstring' and size_is is not None:
+ return xpt.WideStringWithSizeType(size_is, size_is)
+ else:
+ tag = TypeMap[type.name]
+ isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr)
+ return xpt.SimpleType(tag,
+ pointer=isPtr,
+ reference=False)
+
+ if isinstance(type, xpidl.Array):
+ # NB: For an Array<T> we pass down the iid_is to get the type of T.
+ # This allows Arrays of InterfaceIs types to work.
+ return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is,
+ #XXXkhuey length_is duplicates size_is (bug 677788),
+ size_is)
+
+ if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
+ xptiface = None
+ for i in ifaces:
+ if i.name == type.name:
+ xptiface = i
+
+ if not xptiface:
+ xptiface = xpt.Interface(name=type.name)
+ ifaces.append(xptiface)
+
+ return xpt.InterfaceType(xptiface)
+
+ if isinstance(type, xpidl.Native):
+ if type.specialtype:
+ # XXXkhuey jsval is marked differently in the typelib and in the headers :-(
+ isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval'
+ isRef = type.isRef(calltype) and not type.specialtype == 'jsval'
+ return xpt.SimpleType(TypeMap[type.specialtype],
+ pointer=isPtr,
+ reference=isRef)
+ elif iid_is is not None:
+ return xpt.InterfaceIsType(iid_is)
+ else:
+ # void ptr
+ return xpt.SimpleType(TypeMap['void'],
+ pointer=True,
+ reference=False)
+
+ raise Exception("Unknown type!")
+
+ def get_nsresult():
+ return xpt.SimpleType(TypeMap['nsresult'])
+
+ def build_nsresult_param():
+ return xpt.Param(get_nsresult())
+
+ def get_result_type(m):
+ if not m.notxpcom:
+ return get_nsresult()
+
+ return get_type(m.realtype, '')
+
+ def build_result_param(m):
+ return xpt.Param(get_result_type(m))
+
+ def build_retval_param(m):
+ type = get_type(m.realtype, 'out')
+ if isDipperType(type.tag):
+ # NB: The retval bit needs to be set here, contrary to what the
+ # xpt spec says.
+ return xpt.Param(type, in_=True, retval=True, dipper=True)
+ return xpt.Param(type, in_=False, out=True, retval=True)
+
+ def build_attr_param(a, getter=False, setter=False):
+ if not (getter or setter):
+ raise Exception("Attribute param must be for a getter or a setter!")
+
+ type = get_type(a.realtype, getter and 'out' or 'in')
+ if setter:
+ return xpt.Param(type)
+ else:
+ if isDipperType(type.tag):
+ # NB: The retval bit needs to be set here, contrary to what the
+ # xpt spec says.
+ return xpt.Param(type, in_=True, retval=True, dipper=True)
+ return xpt.Param(type, in_=False, out=True, retval=True)
+
+ if iface.namemap is None:
+ raise Exception("Interface was not resolved.")
+
+ consts = []
+ methods = []
+
+ def build_const(c):
+ consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue()))
+
+ def build_method(m):
+ params = []
+
+ def build_param(p):
+ def findattr(p, attr):
+ if hasattr(p, attr) and getattr(p, attr):
+ for i, param in enumerate(m.params):
+ if param.name == getattr(p, attr):
+ return i
+ return None
+
+ iid_is = findattr(p, 'iid_is')
+ size_is = findattr(p, 'size_is')
+
+ in_ = p.paramtype.count("in")
+ out = p.paramtype.count("out")
+ dipper = False
+ type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is)
+ if out and isDipperType(type.tag):
+ out = False
+ dipper = True
+
+ return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional)
+
+ for p in m.params:
+ params.append(build_param(p))
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ params.append(build_retval_param(m))
+
+ methods.append(xpt.Method(m.name, build_result_param(m), params,
+ getter=False, setter=False, notxpcom=m.notxpcom,
+ constructor=False, hidden=m.noscript,
+ optargc=m.optional_argc,
+ implicit_jscontext=m.implicit_jscontext))
+
+ def build_attr(a):
+ # Write the getter
+ methods.append(xpt.Method(a.name, build_nsresult_param(),
+ [build_attr_param(a, getter=True)],
+ getter=True, setter=False,
+ constructor=False, hidden=a.noscript,
+ optargc=False,
+ implicit_jscontext=a.implicit_jscontext))
+
+ # And maybe the setter
+ if not a.readonly:
+ methods.append(xpt.Method(a.name, build_nsresult_param(),
+ [build_attr_param(a, setter=True)],
+ getter=False, setter=True,
+ constructor=False, hidden=a.noscript,
+ optargc=False,
+ implicit_jscontext=a.implicit_jscontext))
+
+ for member in iface.members:
+ if isinstance(member, xpidl.ConstMember):
+ build_const(member)
+ elif isinstance(member, xpidl.Attribute):
+ build_attr(member)
+ elif isinstance(member, xpidl.Method):
+ build_method(member)
+ elif isinstance(member, xpidl.CDATA):
+ pass
+ else:
+ raise Exception("Unexpected interface member: %s" % member)
+
+ parent = None
+ if iface.base:
+ for i in ifaces:
+ if i.name == iface.base:
+ parent = i
+ if not parent:
+ parent = xpt.Interface(name=iface.base)
+ ifaces.append(parent)
+
+ return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods,
+ constants=consts, resolved=True, parent=parent,
+ scriptable=iface.attributes.scriptable,
+ function=iface.attributes.function,
+ builtinclass=iface.attributes.builtinclass,
+ main_process_scriptable_only=iface.attributes.main_process_scriptable_only)
+
+
+def write_typelib(idl, fd, filename):
+ """ Generate the typelib. """
+
+ # We only care about interfaces that are scriptable.
+ ifaces = []
+ for p in idl.productions:
+ if p.kind == 'interface' and p.attributes.scriptable:
+ ifaces.append(build_interface(p, ifaces))
+
+ typelib = xpt.Typelib(interfaces=ifaces)
+ typelib.writefd(fd)
+
+if __name__ == '__main__':
+ from optparse import OptionParser
+ o = OptionParser()
+ o.add_option('-I', action='append', dest='incdirs', default=['.'],
+ help="Directory to search for imported files")
+ o.add_option('--cachedir', dest='cachedir', default=None,
+ help="Directory in which to cache lex/parse tables.")
+ o.add_option('-o', dest='outfile', default=None,
+ help="Output file")
+ o.add_option('-d', dest='depfile', default=None,
+ help="Generate a make dependency file")
+ o.add_option('--regen', action='store_true', dest='regen', default=False,
+ help="Regenerate IDL Parser cache")
+ options, args = o.parse_args()
+ file = args[0] if args else None
+
+ if options.cachedir is not None:
+ if not os.path.isdir(options.cachedir):
+ os.mkdir(options.cachedir)
+ sys.path.append(options.cachedir)
+
+ if options.regen:
+ if options.cachedir is None:
+ print >>sys.stderr, "--regen requires --cachedir"
+ sys.exit(1)
+
+ p = xpidl.IDLParser(outputdir=options.cachedir, regen=True)
+ sys.exit(0)
+
+ if options.depfile is not None and options.outfile is None:
+ print >>sys.stderr, "-d requires -o"
+ sys.exit(1)
+
+ if options.outfile is not None:
+ outfd = open(options.outfile, 'wb')
+ closeoutfd = True
+ else:
+ raise "typelib generation requires an output file"
+
+ p = xpidl.IDLParser(outputdir=options.cachedir)
+ idl = p.parse(open(file).read(), filename=file)
+ idl.resolve(options.incdirs, p)
+ write_typelib(idl, outfd, file)
+
+ if closeoutfd:
+ outfd.close()
+
+ if options.depfile is not None:
+ depfd = open(options.depfile, 'w')
+ deps = [dep.replace('\\', '/') for dep in idl.deps]
+
+ print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))
+ for dep in deps:
+ print >>depfd, "%s:" % dep
diff --git a/xpcom/idl-parser/xpidl/xpidl.py b/xpcom/idl-parser/xpidl/xpidl.py
new file mode 100755
index 000000000..8b2d8c884
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -0,0 +1,1465 @@
+#!/usr/bin/env python
+# xpidl.py - A parser for cross-platform IDL (XPIDL) files.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""A parser for cross-platform IDL (XPIDL) files."""
+
+import sys
+import os.path
+import re
+from ply import lex
+from ply import yacc
+
+"""A type conforms to the following pattern:
+
+ def isScriptable(self):
+ 'returns True or False'
+
+ def nativeType(self, calltype):
+ 'returns a string representation of the native type
+ calltype must be 'in', 'out', or 'inout'
+
+Interface members const/method/attribute conform to the following pattern:
+
+ name = 'string'
+
+ def toIDL(self):
+ 'returns the member signature as IDL'
+"""
+
+
+def attlistToIDL(attlist):
+ if len(attlist) == 0:
+ 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 sorted])
+
+
+def unaliasType(t):
+ while t.kind == 'typedef':
+ t = t.realtype
+ assert t is not None
+ return t
+
+
+def getBuiltinOrNativeTypeName(t):
+ t = unaliasType(t)
+ if t.kind == 'builtin':
+ return t.name
+ elif t.kind == 'native':
+ assert t.specialtype is not None
+ return '[%s]' % t.specialtype
+ else:
+ return None
+
+
+class BuiltinLocation(object):
+ def get(self):
+ return "<builtin type>"
+
+ def __str__(self):
+ return self.get()
+
+
+class Builtin(object):
+ kind = 'builtin'
+ location = BuiltinLocation
+
+ def __init__(self, name, nativename, signed=False, maybeConst=False):
+ self.name = name
+ self.nativename = nativename
+ self.signed = signed
+ self.maybeConst = maybeConst
+
+ def isScriptable(self):
+ return True
+
+ def nativeType(self, calltype, shared=False, const=False):
+ if const:
+ print >>sys.stderr, IDLError("[const] doesn't make sense on builtin types.", self.location, warning=True)
+ const = 'const '
+ elif calltype == 'in' and self.nativename.endswith('*'):
+ const = 'const '
+ elif shared:
+ if not self.nativename.endswith('*'):
+ raise IDLError("[shared] not applicable to non-pointer types.", self.location)
+ const = 'const '
+ else:
+ const = ''
+ return "%s%s %s" % (const, self.nativename,
+ calltype != 'in' and '*' or '')
+
+builtinNames = [
+ Builtin('boolean', 'bool'),
+ Builtin('void', 'void'),
+ Builtin('octet', 'uint8_t'),
+ Builtin('short', 'int16_t', True, True),
+ Builtin('long', 'int32_t', True, True),
+ Builtin('long long', 'int64_t', True, False),
+ Builtin('unsigned short', 'uint16_t', False, True),
+ Builtin('unsigned long', 'uint32_t', False, True),
+ Builtin('unsigned long long', 'uint64_t', False, False),
+ Builtin('float', 'float', True, False),
+ Builtin('double', 'double', True, False),
+ Builtin('char', 'char', True, False),
+ Builtin('string', 'char *', False, False),
+ Builtin('wchar', 'char16_t', False, False),
+ Builtin('wstring', 'char16_t *', False, False),
+]
+
+builtinMap = {}
+for b in builtinNames:
+ builtinMap[b.name] = b
+
+
+class Location(object):
+ _line = None
+
+ def __init__(self, lexer, lineno, lexpos):
+ self._lineno = lineno
+ self._lexpos = lexpos
+ self._lexdata = lexer.lexdata
+ self._file = getattr(lexer, 'filename', "<unknown>")
+
+ def __eq__(self, other):
+ return (self._lexpos == other._lexpos and
+ self._file == other._file)
+
+ def resolve(self):
+ if self._line:
+ return
+
+ startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
+ endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
+ self._line = self._lexdata[startofline:endofline]
+ self._colno = self._lexpos - startofline
+
+ def pointerline(self):
+ def i():
+ for i in xrange(0, self._colno):
+ yield " "
+ yield "^"
+
+ return "".join(i())
+
+ def get(self):
+ self.resolve()
+ return "%s line %s:%s" % (self._file, self._lineno, self._colno)
+
+ def __str__(self):
+ self.resolve()
+ return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
+ self._line, self.pointerline())
+
+
+class NameMap(object):
+ """Map of name -> object. Each object must have a .name and .location property.
+ Setting the same name twice throws an error."""
+ def __init__(self):
+ self._d = {}
+
+ 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.startswith("_"):
+ object.name = object.name[1:]
+ 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, warning=False):
+ self.message = message
+ self.location = location
+ self.warning = warning
+
+ def __str__(self):
+ 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
+
+ def __str__(self):
+ return "".join(["include '%s'\n" % self.filename])
+
+ def resolve(self, parent):
+ def incfiles():
+ yield self.filename
+ for dir in parent.incdirs:
+ yield os.path.join(dir, self.filename)
+
+ for file in incfiles():
+ if not os.path.exists(file):
+ continue
+
+ self.IDL = parent.parser.parse(open(file).read(), filename=file)
+ self.IDL.resolve(parent.incdirs, parent.parser)
+ for type in self.IDL.getNames():
+ parent.setName(type)
+ parent.deps.extend(self.IDL.deps)
+ return
+
+ raise IDLError("File '%s' not found" % self.filename, self.location)
+
+
+class IDL(object):
+ def __init__(self, productions):
+ self.productions = productions
+ self.deps = []
+
+ 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, parser):
+ self.namemap = NameMap()
+ self.incdirs = incdirs
+ self.parser = parser
+ for p in self.productions:
+ p.resolve(self)
+
+ def includes(self):
+ for p in self.productions:
+ if p.kind == 'include':
+ yield p
+
+ def needsJSTypes(self):
+ for p in self.productions:
+ if p.kind == 'interface' and p.needsJSTypes():
+ return True
+ return False
+
+
+class CDATA(object):
+ kind = 'cdata'
+ _re = re.compile(r'\n+')
+
+ def __init__(self, data, location):
+ self.data = self._re.sub('\n', data)
+ self.location = location
+
+ def resolve(self, parent):
+ pass
+
+ def __str__(self):
+ return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
+
+ def count(self):
+ return 0
+
+
+class Typedef(object):
+ kind = 'typedef'
+
+ def __init__(self, type, name, location, doccomments):
+ self.type = type
+ self.name = name
+ self.location = location
+ 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)
+
+ 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, 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):
+ # Hack alert: if an identifier is already present, move the doccomments
+ # forward.
+ if parent.hasName(self.name):
+ for i in xrange(0, len(parent.productions)):
+ if parent.productions[i] is self:
+ break
+ for i in xrange(i + 1, len(parent.productions)):
+ if hasattr(parent.productions[i], 'doccomments'):
+ parent.productions[i].doccomments[0:0] = self.doccomments
+ break
+
+ parent.setName(self)
+
+ def isScriptable(self):
+ return True
+
+ def nativeType(self, calltype):
+ return "%s %s" % (self.name,
+ calltype != 'in' and '* *' or '*')
+
+ def __str__(self):
+ return "forward-declared %s\n" % self.name
+
+
+class Native(object):
+ kind = 'native'
+
+ modifier = None
+ specialtype = None
+
+ specialtypes = {
+ 'nsid': None,
+ 'domstring': 'nsAString',
+ 'utf8string': 'nsACString',
+ 'cstring': 'nsACString',
+ 'astring': 'nsAString',
+ 'jsval': 'JS::Value'
+ }
+
+ def __init__(self, name, nativename, attlist, location):
+ self.name = name
+ self.nativename = nativename
+ self.location = location
+
+ for name, value, aloc in attlist:
+ if value is not None:
+ raise IDLError("Unexpected attribute value", aloc)
+ if name in ('ptr', 'ref'):
+ if self.modifier is not None:
+ raise IDLError("More than one ptr/ref modifier", aloc)
+ self.modifier = name
+ elif name in self.specialtypes.keys():
+ if self.specialtype is not None:
+ raise IDLError("More than one special type", aloc)
+ self.specialtype = name
+ if self.specialtypes[name] is not None:
+ self.nativename = self.specialtypes[name]
+ else:
+ raise IDLError("Unexpected attribute", aloc)
+
+ def __eq__(self, other):
+ return (self.name == other.name and
+ self.nativename == other.nativename and
+ self.modifier == other.modifier and
+ self.specialtype == other.specialtype)
+
+ def resolve(self, parent):
+ parent.setName(self)
+
+ def isScriptable(self):
+ if self.specialtype is None:
+ return False
+
+ if self.specialtype == 'nsid':
+ return self.modifier is not None
+
+ return self.modifier == 'ref'
+
+ def isPtr(self, calltype):
+ return self.modifier == 'ptr'
+
+ def isRef(self, calltype):
+ return self.modifier == 'ref'
+
+ 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 = True
+
+ if self.specialtype == 'jsval':
+ if calltype == 'out' or calltype == 'inout':
+ return "JS::MutableHandleValue "
+ return "JS::HandleValue "
+
+ if self.isRef(calltype):
+ m = '& '
+ elif self.isPtr(calltype):
+ m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '')
+ else:
+ m = calltype != 'in' and '*' or ''
+ 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):
+ self.name = name
+ self.attributes = InterfaceAttributes(attlist, location)
+ self.base = base
+ self.members = members
+ self.location = location
+ self.namemap = NameMap()
+ self.doccomments = doccomments
+ self.nativename = name
+
+ for m in members:
+ 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'):
+ member.doccomments[0:0] = self.doccomments
+ break
+ self.doccomments = parent.getName(self.name, None).doccomments
+
+ if self.attributes.function:
+ has_method = False
+ for member in self.members:
+ if member.kind is 'method':
+ if has_method:
+ raise IDLError("interface '%s' has multiple methods, but marked 'function'" % self.name, self.location)
+ else:
+ has_method = True
+
+ parent.setName(self)
+ if self.base is not None:
+ 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:
+ print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
+
+ if self.attributes.scriptable and realbase.attributes.builtinclass and not self.attributes.builtinclass:
+ raise IDLError("interface '%s' is not builtinclass but derives from builtinclass '%s'" % (self.name, self.base), self.location)
+
+ for member in self.members:
+ member.resolve(self)
+
+ # The number 250 is NOT arbitrary; this number is the maximum number of
+ # stub entries defined in xpcom/reflect/xptcall/genstubs.pl
+ # Do not increase this value without increasing the number in that
+ # location, or you WILL cause otherwise unknown problems!
+ if self.countEntries() > 250 and not self.attributes.builtinclass:
+ raise IDLError("interface '%s' has too many entries" % self.name, self.location)
+
+ 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.
+ return True
+
+ def nativeType(self, calltype, const=False):
+ return "%s%s %s" % (const and 'const ' or '',
+ self.name,
+ calltype != 'in' and '* *' or '*')
+
+ def __str__(self):
+ l = ["interface %s\n" % self.name]
+ if self.base is not None:
+ l.append("\tbase %s\n" % self.base)
+ l.append(str(self.attributes))
+ 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):
+ # The constant may be in a base class
+ iface = self
+ while name not in iface.namemap and iface is not None:
+ iface = self.idl.getName(self.base, self.location)
+ if iface is None:
+ raise IDLError("cannot find symbol '%s'" % name)
+ c = iface.namemap.get(name, location)
+ if c.kind != 'const':
+ raise IDLError("symbol '%s' is not a constant", c.location)
+
+ return c.getValue()
+
+ def needsJSTypes(self):
+ for m in self.members:
+ if m.kind == "attribute" and m.type == "jsval":
+ return True
+ if m.kind == "method" and m.needsJSTypes():
+ return True
+ return False
+
+ def countEntries(self):
+ ''' Returns the number of entries in the vtable for this interface. '''
+ total = sum(member.count() for member in self.members)
+ if self.base is not None:
+ realbase = self.idl.getName(self.base, self.location)
+ total += realbase.countEntries()
+ return total
+
+
+class InterfaceAttributes(object):
+ uuid = None
+ scriptable = False
+ builtinclass = False
+ function = False
+ deprecated = False
+ noscript = False
+ main_process_scriptable_only = False
+
+ def setuuid(self, value):
+ self.uuid = value.lower()
+
+ def setscriptable(self):
+ self.scriptable = True
+
+ def setfunction(self):
+ self.function = True
+
+ def setnoscript(self):
+ self.noscript = True
+
+ def setbuiltinclass(self):
+ self.builtinclass = True
+
+ def setdeprecated(self):
+ self.deprecated = True
+
+ def setmain_process_scriptable_only(self):
+ self.main_process_scriptable_only = True
+
+ actions = {
+ 'uuid': (True, setuuid),
+ 'scriptable': (False, setscriptable),
+ 'builtinclass': (False, setbuiltinclass),
+ 'function': (False, setfunction),
+ 'noscript': (False, setnoscript),
+ 'deprecated': (False, setdeprecated),
+ 'object': (False, lambda self: True),
+ 'main_process_scriptable_only': (False, setmain_process_scriptable_only),
+ }
+
+ 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))
+ if hasval:
+ if val is None:
+ raise IDLError("Expected value for attribute '%s'" % name,
+ aloc)
+
+ action(self, val)
+ else:
+ if val is not None:
+ raise IDLError("Unexpected value for attribute '%s'" % name,
+ aloc)
+
+ action(self)
+
+ if self.uuid is None:
+ raise IDLError("interface has no uuid", location)
+
+ def __str__(self):
+ l = []
+ if self.uuid:
+ l.append("\tuuid: %s\n" % self.uuid)
+ if self.scriptable:
+ l.append("\tscriptable\n")
+ if self.builtinclass:
+ l.append("\tbuiltinclass\n")
+ if self.function:
+ l.append("\tfunction\n")
+ if self.main_process_scriptable_only:
+ l.append("\tmain_process_scriptable_only\n")
+ return "".join(l)
+
+
+class ConstMember(object):
+ kind = 'const'
+
+ def __init__(self, type, name, value, location, doccomments):
+ 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):
+ return self.value(self.iface)
+
+ def __str__(self):
+ return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue())
+
+ def count(self):
+ return 0
+
+
+class Attribute(object):
+ kind = 'attribute'
+ noscript = False
+ readonly = False
+ implicit_jscontext = False
+ nostdcall = False
+ must_use = False
+ binaryname = None
+ null = None
+ undefined = None
+ deprecated = False
+ infallible = False
+
+ def __init__(self, type, name, attlist, readonly, location, doccomments):
+ self.type = type
+ self.name = name
+ self.attlist = attlist
+ self.readonly = readonly
+ self.location = location
+ self.doccomments = doccomments
+
+ for name, value, aloc in attlist:
+ if name == 'binaryname':
+ if value is None:
+ raise IDLError("binaryname attribute requires a value",
+ aloc)
+
+ self.binaryname = value
+ continue
+
+ if name == 'Null':
+ if value is None:
+ raise IDLError("'Null' attribute requires a value", aloc)
+ if readonly:
+ raise IDLError("'Null' attribute only makes sense for setters",
+ aloc)
+ if value not in ('Empty', 'Null', 'Stringify'):
+ raise IDLError("'Null' attribute value must be 'Empty', 'Null' or 'Stringify'",
+ aloc)
+ self.null = value
+ elif name == 'Undefined':
+ if value is None:
+ raise IDLError("'Undefined' attribute requires a value", aloc)
+ if readonly:
+ raise IDLError("'Undefined' attribute only makes sense for setters",
+ aloc)
+ if value not in ('Empty', 'Null'):
+ raise IDLError("'Undefined' attribute value must be 'Empty' or 'Null'",
+ aloc)
+ self.undefined = value
+ else:
+ if value is not None:
+ raise IDLError("Unexpected attribute value", aloc)
+
+ if name == 'noscript':
+ self.noscript = True
+ elif name == 'implicit_jscontext':
+ self.implicit_jscontext = True
+ elif name == 'deprecated':
+ self.deprecated = True
+ elif name == 'nostdcall':
+ self.nostdcall = True
+ elif name == 'must_use':
+ self.must_use = True
+ elif name == 'infallible':
+ self.infallible = True
+ else:
+ raise IDLError("Unexpected attribute '%s'" % name, aloc)
+
+ def resolve(self, iface):
+ self.iface = iface
+ self.realtype = iface.idl.getName(self.type, self.location)
+ if (self.null is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Null' attribute can only be used on DOMString",
+ self.location)
+ if (self.undefined is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Undefined' attribute can only be used on DOMString",
+ self.location)
+ if self.infallible and not self.realtype.kind == 'builtin':
+ raise IDLError('[infallible] only works on builtin types '
+ '(numbers, booleans, and raw char types)',
+ self.location)
+ if self.infallible and not iface.attributes.builtinclass:
+ raise IDLError('[infallible] attributes are only allowed on '
+ '[builtinclass] interfaces',
+ self.location)
+
+ def toIDL(self):
+ attribs = attlistToIDL(self.attlist)
+ readonly = self.readonly and 'readonly ' or ''
+ return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
+
+ def isScriptable(self):
+ if not self.iface.attributes.scriptable:
+ return False
+ return not self.noscript
+
+ def __str__(self):
+ return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
+ self.type, self.name)
+
+ def count(self):
+ return self.readonly and 1 or 2
+
+
+class Method(object):
+ kind = 'method'
+ noscript = False
+ notxpcom = False
+ binaryname = None
+ implicit_jscontext = False
+ nostdcall = False
+ must_use = False
+ optional_argc = False
+ deprecated = False
+
+ def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
+ self.type = type
+ self.name = name
+ self.attlist = attlist
+ self.params = paramlist
+ self.location = location
+ self.doccomments = doccomments
+ self.raises = raises
+
+ for name, value, aloc in attlist:
+ if name == 'binaryname':
+ if value is None:
+ raise IDLError("binaryname attribute requires a value",
+ aloc)
+
+ self.binaryname = value
+ continue
+
+ if value is not None:
+ raise IDLError("Unexpected attribute value", aloc)
+
+ if name == 'noscript':
+ self.noscript = True
+ elif name == 'notxpcom':
+ self.notxpcom = True
+ elif name == 'implicit_jscontext':
+ self.implicit_jscontext = True
+ elif name == 'optional_argc':
+ self.optional_argc = True
+ elif name == 'deprecated':
+ self.deprecated = True
+ elif name == 'nostdcall':
+ self.nostdcall = True
+ elif name == 'must_use':
+ self.must_use = True
+ else:
+ raise IDLError("Unexpected attribute '%s'" % name, aloc)
+
+ self.namemap = NameMap()
+ for p in paramlist:
+ self.namemap.set(p)
+
+ def resolve(self, iface):
+ self.iface = iface
+ self.realtype = self.iface.idl.getName(self.type, self.location)
+ for p in self.params:
+ p.resolve(self)
+ for p in self.params:
+ if p.retval and p != self.params[-1]:
+ raise IDLError("'retval' parameter '%s' is not the last parameter" % p.name, self.location)
+ if p.size_is:
+ found_size_param = False
+ for size_param in self.params:
+ if p.size_is == size_param.name:
+ found_size_param = True
+ if getBuiltinOrNativeTypeName(size_param.realtype) != 'unsigned long':
+ raise IDLError("is_size parameter must have type 'unsigned long'", self.location)
+ if not found_size_param:
+ raise IDLError("could not find is_size parameter '%s'" % p.size_is, self.location)
+
+ def isScriptable(self):
+ if not self.iface.attributes.scriptable:
+ return False
+ return not (self.noscript or self.notxpcom)
+
+ def __str__(self):
+ return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
+
+ def toIDL(self):
+ if len(self.raises):
+ raises = ' raises (%s)' % ','.join(self.raises)
+ else:
+ raises = ''
+
+ return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
+ self.type,
+ self.name,
+ ", ".join([p.toIDL()
+ for p in self.params]),
+ raises)
+
+ def needsJSTypes(self):
+ if self.implicit_jscontext:
+ return True
+ if self.type == "jsval":
+ return True
+ for p in self.params:
+ t = p.realtype
+ if isinstance(t, Native) and t.specialtype == "jsval":
+ return True
+ return False
+
+ def count(self):
+ return 1
+
+
+class Param(object):
+ size_is = None
+ iid_is = None
+ const = False
+ array = False
+ retval = False
+ shared = False
+ optional = False
+ null = None
+ undefined = None
+
+ def __init__(self, paramtype, type, name, attlist, location, realtype=None):
+ self.paramtype = paramtype
+ self.type = type
+ self.name = name
+ self.attlist = attlist
+ self.location = location
+ self.realtype = realtype
+
+ for name, value, aloc in attlist:
+ # Put the value-taking attributes first!
+ if name == 'size_is':
+ if value is None:
+ raise IDLError("'size_is' must specify a parameter", aloc)
+ self.size_is = value
+ elif name == 'iid_is':
+ if value is None:
+ raise IDLError("'iid_is' must specify a parameter", aloc)
+ self.iid_is = value
+ elif name == 'Null':
+ if value is None:
+ raise IDLError("'Null' must specify a parameter", aloc)
+ if value not in ('Empty', 'Null', 'Stringify'):
+ raise IDLError("'Null' parameter value must be 'Empty', 'Null', or 'Stringify'",
+ aloc)
+ self.null = value
+ elif name == 'Undefined':
+ if value is None:
+ raise IDLError("'Undefined' must specify a parameter", aloc)
+ if value not in ('Empty', 'Null'):
+ raise IDLError("'Undefined' parameter value must be 'Empty' or 'Null'",
+ aloc)
+ self.undefined = value
+ else:
+ if value is not None:
+ raise IDLError("Unexpected value for attribute '%s'" % name,
+ aloc)
+
+ if name == 'const':
+ self.const = True
+ elif name == 'array':
+ self.array = True
+ elif name == 'retval':
+ self.retval = True
+ elif name == 'shared':
+ self.shared = True
+ elif name == 'optional':
+ self.optional = True
+ else:
+ raise IDLError("Unexpected attribute '%s'" % name, aloc)
+
+ def resolve(self, method):
+ self.realtype = method.iface.idl.getName(self.type, self.location)
+ if self.array:
+ self.realtype = Array(self.realtype)
+ if (self.null is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Null' attribute can only be used on DOMString",
+ self.location)
+ if (self.undefined is not None and
+ getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
+ raise IDLError("'Undefined' attribute can only be used on DOMString",
+ self.location)
+
+ def nativeType(self):
+ 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)
+
+ def toIDL(self):
+ return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
+ self.paramtype,
+ self.type,
+ self.name)
+
+
+class Array(object):
+ def __init__(self, basetype):
+ self.type = basetype
+
+ def isScriptable(self):
+ return self.type.isScriptable()
+
+ def nativeType(self, calltype, const=False):
+ return "%s%s*" % (const and 'const ' or '',
+ self.type.nativeType(calltype))
+
+
+class IDLParser(object):
+ keywords = {
+ 'const': 'CONST',
+ 'interface': 'INTERFACE',
+ 'in': 'IN',
+ 'inout': 'INOUT',
+ 'out': 'OUT',
+ 'attribute': 'ATTRIBUTE',
+ 'raises': 'RAISES',
+ 'readonly': 'READONLY',
+ 'native': 'NATIVE',
+ 'typedef': 'TYPEDEF',
+ }
+
+ tokens = [
+ 'IDENTIFIER',
+ 'CDATA',
+ 'INCLUDE',
+ 'IID',
+ 'NUMBER',
+ 'HEXNUM',
+ 'LSHIFT',
+ 'RSHIFT',
+ 'NATIVEID',
+ ]
+
+ tokens.extend(keywords.values())
+
+ states = (
+ ('nativeid', 'exclusive'),
+ )
+
+ hexchar = r'[a-fA-F0-9]'
+
+ t_NUMBER = r'-?\d+'
+ t_HEXNUM = r'0x%s+' % hexchar
+ t_LSHIFT = r'<<'
+ t_RSHIFT = r'>>'
+
+ literals = '"(){}[],;:=|+-*'
+
+ t_ignore = ' \t'
+
+ def t_multilinecomment(self, t):
+ r'/\*(?s).*?\*/'
+ t.lexer.lineno += t.value.count('\n')
+ if t.value.startswith("/**"):
+ self._doccomments.append(t.value)
+
+ def t_singlelinecomment(self, t):
+ r'(?m)//.*?$'
+
+ def t_IID(self, t):
+ return t
+ t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
+
+ def t_IDENTIFIER(self, t):
+ r'(unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long)(?!_?[A-Za-z][A-Za-z_0-9])|_?[A-Za-z][A-Za-z_0-9]*'
+ t.type = self.keywords.get(t.value, 'IDENTIFIER')
+ return t
+
+ def t_LCDATA(self, t):
+ r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
+ t.type = 'CDATA'
+ t.value = t.lexer.lexmatch.group('cdata')
+ t.lexer.lineno += t.value.count('\n')
+ return t
+
+ def t_INCLUDE(self, t):
+ r'\#include[ \t]+"[^"\n]+"'
+ inc, value, end = t.value.split('"')
+ t.value = value
+ return t
+
+ def t_directive(self, t):
+ r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
+ raise IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
+ Location(lexer=self.lexer, lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos))
+
+ def t_newline(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+
+ def t_nativeid_NATIVEID(self, t):
+ r'[^()\n]+(?=\))'
+ t.lexer.begin('INITIAL')
+ return t
+
+ t_nativeid_ignore = ''
+
+ def t_ANY_error(self, t):
+ raise IDLError("unrecognized input",
+ Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos))
+
+ precedence = (
+ ('left', '|'),
+ ('left', 'LSHIFT', 'RSHIFT'),
+ ('left', '+', '-'),
+ ('left', '*'),
+ ('left', 'UMINUS'),
+ )
+
+ def p_idlfile(self, p):
+ """idlfile : productions"""
+ p[0] = IDL(p[1])
+
+ def p_productions_start(self, p):
+ """productions : """
+ p[0] = []
+
+ def p_productions_cdata(self, p):
+ """productions : CDATA productions"""
+ p[0] = list(p[2])
+ p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
+
+ def p_productions_include(self, p):
+ """productions : INCLUDE productions"""
+ p[0] = list(p[2])
+ p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
+
+ def p_productions_interface(self, p):
+ """productions : interface productions
+ | typedef productions
+ | 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),
+ 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))
+
+ def p_afternativeid(self, p):
+ """afternativeid : """
+ # this is a place marker: we switch the lexer into literal identifier
+ # mode here, to slurp up everything until the closeparen
+ self.lexer.begin('nativeid')
+
+ def p_anyident(self, p):
+ """anyident : IDENTIFIER
+ | CONST"""
+ p[0] = {'value': p[1],
+ 'location': self.getLocation(p, 1)}
+
+ def p_attributes(self, p):
+ """attributes : '[' attlist ']'
+ | """
+ if len(p) == 1:
+ p[0] = {'attlist': []}
+ else:
+ p[0] = {'attlist': p[2],
+ 'doccomments': p.slice[1].doccomments}
+
+ def p_attlist_start(self, p):
+ """attlist : attribute"""
+ p[0] = [p[1]]
+
+ def p_attlist_continue(self, p):
+ """attlist : attribute ',' attlist"""
+ p[0] = list(p[3])
+ p[0].insert(0, p[1])
+
+ def p_attribute(self, p):
+ """attribute : anyident attributeval"""
+ p[0] = (p[1]['value'], p[2], p[1]['location'])
+
+ def p_attributeval(self, p):
+ """attributeval : '(' IDENTIFIER ')'
+ | '(' IID ')'
+ | """
+ 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.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(), doccomments=doccomments)
+ else:
+ p[0] = Interface(name=name,
+ attlist=attlist,
+ base=base,
+ members=body,
+ location=l(),
+ doccomments=doccomments)
+
+ def p_ifacebody(self, p):
+ """ifacebody : '{' members '}'
+ | """
+ if len(p) > 1:
+ p[0] = p[2]
+
+ def p_ifacebase(self, p):
+ """ifacebase : ':' IDENTIFIER
+ | """
+ if len(p) == 3:
+ p[0] = p[2]
+
+ def p_members_start(self, p):
+ """members : """
+ p[0] = []
+
+ def p_members_continue(self, p):
+ """members : member members"""
+ p[0] = list(p[2])
+ p[0].insert(0, p[1])
+
+ def p_member_cdata(self, p):
+ """member : CDATA"""
+ p[0] = CDATA(p[1], self.getLocation(p, 1))
+
+ def p_member_const(self, p):
+ """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
+ p[0] = ConstMember(type=p[2], name=p[3],
+ value=p[5], location=self.getLocation(p, 1),
+ doccomments=p.slice[1].doccomments)
+
+# All "number" products return a function(interface)
+
+ def p_number_decimal(self, p):
+ """number : NUMBER"""
+ n = int(p[1])
+ p[0] = lambda i: n
+
+ 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]
+ 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]
+ p[0] = lambda i: - n(i)
+
+ def p_number_add(self, p):
+ """number : number '+' number
+ | number '-' number
+ | number '*' number"""
+ n1 = p[1]
+ n2 = p[3]
+ if p[2] == '+':
+ p[0] = lambda i: n1(i) + n2(i)
+ elif p[2] == '-':
+ p[0] = lambda i: n1(i) - n2(i)
+ else:
+ p[0] = lambda i: n1(i) * n2(i)
+
+ def p_number_shift(self, p):
+ """number : number LSHIFT number
+ | 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_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]:
+ doccomments = p[1]['doccomments']
+ elif p[2] is not None:
+ doccomments = p[2]
+ else:
+ doccomments = p.slice[3].doccomments
+
+ p[0] = Attribute(type=p[4],
+ name=p[5],
+ attlist=p[1]['attlist'],
+ readonly=p[2] is not None,
+ location=self.getLocation(p, 3),
+ doccomments=doccomments)
+
+ def p_member_method(self, p):
+ """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
+ if 'doccomments' in p[1]:
+ doccomments = p[1]['doccomments']
+ else:
+ doccomments = p.slice[2].doccomments
+
+ p[0] = Method(type=p[2],
+ name=p[3],
+ attlist=p[1]['attlist'],
+ paramlist=p[5],
+ location=self.getLocation(p, 3),
+ doccomments=doccomments,
+ raises=p[7])
+
+ def p_paramlist(self, p):
+ """paramlist : param moreparams
+ | """
+ if len(p) == 1:
+ p[0] = []
+ else:
+ p[0] = list(p[2])
+ p[0].insert(0, p[1])
+
+ def p_moreparams_start(self, p):
+ """moreparams :"""
+ p[0] = []
+
+ def p_moreparams_continue(self, p):
+ """moreparams : ',' param moreparams"""
+ p[0] = list(p[3])
+ p[0].insert(0, p[2])
+
+ def p_param(self, p):
+ """param : attributes paramtype IDENTIFIER IDENTIFIER"""
+ p[0] = Param(paramtype=p[2],
+ type=p[3],
+ name=p[4],
+ attlist=p[1]['attlist'],
+ location=self.getLocation(p, 3))
+
+ def p_paramtype(self, p):
+ """paramtype : IN
+ | INOUT
+ | OUT"""
+ p[0] = p[1]
+
+ def p_optreadonly(self, p):
+ """optreadonly : READONLY
+ | """
+ if len(p) > 1:
+ p[0] = p.slice[1].doccomments
+ else:
+ p[0] = None
+
+ def p_raises(self, p):
+ """raises : RAISES '(' idlist ')'
+ | """
+ if len(p) == 1:
+ p[0] = []
+ else:
+ p[0] = p[3]
+
+ def p_idlist(self, p):
+ """idlist : IDENTIFIER"""
+ p[0] = [p[1]]
+
+ def p_idlist_continue(self, p):
+ """idlist : IDENTIFIER ',' idlist"""
+ p[0] = list(p[3])
+ p[0].insert(0, p[1])
+
+ def p_error(self, t):
+ if not t:
+ raise IDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", None)
+ else:
+ location = Location(self.lexer, t.lineno, t.lexpos)
+ raise IDLError("invalid syntax", location)
+
+ def __init__(self, outputdir=''):
+ self._doccomments = []
+ self.lexer = lex.lex(object=self,
+ outputdir=outputdir,
+ lextab='xpidllex',
+ optimize=1)
+ self.parser = yacc.yacc(module=self,
+ outputdir=outputdir,
+ debug=0,
+ tabmodule='xpidlyacc',
+ optimize=1)
+
+ def clearComments(self):
+ self._doccomments = []
+
+ def token(self):
+ t = self.lexer.token()
+ 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
+ self.lexer.input(data)
+ idl = self.parser.parse(lexer=self)
+ if filename is not None:
+ idl.deps.append(filename)
+ return idl
+
+ def getLocation(self, p, i):
+ return Location(self.lexer, p.lineno(i), p.lexpos(i))
+
+if __name__ == '__main__':
+ p = IDLParser()
+ for f in sys.argv[1:]:
+ print "Parsing %s" % f
+ p.parse(open(f).read(), filename=f)