Bug 1041262, part 3 - Codegen scoping changes. r=peterv.
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 05 Aug 2014 11:53:37 -0500
changeset 199120 f552c962fea33160cf2b218c21db082e4001f6e0
parent 199119 26f6fc591b4cf4e8b6e1e9c2c6971aefa184af10
child 199121 fb1eefe3c485fe8e569900cd0079667878ba2bb7
push id27298
push userryanvm@gmail.com
push dateTue, 12 Aug 2014 20:25:16 +0000
treeherdermozilla-central@7fc96293ada8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1041262
milestone34.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 1041262, part 3 - Codegen scoping changes. r=peterv. This changes CGProxyNamedPresenceChecker to permit declaring the `found` and/or `result` variables in an outer scope. The default is still to declare them in the innermost scope containing the method call.
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5959,20 +5959,25 @@ def needScopeObject(returnType, argument
 
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
     errorReport should be a CGThing for an error report or None if no
     error reporting is needed.
+
+    resultVar: If the returnType is not void, then the result of the call is
+    stored in a C++ variable named by resultVar. The caller is responsible for
+    declaring the result variable. If the caller doesn't care about the result
+    value, resultVar can be omitted.
     """
     def __init__(self, errorReport, arguments, argsPre, returnType,
                  extendedAttributes, descriptorProvider, nativeMethodName,
-                 static, object="self", argsPost=[]):
+                 static, object="self", argsPost=[], resultVar=None):
         CGThing.__init__(self)
 
         assert errorReport is None or isinstance(errorReport, CGThing)
 
         isFallible = errorReport is not None
 
         resultAlreadyAddRefed = isResultAlreadyAddRefed(extendedAttributes)
         result, resultOutParam, resultRooter, resultArgs = \
@@ -6013,46 +6018,55 @@ class CGCallGenerator(CGThing):
             if needsConst(a):
                 arg = CGWrapper(arg, pre="Constify(", post=")")
             # And convert NonNull<T> to T&
             if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or
                 a.type.isDOMString()):
                 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
             args.append(arg)
 
+        needResultDecl = False
+
         # Return values that go in outparams go here
         if resultOutParam is not None:
-            if resultOutParam is "ref":
-                args.append(CGGeneric("result"))
-            else:
-                assert resultOutParam is "ptr"
-                args.append(CGGeneric("&result"))
+            if resultVar is None:
+                needResultDecl = True
+                resultVar = "result"
+            if resultOutParam == "ref":
+                args.append(CGGeneric(resultVar))
+            else:
+                assert resultOutParam == "ptr"
+                args.append(CGGeneric("&" + resultVar))
 
         if isFallible:
             args.append(CGGeneric("rv"))
         args.extend(CGGeneric(arg) for arg in argsPost)
 
         # Build up our actual call
         self.cgRoot = CGList([])
 
         call = CGGeneric(nativeMethodName)
         if not static:
             call = CGWrapper(call, pre="%s->" % object)
         call = CGList([call, CGWrapper(args, pre="(", post=");\n")])
-        if result is not None:
+        if resultVar is None and result is not None:
+            needResultDecl = True
+            resultVar = "result"
+
+        if needResultDecl:
             if resultRooter is not None:
                 self.cgRoot.prepend(resultRooter)
             if resultArgs is not None:
                 resultArgs = "(%s)" % resultArgs
             else:
                 resultArgs = ""
-            result = CGWrapper(result, post=(" result%s;\n" % resultArgs))
+            result = CGWrapper(result, post=(" %s%s;\n" % (resultVar, resultArgs)))
             self.cgRoot.prepend(result)
             if not resultOutParam:
-                call = CGWrapper(call, pre="result = ")
+                call = CGWrapper(call, pre=resultVar + " = ")
 
         call = CGWrapper(call)
         self.cgRoot.append(call)
 
         if isFallible:
             self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n"))
             self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n"))
             self.cgRoot.append(CGGeneric("if (rv.Failed()) {\n"))
@@ -6240,17 +6254,17 @@ class CGPerSignatureCall(CGThing):
     """
     # XXXbz For now each entry in the argument list is either an
     # IDLArgument or a FakeArgument, but longer-term we may want to
     # have ways of flagging things like JSContext* or optional_argc in
     # there.
 
     def __init__(self, returnType, arguments, nativeMethodName, static,
                  descriptor, idlNode, argConversionStartsAt=0, getter=False,
-                 setter=False, isConstructor=False):
+                 setter=False, isConstructor=False, resultVar=None):
         assert idlNode.isMethod() == (not getter and not setter)
         assert idlNode.isAttr() == (getter or setter)
         # Constructors are always static
         assert not isConstructor or static
 
         CGThing.__init__(self)
         self.returnType = returnType
         self.descriptor = descriptor
@@ -6362,22 +6376,24 @@ class CGPerSignatureCall(CGThing):
                                     lenientFloatCode=lenientFloatCode))
 
         if needsUnwrap:
             # Something depends on having the unwrapped object, so unwrap it now.
             xraySteps = []
             # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
             # not null.
             xraySteps.append(
-                CGGeneric(string.Template(dedent("""
+                CGGeneric(fill(
+                    """
                     ${obj} = js::CheckedUnwrap(${obj});
                     if (!${obj}) {
                       return false;
                     }
-                    """)).substitute({'obj': unwrappedVar})))
+                    """,
+                    obj=unwrappedVar)))
             if isConstructor:
                 # If we're called via an xray, we need to enter the underlying
                 # object's compartment and then wrap up all of our arguments into
                 # that compartment as needed.  This is all happening after we've
                 # already done the conversions from JS values to WebIDL (C++)
                 # values, so we only need to worry about cases where there are 'any'
                 # or 'object' types, or other things that we represent as actual
                 # JSAPI types, present.  Effectively, we're emulating a
@@ -6392,17 +6408,17 @@ class CGPerSignatureCall(CGThing):
             cgThings.append(
                 CGIfWrapper(CGList(xraySteps),
                             "objIsXray"))
 
         cgThings.append(CGCallGenerator(
             self.getErrorReport() if self.isFallible() else None,
             self.getArguments(), argsPre, returnType,
             self.extendedAttributes, descriptor, nativeMethodName,
-            static, argsPost=argsPost))
+            static, argsPost=argsPost, resultVar=resultVar))
         self.cgRoot = CGList(cgThings)
 
     def getArguments(self):
         return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
 
     def isFallible(self):
         return 'infallible' not in self.extendedAttributes
 
@@ -6989,17 +7005,18 @@ class FakeArgument():
 
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
     setter.
     """
     def __init__(self, argType, nativeMethodName, descriptor, attr):
-        CGPerSignatureCall.__init__(self, None, [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
+        CGPerSignatureCall.__init__(self, None,
+                                    [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
                                     nativeMethodName, attr.isStatic(),
                                     descriptor, attr, setter=True)
 
     def wrap_return_value(self):
         attr = self.idlNode
         if self.descriptor.wrapperCache and attr.slotIndex is not None:
             if attr.getExtendedAttribute("StoreInSlot"):
                 args = "cx, self"
@@ -9390,32 +9407,42 @@ class CGClassForwardDeclare(CGThing):
 
 class CGProxySpecialOperation(CGPerSignatureCall):
     """
     Base class for classes for calling an indexed or named special operation
     (don't use this directly, use the derived classes below).
 
     If checkFound is False, will just assert that the prop is found instead of
     checking that it is before wrapping the value.
-    """
-    def __init__(self, descriptor, operation, checkFound=True, argumentMutableValue=None):
+
+    resultVar: See the docstring for CGCallGenerator.
+
+    foundVar: For getters and deleters, the generated code can also set a bool
+    variable, declared by the caller, to indicate whether the given indexed or
+    named property already existed or not. If the caller wants this, it should
+    pass the name of the bool variable as the foundVar keyword argument to the
+    constructor. The caller is responsible for declaring the variable.
+    """
+    def __init__(self, descriptor, operation, checkFound=True,
+                 argumentMutableValue=None, resultVar=None, foundVar=None):
         self.checkFound = checkFound
+        self.foundVar = foundVar or "found"
 
         nativeName = MakeNativeName(descriptor.binaryNameFor(operation))
         operation = descriptor.operations[operation]
         assert len(operation.signatures()) == 1
         signature = operation.signatures()[0]
 
         returnType, arguments = signature
 
         # We pass len(arguments) as the final argument so that the
         # CGPerSignatureCall won't do any argument conversion of its own.
         CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
                                     False, descriptor, operation,
-                                    len(arguments))
+                                    len(arguments), resultVar=resultVar)
 
         if operation.isSetter() or operation.isCreator():
             # arguments[0] is the index or name of the item that we're setting.
             argument = arguments[1]
             info = getJSToNativeConversionInfo(
                 argument.type, descriptor,
                 treatNullAs=argument.treatNullAs,
                 sourceDescription=("value being assigned to %s setter" %
@@ -9425,54 +9452,61 @@ class CGProxySpecialOperation(CGPerSigna
             templateValues = {
                 "declName": argument.identifier.name,
                 "holderName": argument.identifier.name + "_holder",
                 "val": argumentMutableValue,
                 "obj": "obj"
             }
             self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
         elif operation.isGetter() or operation.isDeleter():
-            self.cgRoot.prepend(CGGeneric("bool found;\n"))
+            if foundVar is None:
+                self.cgRoot.prepend(CGGeneric("bool found;\n"))
 
     def getArguments(self):
         args = [(a, a.identifier.name) for a in self.arguments]
         if self.idlNode.isGetter() or self.idlNode.isDeleter():
             args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
                                       self.idlNode),
-                         "found"))
+                         self.foundVar))
         return args
 
     def wrap_return_value(self):
         if not self.idlNode.isGetter() or self.templateValues is None:
             return ""
 
         wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
         if self.checkFound:
-            wrap = CGIfWrapper(wrap, "found")
-        else:
-            wrap = CGList([CGGeneric("MOZ_ASSERT(found);\n"), wrap])
+            wrap = CGIfWrapper(wrap, self.foundVar)
+        else:
+            wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
         return "\n" + wrap.define()
 
 
 class CGProxyIndexedOperation(CGProxySpecialOperation):
     """
     Class to generate a call to an indexed operation.
 
     If doUnwrap is False, the caller is responsible for making sure a variable
     named 'self' holds the C++ object somewhere where the code we generate
     will see it.
 
     If checkFound is False, will just assert that the prop is found instead of
     checking that it is before wrapping the value.
+
+    resultVar: See the docstring for CGCallGenerator.
+
+    foundVar: See the docstring for CGProxySpecialOperation.
     """
     def __init__(self, descriptor, name, doUnwrap=True, checkFound=True,
-                argumentMutableValue=None):
+                 argumentMutableValue=None, resultVar=None, foundVar=None):
         self.doUnwrap = doUnwrap
         CGProxySpecialOperation.__init__(self, descriptor, name, checkFound,
-                                         argumentMutableValue=argumentMutableValue)
+                                         argumentMutableValue=argumentMutableValue,
+                                         resultVar=resultVar,
+                                         foundVar=foundVar)
 
     def define(self):
         # Our first argument is the id we're getting.
         argName = self.arguments[0].identifier.name
         if argName == "index":
             # We already have our index in a variable with that name
             setIndex = ""
         else:
@@ -9491,62 +9525,79 @@ class CGProxyIndexedGetter(CGProxyIndexe
     the returned value will be wrapped with wrapForType using templateValues.
 
     If doUnwrap is False, the caller is responsible for making sure a variable
     named 'self' holds the C++ object somewhere where the code we generate
     will see it.
 
     If checkFound is False, will just assert that the prop is found instead of
     checking that it is before wrapping the value.
+
+    foundVar: See the docstring for CGProxySpecialOperation.
     """
     def __init__(self, descriptor, templateValues=None, doUnwrap=True,
-                 checkFound=True):
+                 checkFound=True, foundVar=None):
         self.templateValues = templateValues
         CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter',
-                                         doUnwrap, checkFound)
+                                         doUnwrap, checkFound, foundVar=foundVar)
 
 
 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
     """
     Class to generate a call that checks whether an indexed property exists.
 
     For now, we just delegate to CGProxyIndexedGetter
-    """
-    def __init__(self, descriptor):
-        CGProxyIndexedGetter.__init__(self, descriptor)
+
+    foundVar: See the docstring for CGProxySpecialOperation.
+    """
+    def __init__(self, descriptor, foundVar=None):
+        CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
         self.cgRoot.append(CGGeneric("(void)result;\n"))
 
 
 class CGProxyIndexedSetter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed setter.
     """
     def __init__(self, descriptor, argumentMutableValue=None):
         CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter',
                                          argumentMutableValue=argumentMutableValue)
 
 
 class CGProxyIndexedDeleter(CGProxyIndexedOperation):
     """
     Class to generate a call to an indexed deleter.
-    """
-    def __init__(self, descriptor):
-        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter')
+
+    resultVar: See the docstring for CGCallGenerator.
+
+    foundVar: See the docstring for CGProxySpecialOperation.
+    """
+    def __init__(self, descriptor, resultVar=None, foundVar=None):
+        CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter',
+                                         resultVar=resultVar,
+                                         foundVar=foundVar)
 
 
 class CGProxyNamedOperation(CGProxySpecialOperation):
     """
     Class to generate a call to a named operation.
 
     'value' is the jsval to use for the name; None indicates that it should be
     gotten from the property id.
-    """
-    def __init__(self, descriptor, name, value=None, argumentMutableValue=None):
+
+    resultVar: See the docstring for CGCallGenerator.
+
+    foundVar: See the docstring for CGProxySpecialOperation.
+    """
+    def __init__(self, descriptor, name, value=None, argumentMutableValue=None,
+                 resultVar=None, foundVar=None):
         CGProxySpecialOperation.__init__(self, descriptor, name,
-                                         argumentMutableValue=argumentMutableValue)
+                                         argumentMutableValue=argumentMutableValue,
+                                         resultVar=resultVar,
+                                         foundVar=foundVar)
         self.value = value
 
     def define(self):
         # Our first argument is the id we're getting.
         argName = self.arguments[0].identifier.name
         if argName == "id":
             # deal with the name collision
             idDecl = "JS::Rooted<jsid> id_(cx, id);\n"
@@ -9604,48 +9655,60 @@ class CGProxyNamedOperation(CGProxySpeci
 
 
 class CGProxyNamedGetter(CGProxyNamedOperation):
     """
     Class to generate a call to an named getter. If templateValues is not None
     the returned value will be wrapped with wrapForType using templateValues.
     'value' is the jsval to use for the name; None indicates that it should be
     gotten from the property id.
-    """
-    def __init__(self, descriptor, templateValues=None, value=None):
+
+    foundVar: See the docstring for CGProxySpecialOperation.
+    """
+    def __init__(self, descriptor, templateValues=None, value=None,
+                 foundVar=None):
         self.templateValues = templateValues
-        CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value)
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value,
+                                       foundVar=foundVar)
 
 
 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
     """
     Class to generate a call that checks whether a named property exists.
 
     For now, we just delegate to CGProxyNamedGetter
-    """
-    def __init__(self, descriptor):
-        CGProxyNamedGetter.__init__(self, descriptor)
+
+    foundVar: See the docstring for CGProxySpecialOperation.
+    """
+    def __init__(self, descriptor, foundVar=None):
+        CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
         self.cgRoot.append(CGGeneric("(void)result;\n"))
 
 
 class CGProxyNamedSetter(CGProxyNamedOperation):
     """
     Class to generate a call to a named setter.
     """
     def __init__(self, descriptor, argumentMutableValue=None):
         CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter',
                                        argumentMutableValue=argumentMutableValue)
 
 
 class CGProxyNamedDeleter(CGProxyNamedOperation):
     """
     Class to generate a call to a named deleter.
-    """
-    def __init__(self, descriptor):
-        CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter')
+
+    resultVar: See the docstring for CGCallGenerator.
+
+    foundVar: See the docstring for CGProxySpecialOperation.
+    """
+    def __init__(self, descriptor, resultVar=None, foundVar=None):
+        CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter',
+                                       resultVar=resultVar,
+                                       foundVar=foundVar)
 
 
 class CGProxyIsProxy(CGAbstractMethod):
     def __init__(self, descriptor):
         args = [Argument('JSObject*', 'obj')]
         CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
 
     def declare(self):
@@ -9909,27 +9972,31 @@ class CGDOMJSProxyHandler_delete(ClassMe
                 else:
                     setBp = dedent("""
                         if (found) {
                           *bp = result;
                         } else {
                           *bp = true;
                         }
                         """)
-                body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() +
+                deleterClass = globals()["CGProxy%sDeleter" % type]
+                body = (deleterClass(self.descriptor).define() +
                         setBp)
-            elif eval("self.descriptor.supports%sProperties()" % type):
-                body = (eval("CGProxy%sPresenceChecker" % type)(self.descriptor).define() +
-                        dedent("""
-                            if (found) {
-                              *bp = false;
-                            } else {
-                              *bp = true;
-                            }
-                            """))
+            elif getattr(self.descriptor, "supports%sProperties" % type)():
+                presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
+                body = fill(
+                    """
+                    $*{presenceChecker}
+                    if (found) {
+                      *bp = false;
+                    } else {
+                      *bp = true;
+                    }
+                    """,
+                    presenceChecker=presenceCheckerClass(self.descriptor).define())
             else:
                 body = None
             return body
 
         delete = dedent("""
             MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
                       "Should not have a XrayWrapper here");
 
@@ -10097,19 +10164,23 @@ class CGDOMJSProxyHandler_hasOwn(ClassMe
                 """)
             unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable)
         else:
             unforgeable = ""
 
         if self.descriptor.supportsNamedProperties():
             # If we support indexed properties we always return above for index
             # property names, so no need to check for those here.
-            named = (CGProxyNamedPresenceChecker(self.descriptor).define() +
-                     "\n" +
-                     "*bp = found;\n")
+            named = fill(
+                """
+                $*{presenceChecker}
+
+                *bp = found;
+                """,
+                presenceChecker=CGProxyNamedPresenceChecker(self.descriptor).define())
             if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
                 named = CGIfWrapper(CGGeneric(named + "return true;\n"),
                                     "!HasPropertyOnPrototype(cx, proxy, id)").define()
                 named += "*bp = false;\n"
             else:
                 named += "\n"
         else:
             named = "*bp = false;\n"