Do GlobalGen stuff with the Codegen machinery.
authorBobby Holley <bobbyholley@gmail.com>
Tue, 31 Jan 2012 18:58:26 +0100
changeset 85189 394b1f82d6d3f6a88f5b57d3dc1360a5658c43c3
parent 85188 988157b08a4bb2ed92a78624aa15f8a344c845c8
child 85190 8a64134a87f0f51a60c09c7428d87b003f609277
push id82
push userbobbyholley@gmail.com
push dateTue, 31 Jan 2012 18:00:24 +0000
milestone12.0a1
Do GlobalGen stuff with the Codegen machinery.
dom/bindings/BindingGen.py
dom/bindings/Codegen.py
dom/bindings/GlobalGen.py
dom/bindings/parser/WebIDL.py
--- a/dom/bindings/BindingGen.py
+++ b/dom/bindings/BindingGen.py
@@ -1,41 +1,41 @@
 # 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/.
 
 import os
 import cPickle
 import WebIDL
 from Configuration import *
-from Codegen import CGRoot
+from Codegen import CGBindingRoot
 
 def generate_binding_header(config, outputprefix):
     """
     |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')
-    root = CGRoot(config, outputprefix)
+    root = CGBindingRoot(config, outputprefix)
     f.write(root.declare())
     f.close()
 
 def generate_binding_cpp(config, outputprefix):
     """
     |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')
-    root = CGRoot(config, outputprefix)
+    root = CGBindingRoot(config, outputprefix)
     f.write(root.define())
     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
@@ -49,16 +49,25 @@ class CGList(CGThing):
         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 CGGeneric(CGThing):
+    def __init__(self, declare="", define=""):
+        self.declareText = declare
+        self.defineText = define
+    def declare(self):
+        return self.declareText
+    def define(self):
+        return self.defineText
+
 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)
@@ -381,17 +390,61 @@ class CGDescriptor(CGThing):
         allCGThings = CGList(cgThings)
         allCGThings = CGWrapper(allCGThings, post="\n")
         self.cgRoot = CGWrapper(CGNamespace(descriptor.name, allCGThings), post="\n")
     def declare(self):
         return self.cgRoot.declare()
     def define(self):
         return self.cgRoot.define()
 
-class CGRoot(CGThing):
+class CGNamespacedEnum(CGThing):
+    def __init__(self, namespace, enumName, names, values, comment=""):
+
+        if not values:
+            values = [None for i in range(0, len(names))]
+
+        # Account for explicit enum values.
+        entries = []
+        for i in range(0, len(names)):
+            entry = names[i] if values[i] is None else "%s = %s" % (names[i], values[i])
+            entries.append(entry)
+
+        # Append a Count.
+        entries.append('Count')
+
+        # Indent.
+        entries = ['  ' + e for e in entries]
+
+        # Buildthe enum body.
+        enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
+        curr = CGGeneric(declare=enumstr)
+
+        # Add some whitespace padding.
+        curr = CGWrapper(curr, pre='\n',post='\n')
+
+        # Add the namespace.
+        curr = CGNamespace(namespace, curr)
+
+        # Add the typedef
+        typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+        curr = CGList([curr, CGGeneric(declare=typedef)])
+
+        # Save the result.
+        self.node = curr
+
+    def declare(self):
+        return self.node.declare()
+    def define(self):
+        assert False # Only for headers.
+
+class CGBindingRoot(CGThing):
+    """
+    Root codegen class for binding generation. Instantiate the class, and call
+    declare or define to generate header or cpp code (respectively).
+    """
     def __init__(self, config, prefix):
 
         # Do codegen for all the descriptors.
         curr = CGList([CGDescriptor(x) for x in config.getConcreteDescriptors()])
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'],
                                  CGWrapper(curr, pre="\n"))
@@ -407,8 +460,51 @@ class CGRoot(CGThing):
 
         # Store the final result.
         self.root = curr
 
     def declare(self):
         return self.root.declare()
     def define(self):
         return self.root.define()
+
+
+class CGGlobalRoot():
+    """
+    Root codegen class for global code generation. Instantiate the class, and call
+
+    It's possible that we may at some point wish to generate more than just
+    PrototypeList.h. As such, this class eschews the define/declare
+    architecture (and thus does not inherit from CGThing).
+
+    To generate code, call the method associated with the target.
+    """
+    def __init__(self, config, prefix):
+        self.config = config
+        self.prefix = prefix
+
+    def prototypeList_h(self):
+
+        # Prototype ID enum.
+        protos = [d.name for d in self.config.getConcreteDescriptors()]
+        idThing = CGNamespacedEnum('id', 'ID', protos, None)
+
+        # Depth enum.
+        depths = [d.interface.inheritanceDepth() for d in self.config.getConcreteDescriptors()]
+        depthComment = '/* The depths at which we expect the above IDs in inheritance chains;\n' + \
+                       '   0 means there is no superclass */\n'
+        depthThing = CGNamespacedEnum('depth', 'Depth', protos, depths, depthComment)
+
+        # Combine.
+        curr = CGList([idThing, depthThing])
+
+        # Wrap all of that in our namespaces.
+        curr = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'],
+                                 CGWrapper(curr, pre="\n"))
+
+        # Add include guards.
+        curr = CGIncludeGuard(self.prefix, curr)
+
+        # Add the auto-generated comment.
+        curr = CGWrapper(curr, pre="/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n")
+
+        # Do header generation on the reuslt.
+        return curr.declare()
--- a/dom/bindings/GlobalGen.py
+++ b/dom/bindings/GlobalGen.py
@@ -5,98 +5,32 @@
 # We do one global pass over all the WebIDL to generate our prototype enum
 # and generate information for subsequent phases.
 
 import os
 import cStringIO
 import WebIDL
 import cPickle
 from Configuration import *
+from Codegen import CGGlobalRoot
 
-def generate_prototype_list(config, filename):
+def generate_prototype_list(config, outputprefix):
 
     # Read a copy of the old file, so that we don't touch it if it hasn't changed.
+    filename = outputprefix + '.h'
     oldFileContents = ""
     try:
         oldFile = open(filename, 'r')
         oldFileContents = ''.join(oldFile.readlines())
         oldFile.close()
     except:
         pass
 
-    prologue = autogenerated_comment + """
-#ifndef mozilla_dom_bindings_PrototypeList_h
-#define mozilla_dom_bindings_PrototypeList_h
-
-namespace mozilla {
-namespace dom {
-namespace bindings {
-namespace prototypes {
-namespace id {
-
-enum ID
-{
-"""
-
-    middle = """
-};
-
-} // namespace id
-
-namespace depth {
-
-/* The depths at which we expect the above IDs in inheritance chains;
-   0 means there is no superclass */
-enum Depth
-{
-"""
-    
-    epilogue = """
-};
-
-} // namespace depth
-
-typedef id::ID ID;
-typedef depth::Depth Depth;
-
-} // namespace prototypes
-} // namespace bindings
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_bindings_PrototypeList_h
-"""
-
-    # Make a list of the protos.
-    protoList = [x.name for x in config.getConcreteDescriptors()]
-
-    # Append the enum count.
-    protoList.append('Count')
-
-    # Add appropriate indentation before the prototype strings.
-    protoList = ["  " + p for p in protoList]
-
-    # Start the enum at 0
-    protoList[0] = protoList[0] + " = 0"
-
-    # XXXbholley - This calculation should go in IDLInterface.inheritanceDepth()
-    # or something.
-    indexList = []
-    for descriptor in config.getConcreteDescriptors():
-        index = 0
-        interface = descriptor.interface
-        while interface.parent:
-            interface = interface.parent
-            index = index + 1
-        indexList.append(descriptor.name + " = " + str(index));
-    indexList.append('IndexCount');
-    indexList = ["  " + i for i in indexList]
-
-    newFileContents = (prologue + ',\n'.join(protoList) + middle +
-                       ',\n'.join(indexList) + epilogue)
+    root = CGGlobalRoot(config, outputprefix)
+    newFileContents = root.prototypeList_h()
 
     if newFileContents == oldFileContents:
         print "Prototype list hasn't changed - not touching %s" % (filename)
         return
 
     print "Generating prototype list: %s" % (filename)
     f = open(filename, 'w')
     f.write(newFileContents)
@@ -133,12 +67,12 @@ def main():
     resultsFile = open('ParserResults.pkl', 'wb')
     cPickle.dump(parserResults, resultsFile, -1)
     resultsFile.close()
 
     # Load the configuration.
     config = Configuration(configFile, parserResults)
 
     # Generate the prototype list header.
-    generate_prototype_list(config, "PrototypeList.h")
+    generate_prototype_list(config, "PrototypeList")
 
 if __name__ == '__main__':
     main()
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -440,16 +440,24 @@ class IDLInterface(IDLObjectWithScope):
         self._callback = value
 
     def isCallback(self):
         return self._callback
 
     def addExtendedAttributes(self, attrs):
         pass
 
+    def inheritanceDepth(self):
+        depth = 0
+        parent = self.parent
+        while parent:
+            depth = depth + 1
+            parent = parent.parent
+        return depth
+
 class IDLEnum(IDLObjectWithIdentifier):
     def __init__(self, location, parentScope, name, values):
         assert isinstance(parentScope, IDLScope)
         assert isinstance(name, IDLUnresolvedIdentifier)
 
         if len(values) != len(set(values)):
             raise WebIDLError("Enum %s has multiple identical strings" % name.name, location)