Bug 868715 part 1. Change instantiateJSToNativeConversionTemplate to return an object, to make its return value more extensible. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 16 May 2013 12:36:54 -0400
changeset 143615 603420c520cf1a24cd4dde3d76e8588f46967c53
parent 143614 55620ca9673c267ca3b97b39df9155e8bbe733ac
child 143616 f57bfa1109004bedb3e9424cb923aff7776724a1
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs868715
milestone24.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 868715 part 1. Change instantiateJSToNativeConversionTemplate to return an object, to make its return value more extensible. r=peterv
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2386,35 +2386,86 @@ if (NS_FAILED(rv) || !wrappedJS) {
 // Use a temp nsCOMPtr for the null-check, because ${target} might be
 // OwningNonNull, not an nsCOMPtr.
 nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
 if (!tmp) {
 ${codeOnFailure}
 }
 ${target} = tmp.forget();""").substitute(self.substitution)
 
+class JSToNativeConversionInfo():
+    """
+    An object representing information about a JS-to-native conversion.
+    """
+    def __init__(self, template, declType=None, holderType=None,
+                 dealWithOptional=False):
+        """
+        template: A string representing the conversion code.  This will have
+                  template substitution performed on it as follows:
+
+          ${val} replaced by an expression for the JS::Value in question
+          ${valPtr} is a pointer to the JS::Value in question
+          ${valHandle} is a handle to the JS::Value in question
+          ${holderName} replaced by the holder's name, if any
+          ${declName} replaced by the declaration's name
+          ${haveValue} replaced by an expression that evaluates to a boolean
+                       for whether we have a JS::Value.  Only used when
+                       defaultValue is not None.
+
+        declType: A CGThing representing the native C++ type we're converting
+                  to.  This is allowed to be None if the conversion code is
+                  supposed to be used as-is.
+
+        holderType: A CGThing representing the type of a "holder" which will
+                    hold a possible reference to the C++ thing whose type we
+                    returned in #1, or  None if no such holder is needed.
+
+        dealWithOptional: A boolean indicating whether the caller has to do
+                          optional-argument handling.  This should only be set
+                          to true if the JS-to-native conversion is being done
+                          for an optional argument or dictionary member with no
+                          default value and if the returned template expects
+                          both declType and holderType to be wrapped in
+                          Optional<>, with ${declName} and ${holderName}
+                          adjusted to point to the Value() of the Optional, and
+                          Construct() calls to be made on the Optional<>s as
+                          needed.
+
+        ${declName} must be in scope before the code from 'template' is entered.
+
+        If holderType is not None then ${holderName} must be in scope before
+        the code from 'template' is entered.
+        """
+        assert isinstance(template, str)
+        assert declType is None or isinstance(declType, CGThing)
+        assert holderType is None or isinstance(holderType, CGThing)
+        self.template = template
+        self.declType = declType
+        self.holderType = holderType
+        self.dealWithOptional = dealWithOptional
+
 # If this function is modified, modify CGNativeMember.getArg and
 # CGNativeMember.getRetvalInfo accordingly.  The latter cares about the decltype
 # and holdertype we end up using, because it needs to be able to return the code
 # that will convert those to the actual return value of the callback function.
-def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
-                                    isDefinitelyObject=False,
-                                    isMember=False,
-                                    isOptional=False,
-                                    invalidEnumValueFatal=True,
-                                    defaultValue=None,
-                                    treatNullAs="Default",
-                                    treatUndefinedAs="Default",
-                                    isEnforceRange=False,
-                                    isClamp=False,
-                                    isNullOrUndefined=False,
-                                    exceptionCode=None,
-                                    lenientFloatCode=None,
-                                    allowTreatNonCallableAsNull=False,
-                                    isCallbackReturnValue=False):
+def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
+                                isDefinitelyObject=False,
+                                isMember=False,
+                                isOptional=False,
+                                invalidEnumValueFatal=True,
+                                defaultValue=None,
+                                treatNullAs="Default",
+                                treatUndefinedAs="Default",
+                                isEnforceRange=False,
+                                isClamp=False,
+                                isNullOrUndefined=False,
+                                exceptionCode=None,
+                                lenientFloatCode=None,
+                                allowTreatNonCallableAsNull=False,
+                                isCallbackReturnValue=False):
     """
     Get a template for converting a JS value to a native object based on the
     given type and descriptor.  If failureCode is given, then we're actually
     testing whether we can convert the argument to the desired type.  That
     means that failures to convert due to the JS value being the wrong type of
     value need to use failureCode instead of throwing exceptions.  Failures to
     convert that are due to JS exceptions (from toString or valueOf methods) or
     out of memory conditions need to throw exceptions no matter what
@@ -2423,19 +2474,20 @@ def getJSToNativeConversionTemplate(type
     exceptionCode must end up doing a return, and every return from this
     function must happen via exceptionCode if exceptionCode is not None.
 
     If isDefinitelyObject is True, that means we know the value
     isObject() and we have no need to recheck that.
 
     if isMember is not False, we're being converted from a property of some JS
     object, not from an actual method argument, so we can't rely on our jsval
-    being rooted or outliving us in any way.  Callers can pass "Dictionary" or
-    "Variadic" to indicate that the conversion is for something that is a
-    dictionary member or a variadic argument respectively.
+    being rooted or outliving us in any way.  Callers can pass "Dictionary",
+    "Variadic", or "Sequence" to indicate that the conversion is for something
+    that is a dictionary member, a variadic argument, or a sequence
+    respectively.
 
     If isOptional is true, then we are doing conversion of an optional
     argument with no default value.
 
     invalidEnumValueFatal controls whether an invalid enum value conversion
     attempt will throw (if true) or simply return without doing anything (if
     false).
 
@@ -2452,46 +2504,17 @@ def getJSToNativeConversionTemplate(type
 
     If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull]
     extended attributes on nullable callback functions will be honored.
 
     If isCallbackReturnValue is true, then the declType may be adjusted to make
     it easier to return from a callback.  Since that type is never directly
     observable by any consumers of the callback code, this is OK.
 
-    The return value from this function is a tuple consisting of four things:
-
-    1)  A string representing the conversion code.  This will have template
-        substitution performed on it as follows:
-
-          ${val} replaced by an expression for the JS::Value in question
-          ${valPtr} is a pointer to the JS::Value in question
-          ${valHandle} is a handle to the JS::Value in question
-          ${holderName} replaced by the holder's name, if any
-          ${declName} replaced by the declaration's name
-          ${haveValue} replaced by an expression that evaluates to a boolean
-                       for whether we have a JS::Value.  Only used when
-                       defaultValue is not None.
-
-    2)  A CGThing representing the native C++ type we're converting to
-        (declType).  This is allowed to be None if the conversion code is
-        supposed to be used as-is.
-    3)  A CGThing representing the type of a "holder" (holderType) which will
-        hold a possible reference to the C++ thing whose type we returned in #1,
-        or None if no such holder is needed.
-    4)  A boolean indicating whether the caller has to do optional-argument handling.
-        This will only be true if isOptional is true and if the returned template
-        expects both declType and holderType to be wrapped in Optional<>, with
-        ${declName} and ${holderName} adjusted to point to the Value() of the
-        Optional, and Construct() calls to be made on the Optional<>s as needed.
-
-    ${declName} must be in scope before the generated code is entered.
-
-    If holderType is not None then ${holderName} must be in scope
-    before the generated code is entered.
+    The return value from this function is a JSToNativeConversionInfo.
     """
     # If we have a defaultValue then we're not actually optional for
     # purposes of what we need to be declared as.
     assert(defaultValue is None or not isOptional)
 
     # Also, we should not have a defaultValue if we know we're an object
     assert(not isDefinitelyObject or defaultValue is None)
 
@@ -2599,17 +2622,18 @@ def getJSToNativeConversionTemplate(type
             # If cx is null then LazyRootedObject will not be
             # constructed and behave as nullptr.
             templateBody = CGIfWrapper(CGGeneric(templateBody), "cx").define()
             setToNullCode = CGIfWrapper(CGGeneric(setToNullCode), "cx").define()
 
         template = wrapObjectTemplate(templateBody, type, setToNullCode,
                                       failureCode)
 
-        return (template, declType, None, isOptional)
+        return JSToNativeConversionInfo(template, declType=declType,
+                                        dealWithOptional=isOptional)
 
 
     assert not (isEnforceRange and isClamp) # These are mutually exclusive
 
     if type.isArray():
         raise TypeError("Can't handle array arguments yet")
 
     if type.isSequence():
@@ -2643,27 +2667,26 @@ def getJSToNativeConversionTemplate(type
         # nullable and optional cases because we don't want to leak the
         # AutoSequence type to consumers, which would be unavoidable with
         # Nullable<AutoSequence> or Optional<AutoSequence>.
         if isMember or isOptional or nullable or isCallbackReturnValue:
             sequenceClass = "Sequence"
         else:
             sequenceClass = "AutoSequence"
 
-        (elementTemplate, elementDeclType,
-         elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
-            elementType, descriptorProvider, isMember=True,
+        elementInfo = getJSToNativeConversionInfo(
+            elementType, descriptorProvider, isMember="Sequence",
             exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
             isCallbackReturnValue=isCallbackReturnValue)
-        if dealWithOptional:
+        if elementInfo.dealWithOptional:
             raise TypeError("Shouldn't have optional things in sequences")
-        if elementHolderType is not None:
+        if elementInfo.holderType is not None:
             raise TypeError("Shouldn't need holders for sequences")
 
-        typeName = CGTemplatedType(sequenceClass, elementDeclType)
+        typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
         sequenceType = typeName.define()
         if nullable:
             typeName = CGTemplatedType("Nullable", typeName)
             arrayRef = "${declName}.SetValue()"
         else:
             arrayRef = "${declName}"
 
         # NOTE: Keep this in sync with variadic conversions as needed
@@ -2688,36 +2711,37 @@ for (uint32_t i = 0; i < length; ++i) {
   }
   %s& slot = *arr.AppendElement();
 """ % (CGIndenter(CGGeneric(notSequence)).define(),
        exceptionCodeIndented.define(),
        sequenceType,
        arrayRef,
        exceptionCodeIndented.define(),
        CGIndenter(exceptionCodeIndented).define(),
-       elementDeclType.define()))
+       elementInfo.declType.define()))
 
         templateBody += CGIndenter(CGGeneric(
-                string.Template(elementTemplate).substitute(
+                string.Template(elementInfo.template).substitute(
                     {
                         "val" : "temp",
                         "valPtr": "temp.address()",
                         "valHandle": "temp",
                         "declName" : "slot",
                         # We only need holderName here to handle isExternal()
                         # interfaces, which use an internal holder for the
                         # conversion even when forceOwningType ends up true.
                         "holderName": "tempHolder",
                         }
                     ))).define()
 
         templateBody += "\n}"
         templateBody = wrapObjectTemplate(templateBody, type,
                                           "${declName}.SetNull()")
-        return (templateBody, typeName, None, isOptional)
+        return JSToNativeConversionInfo(templateBody, declType=typeName,
+                                        dealWithOptional=isOptional)
 
     if type.isUnion():
         if isMember:
             raise TypeError("Can't handle unions as members, we have a "
                             "holderType")
         nullable = type.nullable();
         if nullable:
             type = type.inner
@@ -2900,17 +2924,19 @@ for (uint32_t i = 0; i < length; ++i) {
                 assert(isinstance(defaultValue, IDLNullValue))
                 valueMissing = "!(${haveValue}) || "
             else:
                 valueMissing = ""
             templateBody = handleNull(templateBody, mutableDecl,
                                       extraConditionForNull=valueMissing)
         templateBody = CGList([constructDecl, templateBody], "\n")
 
-        return templateBody.define(), declType, holderType, False
+        return JSToNativeConversionInfo(templateBody.define(),
+                                        declType=declType,
+                                        holderType=holderType)
 
     if type.isGeckoInterface():
         assert not isEnforceRange and not isClamp
 
         descriptor = descriptorProvider.getDescriptor(
             type.unroll().inner.identifier.name)
 
         if (descriptor.interface.isCallback() and
@@ -2924,17 +2950,18 @@ for (uint32_t i = 0; i < length; ++i) {
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
             conversion = (
                 "  ${declName} = new %s(&${val}.toObject());\n" % name)
 
             template = wrapObjectTemplate(conversion, type,
                                           "${declName} = nullptr",
                                           failureCode)
-            return (template, declType, None, isOptional)
+            return JSToNativeConversionInfo(template, declType=declType,
+                                            dealWithOptional=isOptional)
 
         # This is an interface that we implement as a concrete class
         # or an XPCOM interface.
 
         # Allow null pointers for nullable types and old-binding classes, and
         # use an nsRefPtr or raw pointer for callback return values to make
         # them easier to return.
         argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
@@ -3032,17 +3059,20 @@ for (uint32_t i = 0; i < length; ++i) {
 
         templateBody = wrapObjectTemplate(templateBody, type,
                                           "${declName} = nullptr",
                                           failureCode)
 
         declType = CGGeneric(declType)
         if holderType is not None:
             holderType = CGGeneric(holderType)
-        return (templateBody, declType, holderType, isOptional)
+        return JSToNativeConversionInfo(templateBody,
+                                        declType=declType,
+                                        holderType=holderType,
+                                        dealWithOptional=isOptional)
 
     if type.isSpiderMonkeyInterface():
         assert not isEnforceRange and not isClamp
         if isMember:
             raise TypeError("Can't handle member arraybuffers or "
                             "arraybuffer views because making sure all the "
                             "objects are properly rooted is hard")
         name = type.name
@@ -3087,17 +3117,19 @@ for (uint32_t i = 0; i < length; ++i) {
             template += "${declName} = ${holderName}.addr();"
         template = wrapObjectTemplate(template, type,
                                       "%s = nullptr" % nullableTarget,
                                       failureCode)
 
         if holderType is not None:
             holderType = CGGeneric(holderType)
         # We handle all the optional stuff ourselves; no need for caller to do it.
-        return (template, CGGeneric(declType), holderType, False)
+        return JSToNativeConversionInfo(template,
+                                        declType=CGGeneric(declType),
+                                        holderType=holderType)
 
     if type.isString():
         assert not isEnforceRange and not isClamp
 
         treatAs = {
             "Default": "eStringify",
             "EmptyString": "eEmpty",
             "Null": "eNull"
@@ -3133,36 +3165,36 @@ for (uint32_t i = 0; i < length; ++i) {
                  "%s.SetData(data, ArrayLength(data) - 1)" %
                  (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
                   varName)))
 
         if isMember:
             # We have to make a copy, because our jsval may well not
             # live as long as our string needs to.
             declType = CGGeneric("nsString")
-            return (
+            return JSToNativeConversionInfo(
                 "{\n"
                 "  FakeDependentString str;\n"
                 "%s\n"
                 "  ${declName} = str;\n"
                 "}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(),
-                declType, None, isOptional)
+                declType=declType, dealWithOptional=isOptional)
 
         if isOptional:
             declType = "Optional<nsAString>"
         else:
             declType = "NonNull<nsAString>"
 
-        return (
+        # No need to deal with optional here; we handled it already
+        return JSToNativeConversionInfo(
             "%s\n"
             "${declName} = &${holderName};" %
             getConversionCode("${holderName}"),
-            CGGeneric(declType), CGGeneric("FakeDependentString"),
-            # No need to deal with Optional here; we have handled it already
-            False)
+            declType=CGGeneric(declType),
+            holderType=CGGeneric("FakeDependentString"))
 
     if type.isEnum():
         assert not isEnforceRange and not isClamp
 
         enumName = type.unroll().inner.identifier.name
         declType = CGGeneric(enumName)
         if type.nullable():
             declType = CGTemplatedType("Nullable", declType)
@@ -3214,17 +3246,18 @@ for (uint32_t i = 0; i < length; ++i) {
                 assert type.nullable()
                 template = handleDefault(template, setNull)
             else:
                 assert(defaultValue.type.tag() == IDLType.Tags.domstring)
                 template = handleDefault(template,
                                          ("%s = %s::%s" %
                                           (enumLoc, enumName,
                                            getEnumValueName(defaultValue.value))))
-        return (template, CGGeneric(declType), None, isOptional)
+        return JSToNativeConversionInfo(template, declType=CGGeneric(declType),
+                                        dealWithOptional=isOptional)
 
     if type.isCallback():
         assert not isEnforceRange and not isClamp
         assert not type.treatNonCallableAsNull() or type.nullable()
 
         if descriptorProvider.workers:
             if isMember:
                 raise NoSuchDescriptorError("Can't handle member callbacks in "
@@ -3262,17 +3295,18 @@ for (uint32_t i = 0; i < length; ++i) {
                 "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" +
                 conversion +
                 "} else {\n"
                 "%s"
                 "}" % CGIndenter(onFailureNotCallable(failureCode)).define(),
                 type,
                 "${declName} = nullptr",
                 failureCode)
-        return (template, declType, None, isOptional)
+        return JSToNativeConversionInfo(template, declType=declType,
+                                        dealWithOptional=isOptional)
 
     if type.isAny():
         assert not isEnforceRange and not isClamp
 
         if isMember == "Dictionary" or not isMember:
             declType = "LazyRootedValue"
             templateBody = "${declName}.construct(cx, ${val});"
             nullHandling = "${declName}.construct(cx, JS::NullValue());"
@@ -3281,17 +3315,19 @@ for (uint32_t i = 0; i < length; ++i) {
                             "out rooting issues")
         else:
             # Variadic arguments are rooted by being in argv
             declType = "JS::Value"
             templateBody = "${declName} = ${val};"
             nullHandling = "${declName} = JS::NullValue()"
 
         templateBody = handleDefaultNull(templateBody, nullHandling)
-        return (templateBody, CGGeneric(declType), None, isOptional)
+        return JSToNativeConversionInfo(templateBody,
+                                        declType=CGGeneric(declType),
+                                        dealWithOptional=isOptional)
 
     if type.isObject():
         assert not isEnforceRange and not isClamp
         return handleJSObjectType(type, isMember, failureCode)
 
     if type.isDictionary():
         if failureCode is not None and not isDefinitelyObject:
             raise TypeError("Can't handle dictionaries when failureCode is "
@@ -3330,23 +3366,23 @@ for (uint32_t i = 0; i < length; ++i) {
                 "!IsObjectValueConvertibleToDictionary(cx, ${valHandle})").define() + "\n\n"
         else:
             template = ""
 
         template += ("if (!${declName}.Init(cx, %s)) {\n"
                      "%s\n"
                      "}" % (val, exceptionCodeIndented.define()))
 
-        return (template, declType, None, False)
+        return JSToNativeConversionInfo(template, declType=declType)
 
     if type.isVoid():
         assert not isOptional
         # This one only happens for return values, and its easy: Just
         # ignore the jsval.
-        return ("", None, None, False)
+        return JSToNativeConversionInfo("")
 
     if type.isDate():
         assert not isEnforceRange and not isClamp
 
         declType = CGGeneric("Date")
         if type.nullable():
             declType = CGTemplatedType("Nullable", declType)
             dateVal = "${declName}.SetValue()"
@@ -3363,17 +3399,19 @@ for (uint32_t i = 0; i < length; ++i) {
             "if (!JS_ObjectIsDate(cx, &${val}.toObject()) ||\n"
             "    !%s.SetTimeStamp(cx, &${val}.toObject())) {\n"
             "%s\n"
             "}" %
             (dateVal, CGIndenter(CGGeneric(notDate)).define()))
 
         conversion = wrapObjectTemplate(conversion, type,
                                         "${declName}.SetNull()")
-        return (conversion, declType, None, isOptional)
+        return JSToNativeConversionInfo(conversion,
+                                        declType=declType,
+                                        dealWithOptional=isOptional)
 
     if not type.isPrimitive():
         raise TypeError("Need conversion for argument type '%s'" % str(type))
 
     typeName = builtinNames[type.tag()]
 
     conversionBehavior = "eDefault"
     if isEnforceRange:
@@ -3433,30 +3471,31 @@ for (uint32_t i = 0; i < length; ++i) {
             defaultStr = toStringBool(defaultValue.value)
         template = CGWrapper(CGIndenter(CGGeneric(template)),
                              pre="if (${haveValue}) {\n",
                              post=("\n"
                                    "} else {\n"
                                    "  %s = %s;\n"
                                    "}" % (writeLoc, defaultStr))).define()
 
-    return (template, declType, None, isOptional)
-
-def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
-                                            argcAndIndex=None):
-    """
-    Take a tuple as returned by getJSToNativeConversionTemplate and a set of
-    replacements as required by the strings in such a tuple, and generate code
-    to convert into stack C++ types.
+    return JSToNativeConversionInfo(template, declType=declType,
+                                    dealWithOptional=isOptional)
+
+def instantiateJSToNativeConversion(info, replacements, argcAndIndex=None):
+    """
+    Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
+    and a set of replacements as required by the strings in such an object, and
+    generate code to convert into stack C++ types.
 
     If argcAndIndex is not None it must be a dict that can be used to
     replace ${argc} and ${index}, where ${index} is the index of this
     argument (0-based) and ${argc} is the total number of arguments.
     """
-    (templateBody, declType, holderType, dealWithOptional) = templateTuple
+    (templateBody, declType, holderType, dealWithOptional) = (
+        info.template, info.declType, info.holderType, info.dealWithOptional)
 
     if dealWithOptional and argcAndIndex is None:
         raise TypeError("Have to deal with optional things, but don't know how")
     if argcAndIndex is not None and declType is None:
         raise TypeError("Need to predeclare optional things, so they will be "
                         "outside the check for big enough arg count!");
 
     result = CGList([], "\n")
@@ -3572,63 +3611,62 @@ class CGArgumentConverter(CGThing):
             self.argcAndIndex = replacer
         else:
             self.argcAndIndex = None
         self.invalidEnumValueFatal = invalidEnumValueFatal
         self.lenientFloatCode = lenientFloatCode
         self.allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
 
     def define(self):
-        typeConversion = getJSToNativeConversionTemplate(
+        typeConversion = getJSToNativeConversionInfo(
             self.argument.type,
             self.descriptorProvider,
             isOptional=(self.argcAndIndex is not None and
                         not self.argument.variadic),
             invalidEnumValueFatal=self.invalidEnumValueFatal,
             defaultValue=self.argument.defaultValue,
             treatNullAs=self.argument.treatNullAs,
             treatUndefinedAs=self.argument.treatUndefinedAs,
             isEnforceRange=self.argument.enforceRange,
             isClamp=self.argument.clamp,
             lenientFloatCode=self.lenientFloatCode,
             isMember="Variadic" if self.argument.variadic else False,
             allowTreatNonCallableAsNull=self.allowTreatNonCallableAsNull)
 
         if not self.argument.variadic:
-            return instantiateJSToNativeConversionTemplate(
+            return instantiateJSToNativeConversion(
                 typeConversion,
                 self.replacementVariables,
                 self.argcAndIndex).define()
 
         # Variadic arguments get turned into a sequence.
-        (elementTemplate, elementDeclType,
-         elementHolderType, dealWithOptional) = typeConversion
-        if dealWithOptional:
+        if typeConversion.dealWithOptional:
             raise TypeError("Shouldn't have optional things in variadics")
-        if elementHolderType is not None:
+        if typeConversion.holderType is not None:
             raise TypeError("Shouldn't need holders for variadics")
 
         replacer = dict(self.argcAndIndex, **self.replacementVariables)
-        replacer["seqType"] = CGTemplatedType("AutoSequence", elementDeclType).define()
-        replacer["elemType"] = elementDeclType.define()
+        replacer["seqType"] = CGTemplatedType("AutoSequence",
+                                              typeConversion.declType).define()
+        replacer["elemType"] = typeConversion.declType.define()
 
         # NOTE: Keep this in sync with sequence conversions as needed
         variadicConversion = string.Template("""${seqType} ${declName};
 if (${argc} > ${index}) {
   if (!${declName}.SetCapacity(${argc} - ${index})) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
   for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
     ${elemType}& slot = *${declName}.AppendElement();
 """).substitute(replacer)
 
         val = string.Template("${argv}[variadicArg]").substitute(replacer)
         variadicConversion += CGIndenter(CGGeneric(
-                string.Template(elementTemplate).substitute(
+                string.Template(typeConversion.template).substitute(
                     {
                         "val" : val,
                         "valPtr": "&" + val,
                         "valHandle" : ("JS::Handle<JS::Value>::fromMarkedLocation(&%s)" %
                                        val),
                         "declName" : "slot",
                         # We only need holderName here to handle isExternal()
                         # interfaces, which use an internal holder for the
@@ -4052,17 +4090,17 @@ def getRetvalDeclarationForType(returnTy
         nullable = returnType.nullable()
         if nullable:
             returnType = returnType.inner
         # If our result is already addrefed, use the right type in the
         # sequence argument here.
         (result, _) = getRetvalDeclarationForType(returnType.inner,
                                                   descriptorProvider,
                                                   resultAlreadyAddRefed,
-                                                  isMember=True)
+                                                  isMember="Sequence")
         result = CGTemplatedType("nsTArray", result)
         if nullable:
             result = CGTemplatedType("Nullable", result)
         return result, True
     if returnType.isDictionary():
         nullable = returnType.nullable()
         result = CGGeneric(
             CGDictionary.makeDictionaryName(returnType.unroll().inner,
@@ -4626,21 +4664,21 @@ class CGMethodCall(CGThing):
                 if isDefinitelyObject:
                     failureCode = "break;"
                 else:
                     failureCode = None
                 type = distinguishingType(signature)
                 # The argument at index distinguishingIndex can't possibly
                 # be unset here, because we've already checked that argc is
                 # large enough that we can examine this argument.
-                testCode = instantiateJSToNativeConversionTemplate(
-                    getJSToNativeConversionTemplate(type, descriptor,
-                                                    failureCode=failureCode,
-                                                    isDefinitelyObject=isDefinitelyObject,
-                                                    isNullOrUndefined=isNullOrUndefined),
+                testCode = instantiateJSToNativeConversion(
+                    getJSToNativeConversionInfo(type, descriptor,
+                                                failureCode=failureCode,
+                                                isDefinitelyObject=isDefinitelyObject,
+                                                isNullOrUndefined=isNullOrUndefined),
                     {
                         "declName" : "arg%d" % distinguishingIndex,
                         "holderName" : ("arg%d" % distinguishingIndex) + "_holder",
                         "val" : distinguishingArg,
                         "valHandle" : ("JS::Handle<JS::Value>::fromMarkedLocation(&%s)" %
                                        distinguishingArg),
                         "obj" : "obj"
                         })
@@ -5441,20 +5479,20 @@ def getUnionAccessorSignatureType(type, 
         raise TypeError("Can't handle array arguments yet")
 
     if type.isSequence():
         nullable = type.nullable();
         if nullable:
             type = type.inner.inner
         else:
             type = type.inner
-        (elementTemplate, elementDeclType,
-         elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
-            type, descriptorProvider, isSequenceMember=True)
-        typeName = CGTemplatedType("Sequence", elementDeclType, isReference=True)
+        elementInfo = getJSToNativeConversionInfo(type, descriptorProvider,
+                                                  isMember="Sequence")
+        typeName = CGTemplatedType("Sequence", elementInfo.declType,
+                                   isReference=True)
         if nullable:
             typeName = CGTemplatedType("Nullable", typeName, isReference=True)
 
         return typeName
 
     if type.isUnion():
         typeName = CGGeneric(type.name)
         if type.nullable():
@@ -5517,17 +5555,17 @@ def getUnionAccessorSignatureType(type, 
 
     typeName = CGGeneric(builtinNames[type.tag()])
     if type.nullable():
         typeName = CGTemplatedType("Nullable", typeName, isReference=True)
     return typeName
 
 def getUnionTypeTemplateVars(type, descriptorProvider):
     # For dictionaries and sequences we need to pass None as the failureCode
-    # for getJSToNativeConversionTemplate.
+    # for getJSToNativeConversionInfo.
     # Also, for dictionaries we would need to handle conversion of
     # null/undefined to the dictionary correctly.
     if type.isDictionary() or type.isSequence():
         raise TypeError("Can't handle dictionaries or sequences in unions")
 
     if type.isGeckoInterface():
         name = type.inner.identifier.name
     elif type.isEnum():
@@ -5538,36 +5576,35 @@ def getUnionTypeTemplateVars(type, descr
         name = type.name
 
     tryNextCode = """tryNext = true;
 return true;"""
     if type.isGeckoInterface():
          tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) {
   mUnion.Destroy%s();
 }""" % name) + tryNextCode
-    (template, declType, holderType,
-     dealWithOptional) = getJSToNativeConversionTemplate(
+    conversionInfo = getJSToNativeConversionInfo(
         type, descriptorProvider, failureCode=tryNextCode,
         isDefinitelyObject=True)
 
     # This is ugly, but UnionMember needs to call a constructor with no
     # arguments so the type can't be const.
-    structType = declType.define()
+    structType = conversionInfo.declType.define()
     if structType.startswith("const "):
         structType = structType[6:]
     externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
 
     if type.isObject():
         setter = CGGeneric("void SetToObject(JSContext* cx, JSObject* obj)\n"
                            "{\n"
                            "  mUnion.mValue.mObject.SetValue().construct(cx, obj);\n"
                            "  mUnion.mType = mUnion.eObject;\n"
                            "}")
     else:
-        jsConversion = string.Template(template).substitute(
+        jsConversion = string.Template(conversionInfo.template).substitute(
             {
                 "val": "value",
                 "valHandle": "value",
                 "valPtr": "pvalue",
                 "declName": "SetAs" + name + "()",
                 "holderName": "m" + name + "Holder",
                 }
             )
@@ -5581,17 +5618,17 @@ return true;"""
                            post="\n"
                                 "}")
 
     return {
                 "name": name,
                 "structType": structType,
                 "externalType": externalType,
                 "setter": CGIndenter(setter).define(),
-                "holderType": holderType.define() if holderType else None
+                "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None
                 }
 
 def mapTemplate(template, templateVarArray):
     return map(lambda v: string.Template(template).substitute(v),
                templateVarArray)
 
 class CGUnionStruct(CGThing):
     def __init__(self, type, descriptorProvider):
@@ -6371,28 +6408,28 @@ class CGProxySpecialOperation(CGPerSigna
         # CGPerSignatureCall won't do any argument conversion of its own.
         CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
                                     False, descriptor, operation,
                                     len(arguments))
 
         if operation.isSetter() or operation.isCreator():
             # arguments[0] is the index or name of the item that we're setting.
             argument = arguments[1]
-            template = getJSToNativeConversionTemplate(argument.type, descriptor,
-                                                       treatNullAs=argument.treatNullAs,
-                                                       treatUndefinedAs=argument.treatUndefinedAs)
+            info = getJSToNativeConversionInfo(argument.type, descriptor,
+                                               treatNullAs=argument.treatNullAs,
+                                               treatUndefinedAs=argument.treatUndefinedAs)
             templateValues = {
                 "declName": argument.identifier.name,
                 "holderName": argument.identifier.name + "_holder",
                 "val": "desc->value",
                 "valPtr": "&desc->value",
                 "valHandle" : "JS::Handle<JS::Value>::fromMarkedLocation(&desc->value)",
                 "obj": "obj"
             }
-            self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
+            self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
         elif operation.isGetter() or operation.isDeleter():
             self.cgRoot.prepend(CGGeneric("bool found;"))
 
     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),
@@ -7348,21 +7385,21 @@ class CGNamespacedEnum(CGThing):
 class CGDictionary(CGThing):
     def __init__(self, dictionary, descriptorProvider):
         self.dictionary = dictionary
         self.descriptorProvider = descriptorProvider
         self.workers = descriptorProvider.workers
         self.needToInitIds = not self.workers and len(dictionary.members) > 0
         self.memberInfo = [
             (member,
-             getJSToNativeConversionTemplate(member.type,
-                                             descriptorProvider,
-                                             isMember="Dictionary",
-                                             isOptional=(not member.defaultValue),
-                                             defaultValue=member.defaultValue))
+             getJSToNativeConversionInfo(member.type,
+                                         descriptorProvider,
+                                         isMember="Dictionary",
+                                         isOptional=(not member.defaultValue),
+                                         defaultValue=member.defaultValue))
             for member in dictionary.members ]
 
     def declare(self):
         d = self.dictionary
         if d.parent:
             inheritance = ": public %s " % self.makeClassName(d.parent)
         elif not self.workers:
             inheritance = ": public MainThreadDictionaryBase "
@@ -7518,38 +7555,37 @@ class CGDictionary(CGThing):
     def makeClassName(self, dictionary):
         return self.makeDictionaryName(dictionary, self.workers)
 
     @staticmethod
     def makeMemberName(name):
         return "m" + name[0].upper() + name[1:]
 
     def getMemberType(self, memberInfo):
-        (member, (templateBody, declType,
-                  holderType, dealWithOptional)) = memberInfo
+        (_, conversionInfo) = memberInfo
         # We can't handle having a holderType here
-        assert holderType is None
-        if dealWithOptional:
+        assert conversionInfo.holderType is None
+        declType = conversionInfo.declType
+        if conversionInfo.dealWithOptional:
             declType = CGTemplatedType("Optional", declType)
         return declType.define()
 
     def getMemberConversion(self, memberInfo):
-        (member, (templateBody, declType,
-                  holderType, dealWithOptional)) = memberInfo
+        (member, conversionInfo) = memberInfo
         replacements = { "val": "temp",
                          "valPtr": "temp.address()",
                          "valHandle": "temp",
                          "declName": self.makeMemberName(member.identifier.name),
                          # We need a holder name for external interfaces, but
                          # it's scoped down to the conversion so we can just use
                          # anything we want.
                          "holderName": "holder" }
         # We can't handle having a holderType here
-        assert holderType is None
-        if dealWithOptional:
+        assert conversionInfo.holderType is None
+        if conversionInfo.dealWithOptional:
             replacements["declName"] = "(" + replacements["declName"] + ".Value())"
         if member.defaultValue:
             replacements["haveValue"] = "found"
 
         # NOTE: jsids are per-runtime, so don't use them in workers
         if self.workers:
             propName = member.identifier.name
             propCheck = ('JS_HasProperty(cx, &val.toObject(), "%s", &found)' %
@@ -7560,17 +7596,17 @@ class CGDictionary(CGThing):
             propId = self.makeIdName(member.identifier.name);
             propCheck = ("JS_HasPropertyById(cx, &val.toObject(), %s, &found)" %
                          propId)
             propGet = ("JS_GetPropertyById(cx, &val.toObject(), %s, temp.address())" %
                        propId)
 
         conversionReplacements = {
             "prop": self.makeMemberName(member.identifier.name),
-            "convert": string.Template(templateBody).substitute(replacements),
+            "convert": string.Template(conversionInfo.template).substitute(replacements),
             "propCheck": propCheck,
             "propGet": propGet
             }
         conversion = ("if (isNull) {\n"
                       "  found = false;\n"
                       "} else if (!${propCheck}) {\n"
                       "  return false;\n"
                       "}\n")
@@ -7595,17 +7631,17 @@ class CGDictionary(CGThing):
                 CGGeneric(conversionReplacements["convert"])).define()
         
         return CGGeneric(
             string.Template(conversion).substitute(conversionReplacements)
             )
 
     def getMemberDefinition(self, memberInfo):
         member = memberInfo[0]
-        declType = memberInfo[1][1]
+        declType = memberInfo[1].declType
         memberLoc = self.makeMemberName(member.identifier.name)
         if member.defaultValue:
             memberData = memberLoc
         else:
             # The data is inside the Optional<>
             memberData = "%s.Value()" % memberLoc
 
         if self.workers:
@@ -9140,21 +9176,21 @@ class CallbackMember(CGNativeMember):
             "holderName" : "rvalHolder",
             "declName" : "rvalDecl",
             # We actually want to pass in a null scope object here, because
             # wrapping things into our current compartment (that of mCallback)
             # is what we want.
             "obj": "nullptr"
             }
 
-        convertType = instantiateJSToNativeConversionTemplate(
-            getJSToNativeConversionTemplate(self.retvalType,
-                                            self.descriptor,
-                                            exceptionCode=self.exceptionCode,
-                                            isCallbackReturnValue=True),
+        convertType = instantiateJSToNativeConversion(
+            getJSToNativeConversionInfo(self.retvalType,
+                                        self.descriptor,
+                                        exceptionCode=self.exceptionCode,
+                                        isCallbackReturnValue=True),
             replacements)
         assignRetval = string.Template(
             self.getRetvalInfo(self.retvalType,
                                False)[2]).substitute(replacements)
         return convertType.define() + "\n" + assignRetval
 
     def getArgConversions(self):
         # Just reget the arglist from self.originalSig, because our superclasses