Bug 1444991 - Part 2: Parse webidl productions in xpidl, r=mccr8
authorNika Layzell <nika@thelayzells.com>
Fri, 06 Apr 2018 18:22:14 -0400
changeset 467734 c59b5be67ba225f8864219c8b8d589446d1d8459
parent 467733 40a027a1f2cf5318f96e97cef8a5a7a388cb0a20
child 467735 ae4da56bcf71aeb41efdd4cd1a7a76d59cfcf1cc
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1444991
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1444991 - Part 2: Parse webidl productions in xpidl, r=mccr8 They are parsed into a WebIDL object, and lowered into C++, Rust, and XPT. For C++ code, we generate a correctly namespaced forward declaration. In Rust, the types are exposed as `*const c_void`, as we don't have WebIDL type information there. The XPT code generator needs to know the header filename in order to perform correct codegen, so we also get that information.
xpcom/idl-parser/xpidl/header.py
xpcom/idl-parser/xpidl/jsonxpt.py
xpcom/idl-parser/xpidl/xpidl.py
--- a/xpcom/idl-parser/xpidl/header.py
+++ b/xpcom/idl-parser/xpidl/header.py
@@ -228,29 +228,43 @@ def print_header(idl, fd, filename):
 
     for p in idl.productions:
         if p.kind == 'include':
             continue
         if p.kind == 'cdata':
             fd.write(p.data)
             continue
 
+        if p.kind == 'webidl':
+            write_webidl(p, fd)
+            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)})
 
+
+def write_webidl(p, fd):
+    path = p.native.split('::')
+    for seg in path[:-1]:
+        fd.write("namespace %s {\n" % seg)
+    fd.write("class %s; /* webidl %s */\n" % (path[-1], p.name))
+    for seg in reversed(path[:-1]):
+        fd.write("} // namespace %s\n" % seg)
+    fd.write("\n")
+
+
 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 }}
 
--- a/xpcom/idl-parser/xpidl/jsonxpt.py
+++ b/xpcom/idl-parser/xpidl/jsonxpt.py
@@ -73,16 +73,24 @@ def get_type(type, calltype, iid_is=None
         }
 
     if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
         return {
             'tag': 'TD_INTERFACE_TYPE',
             'name': type.name,
         }
 
+    if isinstance(type, xpidl.WebIDL):
+        return {
+            'tag': 'TD_DOMOBJECT',
+            'name': type.name,
+            'native': type.native,
+            'headerFile': type.headerFile,
+        }
+
     if isinstance(type, xpidl.Native):
         if type.specialtype:
             return {
                 'tag': TypeMap[type.specialtype]
             }
         elif iid_is is not None:
             return {
                 'tag': 'TD_INTERFACE_IS_TYPE',
--- a/xpcom/idl-parser/xpidl/xpidl.py
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -560,16 +560,60 @@ class Native(object):
         else:
             m = calltype != 'in' and '*mut ' or ''
         return "%s%s" % (m, name)
 
     def __str__(self):
         return "native %s(%s)\n" % (self.name, self.nativename)
 
 
+class WebIDL(object):
+    kind = 'webidl'
+
+    def __init__(self, name, location):
+        self.name = name
+        self.location = location
+
+    def __eq__(self, other):
+        return other.kind == 'webidl' and self.name == other.name
+
+    def resolve(self, parent):
+        # XXX(nika): We don't handle _every_ kind of webidl object here (as that
+        # would be hard). For example, we don't support nsIDOM*-defaulting
+        # interfaces.
+        # TODO: More explicit compile-time checks?
+
+        assert parent.webidlconfig is not None, \
+            "WebIDL declarations require passing webidlconfig to resolve."
+
+        # Resolve our native name according to the WebIDL configs.
+        config = parent.webidlconfig.get(self.name, {})
+        self.native = config.get('nativeType')
+        if self.native is None:
+            self.native = "mozilla::dom::%s" % self.name
+        self.headerFile = config.get('headerFile')
+        if self.headerFile is None:
+            self.headerFile = self.native.replace('::', '/') + '.h'
+
+        parent.setName(self)
+
+    def isScriptable(self):
+        return True  # All DOM objects are script exposed.
+
+    def nativeType(self, calltype):
+        return "%s %s" % (self.native, calltype != 'in' and '* *' or '*')
+
+    def rustType(self, calltype):
+        # Just expose the type as a void* - we can't do any better.
+        return "%s*const libc::c_void" % (calltype != 'in' and '*mut ' or '')
+
+    def __str__(self):
+        return "webidl %s\n" % self.name
+
+
 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
@@ -1160,16 +1204,17 @@ class IDLParser(object):
         'in': 'IN',
         'inout': 'INOUT',
         'out': 'OUT',
         'attribute': 'ATTRIBUTE',
         'raises': 'RAISES',
         'readonly': 'READONLY',
         'native': 'NATIVE',
         'typedef': 'TYPEDEF',
+        'webidl': 'WEBIDL',
         }
 
     tokens = [
         'IDENTIFIER',
         'CDATA',
         'INCLUDE',
         'IID',
         'NUMBER',
@@ -1274,17 +1319,18 @@ class IDLParser(object):
     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"""
+                       | native productions
+                       | webidl 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),
@@ -1298,16 +1344,20 @@ class IDLParser(object):
                       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_webidl(self, p):
+        """webidl : WEBIDL IDENTIFIER ';'"""
+        p[0] = WebIDL(name=p[2], location=self.getLocation(p, 2))
+
     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 ']'