Convert argument conversion external interface to CGThings
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 14 Feb 2012 12:15:44 -0500
changeset 86822 3ce98a4f1f5d42bbaad08c4b197847d8dd3f7757
parent 86821 8222d23e721626fe47131d655d21d40f344b8c3e
child 86823 91606f210e752b10fabefd910fe6bc46a6094589
push id125
push userbzbarsky@mozilla.com
push dateWed, 15 Feb 2012 19:58:46 +0000
milestone13.0a1
Convert argument conversion external interface to CGThings
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -140,37 +140,60 @@ static JSClass ConstructorClass = {
   // XXXbz This needs a useful hasInstance hook
   NULL,                  /* hasInstance */
   NULL,                  /* trace */
   NULL                   /* reserved1 */
 };
 """
 
 class CGList(CGThing):
-    def __init__(self, children):
+    def __init__(self, children, joiner=""):
         CGThing.__init__(self)
         self.children = children
+        self.joiner = joiner
     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])
+        return self.joiner.join([child.declare() for child in self.children])
     def define(self):
-        return ''.join([child.define() for child in self.children])
+        return self.joiner.join([child.define() for child in self.children])
 
 class CGGeneric(CGThing):
-    def __init__(self, declare="", define=""):
+    def __init__(self, define="", declare=""):
         self.declareText = declare
         self.defineText = define
     def declare(self):
         return self.declareText
     def define(self):
         return self.defineText
 
+class CGIndenter(CGThing):
+    def __init__(self, child, indentLevel=2):
+        CGThing.__init__(self)
+        self.child = child
+        self.indent = " " * indentLevel
+        # We'll want to insert the indent at the beginnings of lines, but we
+        # don't want to indent empty lines.  So only indent lines that have a
+        # non-newline character on them.
+        self.pattern = re.compile("^(?=[^\n])", re.MULTILINE)
+    def declare(self):
+        decl = self.child.declare()
+        if decl is not "":
+            return re.sub(self.pattern, self.indent, decl)
+        else:
+            return ""
+    def define(self):
+        defn = self.child.define()
+        if defn is not "":
+            return re.sub(self.pattern, self.indent, defn)
+        else:
+            return ""
+
 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)
@@ -279,18 +302,17 @@ class CGAbstractMethod(CGThing):
         if self.inline:
             decorators.append('inline')
         if self.static:
             decorators.append('static')
         decorators.append(self.returnType)
         return ' '.join(decorators)
     def declare(self):
         if self.inline:
-            # Make sure to indent our definition properly
-            return self._define().replace("\n", "\n  ")
+            return self._define()
         return "\n  %s %s(%s);\n" % (self._decorators(), self.name, self._argstring())
     def _define(self):
         return self.definition_prologue() + self.definition_body() + self.definition_epilogue()
     def define(self):
         return "" if self.inline else self._define()
     def definition_prologue(self):
         maybeNewline = " " if self.inline else "\n"
         return "\n%s%s%s(%s)\n{" % (self._decorators(), maybeNewline,
@@ -696,23 +718,24 @@ def getArgumentConversionTemplate(type, 
     return ("  %(jstype)s ${name}_jstype;\n"
             "%(declareArg)s" # No leading whitespace or newline here, on purpose
             "  if (%(converter)s(cx, ${argVal}, &${name}_jstype)) {\n"
             "    %(finalValueSetter)s((${typeName})%(intermediateCast)s${name}_jstype);\n"
             "  } else {\n"
             "    return false;\n"
             "  }\n" % replacements)
 
-class ArgumentConverter():
+class ArgumentConverter(CGThing):
     """
     A class that takes an IDL argument object, its index in the
     argument list, and the argv and argc strings and generates code to
     unwrap the argument to the right native type.
     """
     def __init__(self, argument, index, argv, argc, descriptorProvider):
+        CGThing.__init__(self)
         self.argument = argument
         # XXXbz should optional jsval args get JSVAL_VOID? What about
         # others?
         self.replacementVariables = {
             "index" : index,
             "argc" : argc,
             "argv" : argv,
             "defaultValue" : "JSVAL_NULL",
@@ -735,17 +758,17 @@ class ArgumentConverter():
         if argument.type.isPrimitive():
             self.replacementVariables["typeName"] = builtinNames[argument.type.tag()]
         elif argument.type.isInterface() and not argument.type.isArrayBuffer():
             descriptor = descriptorProvider.getDescriptor(
                 argument.type.inner.identifier.name)
             self.descriptor = descriptor
             self.replacementVariables["typeName"] = descriptor.typeName
 
-    def __str__(self):
+    def define(self):
         return string.Template(
             "\n" + getArgumentConversionTemplate(self.argument.type,
                                                  self.descriptor)
             ).substitute(self.replacementVariables)
 
 def getWrapTemplateForTypeImpl(type, result, descriptorProvider,
                                resultAlreadyAddRefed):
     if type.isSequence() or type.isArray():
@@ -900,32 +923,31 @@ class PerSignatureCall():
         self.arguments = arguments
         self.nativeMethodName = nativeMethodName
         self.descriptor = descriptor
         self.idlNode = idlNode
         self.extendedAttributes = extendedAttributes
         # Default to already_AddRefed on the main thread, raw pointer in workers
         self.resultAlreadyAddRefed = not descriptor.workers
 
+        cgThings = [ArgumentConverter(self.arguments[i], i, self.getArgv(),
+                                      self.getArgc(), self.descriptor) for
+                    i in range(len(self.arguments))]
+        self.cgRoot = CGList(cgThings)
+
     def getArgv(self):
         assert(False) # Override me
     def getArgc(self):
         assert(False) # Override me
     def getErrorReport(self):
         assert(False) # Override me
 
     def isFallible(self):
         return not 'infallible' in self.extendedAttributes
 
-    def unwrap_arguments(self):
-        args = [ArgumentConverter(self.arguments[i], i, self.getArgv(),
-                                  self.getArgc(), self.descriptor) for
-                i in range(len(self.arguments))]
-        return "".join([str(arg) for arg in args])
-
     def generate_call(self):
         nativeArgs = ["arg" + str(i) for i in range(len(self.arguments))]
         # XXXbz arguments that have to go in outparams go here?
         if self.isFallible():
             nativeArgs.append("rv")
         # XXXbz need to have full config to do this retval business right
         if self.returnType is None or self.returnType.isVoid():
             # Nothing to declare.
@@ -991,40 +1013,48 @@ class PerSignatureCall():
                                    self.resultAlreadyAddRefed)
             ).substitute(resultTemplateValues)
 
     def getErrorReport(self):
         return 'return ThrowMethodFailedWithDetails(cx, rv, "%s", "%s");'\
                % (self.descriptor.name, self.idlNode.identifier.name)
 
     def __str__(self):
-        return (self.unwrap_arguments() + self.generate_call() +
+        return (self.cgRoot.define() + self.generate_call() +
                 self.wrap_return_value())
 
 class PerSignatureMethodCall(PerSignatureCall):
     def __init__(self, returnType, arguments, nativeMethodName, descriptor,
                  method, extendedAttributes):
         PerSignatureCall.__init__(self, returnType, arguments, nativeMethodName,
                                   descriptor, method, extendedAttributes)
+
+        # Insert our argv-unwrapping at the beginning of our CGThing list
+        if len(arguments) > 0:
+            requiredArgs = len(arguments)
+            while requiredArgs and arguments[requiredArgs-1].optional:
+                requiredArgs -= 1
+            if requiredArgs > 0:
+                argv = [CGGeneric(
+                        "// XXXbz is this the right place for this check?  Or should it be more\n"
+                        "// up-front somewhere, not per-signature?\n"
+                        "if (argc < %d) {\n"
+                        "  return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n"
+                        "}" % requiredArgs)]
+            else:
+                argv = []
+            argv.append(CGGeneric("JS::Value *argv = JS_ARGV(cx, vp);"))
+            self.cgRoot.prepend(CGWrapper(CGIndenter(CGList(argv, "\n")),
+                                          pre="\n",
+                                          post="\n"))
+
     def getArgv(self):
         return "argv" if len(self.arguments) > 0 else ""
     def getArgc(self):
         return "argc"
-    def unwrap_arguments(self):
-        requiredArgs = len(self.arguments)
-        while requiredArgs and self.arguments[requiredArgs-1].optional:
-            requiredArgs -= 1
-        argv = "  JS::Value *argv = JS_ARGV(cx, vp);\n" if len(self.arguments) > 0 else ""
-        return ("""
-  // XXXbz is this the right place for this check?  Or should it be more
-  // up-front somewhere, not per-signature?
-  if (argc < %d) {
-    return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
-  }
-%s""" % (requiredArgs, argv)) + PerSignatureCall.unwrap_arguments(self)
 
 class GetterSetterCall(PerSignatureCall):
     def __init__(self, returnType, arguments, nativeMethodName, descriptor,
                  attr, extendedAttributes):
         PerSignatureCall.__init__(self, returnType, arguments, nativeMethodName,
                                   descriptor, attr, extendedAttributes)
     def getArgv(self):
         return "vp"
@@ -1196,17 +1226,17 @@ class CGDescriptor(CGThing):
             cgThings.append(CGClassTraceHook(descriptor))
 
         # XXXbz this should check for [NoInterfaceObject]
         if True:
             cgThings.append(CGConstructorJSClass(descriptor))
         cgThings.extend([CGDOMJSClass(descriptor),
                          CGPrototypeJSClass(descriptor),
                          CGCreateProtoObjectMethod(descriptor),
-                         CGGetProtoObjectMethod(descriptor)])
+                         CGIndenter(CGGetProtoObjectMethod(descriptor))])
 
         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()