Convert call generation to CGThings
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 15 Feb 2012 14:58:34 -0500
changeset 86823 91606f210e752b10fabefd910fe6bc46a6094589
parent 86822 3ce98a4f1f5d42bbaad08c4b197847d8dd3f7757
child 86824 e8a0f580cbe86dc18dd9820b79983b06ba9d801a
push id125
push userbzbarsky@mozilla.com
push dateWed, 15 Feb 2012 19:58:46 +0000
milestone13.0a1
Convert call generation to CGThings
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -170,17 +170,17 @@ class CGGeneric(CGThing):
 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)
+        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()
@@ -718,17 +718,17 @@ 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(CGThing):
+class CGArgumentConverter(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
@@ -882,16 +882,89 @@ def getWrapTemplateForTypeImpl(type, res
 
     else:
         raise TypeError("Need to learn to wrap primitive: %s" % type)
 
 def getWrapTemplateForType(type, descriptorProvider, resultAlreadyAddRefed):
     return getWrapTemplateForTypeImpl(type, "result", descriptorProvider,
                                       resultAlreadyAddRefed)
 
+class CGCallGenerator(CGThing):
+    """
+    A class to generate an actual call to a C++ object.  Assumes that the C++
+    object is stored in a variable named "self".
+    """
+    def __init__(self, errorReport, argCount, returnType, resultAlreadyAddRefed,
+                 descriptorProvider, nativeMethodName):
+        CGThing.__init__(self)
+
+        isFallible = errorReport is not None
+
+        # XXXbz return values that have to go in outparams go here?
+        args = CGList([CGGeneric("arg" + str(i)) for i in range(argCount)], ", ")
+        resultOutParam = returnType is not None and returnType.isString()
+        # Return values that go in outparams go here
+        if resultOutParam:
+            args.append(CGGeneric("result"))
+        if isFallible:
+            args.append(CGGeneric("rv"))
+
+        if returnType is None or returnType.isVoid():
+            # Nothing to declare
+            result = None
+        elif returnType.isPrimitive() and returnType.tag() in builtinNames:
+            result = CGGeneric(builtinNames[returnType.tag()])
+            if returnType.nullable():
+                result = CGWrapper(result, pre="Nullable<", post=">")
+        elif returnType.isString():
+            result = CGGeneric("nsString")
+        elif returnType.isEnum():
+            if returnType.nullable():
+                raise TypeError("We don't support nullable enum return values")
+            result = CGGeneric(returnType.inner.identifier.name + "::value")
+        elif returnType.isInterface() and not returnType.isArrayBuffer():
+            result = CGGeneric(descriptorProvider.getDescriptor(
+                returnType.inner.identifier.name).typeName)
+            if resultAlreadyAddRefed:
+                result = CGWrapper(result, pre="nsRefPtr<", post=">")
+            else:
+                result = CGWrapper(result, post="*")
+        elif returnType.isCallback():
+            # XXXbz we're going to assume that callback types are always
+            # nullable for now.
+            result = CGGeneric("JSObject*")
+        elif returnType.tag() is IDLType.Tags.any:
+            result = CGGeneric("jsval")
+        else:
+            raise TypeError("Don't know how to declare return value for %s" %
+                            returnType)
+
+        # Build up our actual call
+        self.cgRoot = CGList([], "\n")
+
+        call = CGGeneric(nativeMethodName)
+        call = CGWrapper(call, pre="self->")
+        call = CGList([call, CGWrapper(args, pre="(", post=");")])
+        if result is not None:
+            result = CGWrapper(result, post=" result;")
+            self.cgRoot.prepend(result)
+            if not resultOutParam:
+                call = CGWrapper(call, pre="result = ")
+
+        call = CGWrapper(call, pre="#if 0\n", post="\n#endif")
+        self.cgRoot.append(call)
+
+        if isFallible:
+            self.cgRoot.prepend(CGGeneric("nsresult rv = NS_OK;"))
+            self.cgRoot.append(CGGeneric("if (NS_FAILED(rv)) {"))
+            self.cgRoot.append(CGIndenter(CGGeneric(errorReport)))
+            self.cgRoot.append(CGGeneric("}"))
+
+    def define(self):
+        return self.cgRoot.define()
 
 class PerSignatureCall():
     """
     This class handles the guts of generating code for a particular
     call signature.  A call signature consists of three things:
 
     1) A return type, which can be None to indicate that there is no
        actual return value (e.g. this is an attribute setter) or an
@@ -922,109 +995,51 @@ class PerSignatureCall():
         self.returnType = returnType
         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))]
+        
+        argCount = len(self.arguments)
+        cgThings = [CGArgumentConverter(self.arguments[i], i, self.getArgv(),
+                                        self.getArgc(), self.descriptor) for
+                    i in range(argCount)]
+        cgThings.append(CGGeneric("\n"))
+        cgThings.append(CGIndenter(CGCallGenerator(
+                    self.getErrorReport() if self.isFallible() else None,
+                    argCount, returnType, self.resultAlreadyAddRefed,
+                    descriptor, nativeMethodName)))
         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 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.
-            resultDeclaration = ""
-        elif self.returnType.isPrimitive() and self.returnType.tag() in builtinNames:
-            if self.returnType.nullable():
-                resultDeclaration = "  Nullable<%s> result;\n" % builtinNames[self.returnType.tag()]
-            else:
-                resultDeclaration = "  %s result = 0;\n" % builtinNames[self.returnType.tag()]
-        elif self.returnType.isString():
-            resultDeclaration = "  nsString result;\n"
-        elif self.returnType.isEnum():
-            if self.returnType.nullable():
-                raise TypeError("We don't support nullable enum return values")
-            resultDeclaration = "  %s result;\n" % (self.returnType.inner.identifier.name + "::value")
-        elif self.returnType.isInterface() and not self.returnType.isArrayBuffer():
-            if not self.resultAlreadyAddRefed:
-                resultDeclaration = "  %s *result = NULL;\n"
-            else:
-                resultDeclaration = "  nsRefPtr<%s> result;\n"
-            resultDescriptor = self.descriptor.getDescriptor(
-                self.returnType.inner.identifier.name)
-            resultDeclaration = resultDeclaration % resultDescriptor.typeName
-        elif self.returnType.isCallback():
-            # XXXbz we're going to assume that callback types are always
-            # nullable for now.
-            resultDeclaration = "  JSObject* result = NULL;\n"
-        elif self.returnType.tag() == IDLType.Tags.any:
-            resultDeclaration = "  jsval result;\n"
-        else:
-            raise TypeError("Don't know how to declare return value for %s" %
-                            self.returnType)
-
-        if self.returnType is None or self.returnType.isVoid():
-            resultAssignment = ""
-        else:
-            resultAssignment = "result = "
-
-        if self.isFallible():
-            rvDeclaration =  '  nsresult rv = NS_OK;\n'
-            errorCheckClause = '  if (NS_FAILED(rv)) {\n    %s\n  }\n' \
-                               % self.getErrorReport()
-        else:
-            rvDeclaration = ''
-            errorCheckClause = ''
-
-        return string.Template("""
-${rv}${resultDecl}  // XXXbz need to actually make those methods exist!
-#if 0
-  ${resultAssign}self->${method}(${args});
-#endif
-${errorCheck}""").substitute({ "rv": rvDeclaration,
-                               "resultDecl": resultDeclaration,
-                               "resultAssign": resultAssignment,
-                               "method": self.nativeMethodName,
-                               "args": ', '.join(nativeArgs),
-                               "errorCheck": errorCheckClause })
-
     def wrap_return_value(self):
         resultTemplateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp'}
         return string.Template(
             getWrapTemplateForType(self.returnType, self.descriptor,
                                    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.cgRoot.define() + self.generate_call() +
-                self.wrap_return_value())
+        return (self.cgRoot.define() + 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