Reworked a bunch of codegen stuff. Renamed some classes, expanded the recursive structure, and hoisted more of our codegen into the CG system. Please take a look at this patch to see how things changed.
authorBobby Holley <bobbyholley@gmail.com>
Fri, 27 Jan 2012 21:05:22 +0100
changeset 85177 379ad3ace1ce2e4e1a5889fd9700ee60636a450f
parent 85176 7adc9bb9ca5800d1dfa70ed2da1b7dd7ce5fa123
child 85178 6f77b9ab43d79725184c30e5833cdca5be48a371
push id75
push userbobbyholley@gmail.com
push dateFri, 27 Jan 2012 20:06:15 +0000
milestone12.0a1
Reworked a bunch of codegen stuff. Renamed some classes, expanded the recursive structure, and hoisted more of our codegen into the CG system. Please take a look at this patch to see how things changed.
dom/bindings/BindingGen.py
dom/bindings/Codegen.py
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/BindingGen.py
@@ -4,72 +4,50 @@
 
 import os
 import cPickle
 import WebIDL
 from Codegen import *
 
 def generate_binding_header(config, outputprefix):
     """
-    protoList is the list of prototypes defined by this binding
-    outputprefix is a prefix to use for the header guards and filename
+    |config| Is the configuration object.
+    |outputprefix| is a prefix to use for the header guards and filename.
     """
 
     filename = outputprefix + ".h"
     print "Generating binding header: %s" % (filename)
     f = open(filename, 'w')
 
     prologue = """
 /* THIS FILE IS AUTO-GENERATED - DO NOT EDIT */
 
 #ifndef mozilla_dom_bindings_%s_h__
 #define mozilla_dom_bindings_%s_h__
 
 #include "mozilla/dom/bindings/DOMJSClass.h"
 #include "mozilla/dom/bindings/Utils.h"
 
-namespace mozilla {
-namespace dom {
-namespace bindings {
-namespace prototypes {
-
 """ % (outputprefix, outputprefix)
 
     epilogue = """
-} // namespace prototypes
-} // namespace bindings
-} // namespace dom
-} // namespace mozilla
 
 #endif // mozilla_dom_bindings_%s_h__
 """ % (outputprefix)
 
-    # Write from templates.
+    # Write recursively.
     f.write(prologue)
-    for domClass in config.dom_classes.values():
-        for implementation in domClass.implementations:
-            f.write("namespace %s {\n" % (implementation.name))
-            for cgThing in implementation.codegenThings:
-                f.write(cgThing.declare())
-            f.write("\n} // namespace %s\n\n" % (implementation.name))
-
+    f.write(config.cgRoot.declare())
     f.write(epilogue)
     f.close()
 
 def generate_binding_cpp(config, outputprefix):
     """
-    protoStructure is a list containing information about the prototypes
-    this binding file defines.  Each entry is a dict with the following entries:
-
-      protoName: the name of the prototype in our prototype ID list
-      canonicalName: the JS-visible name of the prototype
-      interfaceChain: a list of the prototypes that this prototype inherits
-                      from, in order from least-derived to most-derived.
-      nativeIsISupports: a boolean indicating whether the native this class
-                         wraps is a subclass of nsISupports.
+    |config| Is the configuration object.
+    |outputprefix| is a prefix to use for the header guards and filename.
     """
 
     filename = outputprefix + ".cpp"
     print "Generating binding implementation: %s" % (filename)
     f = open(filename, 'w')
 
     parentIncludeTemplate = """#include "%sBinding.h"
 """
@@ -81,40 +59,23 @@ def generate_binding_cpp(config, outputp
             parentFileName = os.path.split(parentInterface.filename())[1].replace(".webidl", "");
             includes += parentIncludeTemplate % parentFileName
 
     prologue = """/* THIS FILE IS AUTO-GENERATED - DO NOT EDIT */
 
 %s
 #include "%s.h"
 
-namespace mozilla {
-namespace dom {
-namespace bindings {
-namespace prototypes {
-
 """ % (includes, outputprefix)
 
-    epilogue = """
-} // namespace prototypes
-} // namespace bindings
-} // namespace dom
-} // namespace mozilla
-"""
+    epilogue = """"""
 
-    # Write from templates.
+    # Write recursively.
     f.write(prologue)
-    for domClass in config.dom_classes.values():
-        for implementation in domClass.implementations:
-            f.write("namespace %s {\n\n" % (implementation.name))
-            for cgThing in implementation.codegenThings:
-                definition = cgThing.define()
-                if definition:
-                    f.write(cgThing.define() + "\n")
-            f.write("} // namespace %s\n\n" % (implementation.name))
+    f.write(config.cgRoot.define())
     f.write(epilogue)
     f.close()
 
 def main():
 
     # Parse arguments.
     from optparse import OptionParser
     usagestring = "usage: %prog [header|cpp] configFile outputPrefix webIDLFile"
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1,30 +1,38 @@
 # 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/.
 
 # Common codegen classes.
 
-class CodegenThing():
+class CGThing():
     """
-    Abstract base case for things that spit out code within a class.
+    Abstract base case for things that spit out code.
     """
-    def __init__(self, implementation):
-        self.implementation = implementation
+    def __init__(self):
+        pass # Nothing for now
     def declare(self):
         """Produce code for a header file."""
         assert(False)  # Override me!
     def define(self):
         """Produce code for a cpp file."""
         assert(False) # Override me!
 
-class DOMJSClass(CodegenThing):
+class CGImplThing(CGThing):
+    """
+    CGThing with a reference to a DOMClassImplementation.
+    """
     def __init__(self, implementation):
-        CodegenThing.__init__(self, implementation)
+        CGThing.__init__(self)
+        self.implementation = implementation
+
+class CGDOMJSClass(CGImplThing):
+    def __init__(self, implementation):
+        CGImplThing.__init__(self, implementation)
     def declare(self):
         return "extern DOMJSClass Class;\n\n"
     def define(self):
         prototypeChainString = ', '.join(['id::' + proto \
                                           for proto in self.implementation.prototypeChain])
         return """DOMJSClass Class = {
   { "%s",
     JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
@@ -32,29 +40,71 @@ class DOMJSClass(CodegenThing):
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
   },
   { %s }, -1, %s
 };
 """ % (self.implementation.domClass.name, prototypeChainString,
        str(self.implementation.nativeIsISupports).lower())
 
+class CGList(CGThing):
+    def __init__(self, children):
+        CGThing.__init__(self)
+        self.children = children
+    def append(self, child):
+        self.children.append(child)
+    def prepend(self, child):
+        self.children.insert(0, child)
+    def declare(self):
+        return ''.join([child.declare() for child in self.children])
+    def define(self):
+        return ''.join([child.define() for child in self.children])
 
+class CGWrapper(CGThing):
+    """
+    Generic CGThing that wraps other CGThings with pre and post text.
+    """
+    def __init__(self, child, pre="", post="",
+                 declarePre=None, declarePost=None,
+                 definePre=None, definePost=None):
+        CGThing.__init__(self)
+        self.child = child
+        self.declarePre = declarePre or pre
+        self.declarePost = declarePost or post
+        self.definePre = definePre or pre
+        self.definePost = definePost or post
+    def declare(self):
+        return self.declarePre + self.child.declare() + self.declarePost
+    def define(self):
+        return self.definePre + self.child.define() + self.definePost
 
+class CGNamespace(CGWrapper):
+    def __init__(self, namespace, child):
+        pre = "namespace %s {\n" % namespace
+        post="} // namespace %s\n" % namespace
+        CGWrapper.__init__(self, child, pre=pre, post=post)
+    @staticmethod
+    def build(namespaces, child):
+        """
+        Static helper method to build multiple wrapped namespaces.
+        """
+        if not namespaces:
+            return child
+        return CGNamespace(namespaces[0], CGNamespace.build(namespaces[1:], child))
 
 class Argument():
     def __init__(self, argType, name):
         self.argType = argType
         self.name = name
     def __str__(self):
         return self.argType + ' ' + self.name
 
-class AbstractMethod(CodegenThing):
+class CGAbstractMethod(CGImplThing):
     def __init__(self, implementation, name, returnType, args, inline=False, static=False):
-        CodegenThing.__init__(self, implementation)
+        CGImplThing.__init__(self, implementation)
         self.name = name
         self.returnType = returnType
         self.args = args
         self.inline = inline
         self.static = static
     def _argstring(self):
         return ', '.join([str(a) for a in self.args])
     def _decorators(self):
@@ -138,20 +188,20 @@ class AttrDefiner:
 
         return ("  static JSPropertySpec props[] = {\n" +
                 ',\n'.join(attrdecls) + "\n" +
                 "  };\n" +
                 "  if (!JS_DefineProperties(aCx, ourProto, props)) {\n" +
                 "    return NULL;\n" +
                 "  }\n")
 
-class CreateProtoObjectMethod(AbstractMethod):
+class CGCreateProtoObjectMethod(CGAbstractMethod):
     def __init__(self, implementation):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
-        AbstractMethod.__init__(self, implementation, 'CreateProtoObject', 'JSObject*', args)
+        CGAbstractMethod.__init__(self, implementation, 'CreateProtoObject', 'JSObject*', args)
     def definition_body(self):
         protoChain = self.implementation.prototypeChain
         if len(protoChain) == 1:
             getParentProto = "GetCanonicalObjectProto(aCx, aGlobal)"
         else:
             parentProtoName = self.implementation.prototypeChain[-2]
             getParentProto = "%s::GetProtoObject(aCx, aGlobal)" % (parentProtoName)
 
@@ -169,21 +219,21 @@ class CreateProtoObjectMethod(AbstractMe
     return NULL;
   }
 
 %s
 %s
 
   return ourProto;""" % (getParentProto, defineMethods, defineAttributes)
 
-class GetProtoObjectMethod(AbstractMethod):
+class CGGetProtoObjectMethod(CGAbstractMethod):
     def __init__(self, implementation):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
-        AbstractMethod.__init__(self, implementation, 'GetProtoObject',
-                                'JSObject*', args, inline=True)
+        CGAbstractMethod.__init__(self, implementation, 'GetProtoObject',
+                                  'JSObject*', args, inline=True)
     def definition_body(self):
         return """
   /* Get the prototype object for this class.  This will create the prototype
      as needed. */
 
   /* Make sure our global is sane.  Hopefully we can remove this sometime */
   if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
     return NULL;
@@ -194,96 +244,113 @@ class GetProtoObjectMethod(AbstractMetho
   JSObject *ourProto = protoArray[id::%s];
   if (!ourProto) {
     ourProto = protoArray[id::%s] = CreateProtoObject(aCx, aGlobal);
   }
 
   /* ourProto might _still_ be null, but that's OK */
   return ourProto;""" % (self.implementation.name, self.implementation.name)
 
-class NativeMethod(AbstractMethod):
+class CGNativeMethod(CGAbstractMethod):
     def __init__(self, implementation, method):
         self.method = method
         args = [Argument('JSContext*', 'cx'), Argument('uintN', 'argc'),
                 Argument('js::Value*', 'vp')]
-        AbstractMethod.__init__(self, implementation, method.identifier.name,
-                                'JSBool', args)
+        CGAbstractMethod.__init__(self, implementation, method.identifier.name,
+                                  'JSBool', args)
     def declare(self):
         # We only have an implementation
         return ""
     def definition_body(self):
         # NEED TO ADD SOME IMPL HERE
         return """
   return false;"""
 
-class NativeGetter(AbstractMethod):
+class CGNativeGetter(CGAbstractMethod):
     def __init__(self, implementation, attr):
         self.attr = attr
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
                 Argument('jsid', 'id'), Argument('js::Value*', 'vp')]
-        AbstractMethod.__init__(self, implementation,
-                                'get_'+attr.identifier.name, 'JSBool', args)
+        CGAbstractMethod.__init__(self, implementation,
+                                  'get_'+attr.identifier.name, 'JSBool', args)
     def declare(self):
         # We only have an implementation
         return ""
     def definition_body(self):
         # NEED TO ADD SOME IMPL HERE
         return """
   return false;"""
 
-class NativeSetter(AbstractMethod):
+class CGNativeSetter(CGAbstractMethod):
     def __init__(self, implementation, attr):
         self.attr = attr
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
                 Argument('jsid', 'id'), Argument('JSBool', 'strict'),
                 Argument('js::Value*', 'vp')]
-        AbstractMethod.__init__(self, implementation,
-                                'set_'+attr.identifier.name, 'JSBool', args)
+        CGAbstractMethod.__init__(self, implementation,
+                                  'set_'+attr.identifier.name, 'JSBool', args)
     def declare(self):
         # We only have an implementation
         return ""
     def definition_body(self):
         # NEED TO ADD SOME IMPL HERE
         return """
   return false;"""
 
-class DOMClassImplementation():
+class DOMClassImplementation(CGThing):
     def __init__(self, domClass, implConf):
+        CGThing.__init__(self)
         self.domClass = domClass
         self.nativeClass = implConf['nativeClass']
         self.workers = implConf.get('workers', False)
         def make_name(name):
             return name + "_workers" if self.workers else name
         self.name = make_name(domClass.name)
         self.nativeIsISupports = not self.workers
 
         # Build the prototype chain.
         self.prototypeChain = []
         parent = domClass.interface
         while parent:
             self.prototypeChain.insert(0, make_name(parent.identifier.name))
             parent = parent.parent
 
         # XXXbholley - Not everything should actually have a jsclass.
-        self.codegenThings = [NativeMethod(self, m) for m in
-                              domClass.interface.members if m.isMethod()]
-        self.codegenThings.extend([NativeGetter(self, a) for a in
-                                   domClass.interface.members if a.isAttr()])
-        self.codegenThings.extend([NativeSetter(self, a) for a in
-                                   domClass.interface.members if
-                                   a.isAttr() and not a.readonly])
-        self.codegenThings.extend([DOMJSClass(self),
-                                   CreateProtoObjectMethod(self),
-                                   GetProtoObjectMethod(self)])
+        cgThings = [CGNativeMethod(self, m) for m in
+                    domClass.interface.members if m.isMethod()]
+        cgThings.extend([CGNativeGetter(self, a) for a in
+                         domClass.interface.members if a.isAttr()])
+        cgThings.extend([CGNativeSetter(self, a) for a in
+                         domClass.interface.members if
+                         a.isAttr() and not a.readonly])
+        cgThings.extend([CGDOMJSClass(self),
+                         CGCreateProtoObjectMethod(self),
+                         CGGetProtoObjectMethod(self)])
 
-class DOMClass():
+        allCGThings = CGList(cgThings)
+        allCGThings = CGWrapper(allCGThings, post="\n")
+        self.cgRoot = CGWrapper(CGNamespace(self.name, allCGThings), post="\n")
+    def declare(self):
+        return self.cgRoot.declare()
+    def define(self):
+        return self.cgRoot.define()
+
+
+class DOMClass(CGThing):
     def __init__(self, classConf, interface):
+        CGThing.__init__(self)
         self.name = classConf['name']
         self.interface = interface
-        self.implementations = [DOMClassImplementation(self, implConf) for implConf in classConf['implementations']]
+        self.implementations = [DOMClassImplementation(self, implConf)
+                                for implConf in classConf['implementations']]
+        self.cgRoot = CGList(self.implementations)
+    def declare(self):
+        return self.cgRoot.declare()
+    def define(self):
+        return self.cgRoot.define()
 
 class Configuration:
     def __init__(self, filename, parseData):
         self.configFile = {}
         execfile(filename, self.configFile)
 
         # We need dom_classes.
         if 'dom_classes' not in self.configFile:
@@ -293,9 +360,11 @@ class Configuration:
         # already filtered the parse data so that this does what we want.
         self.dom_classes = dict()
         for classConf in self.configFile['dom_classes']:
             name = classConf['name']
             if name not in parseData:
                 continue
             self.dom_classes[name] = DOMClass(classConf, parseData[name])
 
-
+        classCollection = [cls for cls in self.dom_classes.values()]
+        self.cgRoot = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'],
+                                        CGWrapper(CGList(classCollection), pre="\n"))