Bug 747287 - Part 2: Generate infalliblity data for further JIT optimization. (r=peterv)
authorEric Faust <efaust@mozilla.com>
Tue, 07 Aug 2012 22:26:18 -0700
changeset 104906 ddacf21b5431255706812135bf54824ad87575cb
parent 104905 87ddeea57fbddfeb6b3095e88caddeef698d4531
child 104907 7d9b9f1158a2367ba737ff0f9e3445f68e0c59a9
push idunknown
push userunknown
push dateunknown
reviewerspeterv
bugs747287
milestone17.0a1
Bug 747287 - Part 2: Generate infalliblity data for further JIT optimization. (r=peterv)
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2399,16 +2399,18 @@ class CGArgumentConverter(CGThing):
 
 def getWrapTemplateForType(type, descriptorProvider, result, successCode,
                            isCreator):
     """
     Reflect a C++ value stored in "result", of IDL type "type" into JS.  The
     "successCode" is the code to run once we have successfully done the
     conversion.  The resulting string should be used with string.Template, it
     needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
+
+    Returns (templateString, infallibility of conversion template)
     """
     haveSuccessCode = successCode is not None
     if not haveSuccessCode:
         successCode = "return true;"
 
     def setValue(value, callWrapValue=False):
         """
         Returns the code to set the jsval to value. If "callWrapValue" is true
@@ -2437,32 +2439,32 @@ def getWrapTemplateForType(type, descrip
             failureCode = "return false;"
         str = ("if (!%s) {\n" +
                CGIndenter(CGGeneric(failureCode)).define() + "\n" +
                "}\n" +
                successCode) % (wrapCall)
         return str
     
     if type is None or type.isVoid():
-        return setValue("JSVAL_VOID")
+        return (setValue("JSVAL_VOID"), True)
 
     if type.isArray():
         raise TypeError("Can't handle array return values yet")
 
     if type.isSequence():
         if type.nullable():
             # Nullable sequences are Nullable< nsTArray<T> >
-            return """
+            (recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider,
+                                                              "%s.Value()" % result, successCode,
+                                                              isCreator)
+            return ("""
 if (%s.IsNull()) {
 %s
 }
-%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(),
-         getWrapTemplateForType(type.inner, descriptorProvider,
-                                "%s.Value()" % result, successCode,
-                                isCreator))
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(), recTemplate), recInfall)
 
         # Now do non-nullable sequences.  We use setting the element
         # in the array as our succcess code because when we succeed in
         # wrapping that's what we should do.
         innerTemplate = wrapForType(
             type.inner, descriptorProvider,
             {
                 'result' :  "%s[i]" % result,
@@ -2471,26 +2473,26 @@ if (%s.IsNull()) {
                                 "  return false;\n"
                                 "}"),
                 'jsvalRef': "tmp",
                 'jsvalPtr': "&tmp",
                 'isCreator': isCreator
                 }
             )
         innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
-        return ("""
+        return (("""
 uint32_t length = %s.Length();
 JSObject *returnArray = JS_NewArrayObject(cx, length, NULL);
 if (!returnArray) {
   return false;
 }
 jsval tmp;
 for (uint32_t i = 0; i < length; ++i) {
 %s
-}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)")
+}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)"), False)
 
     if type.isGeckoInterface():
         descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
         if type.nullable():
             wrappingCode = ("if (!%s) {\n" % (result) +
                             CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
                             "}\n")
         else:
@@ -2522,87 +2524,91 @@ for (uint32_t i = 0; i < length; ++i) {
             wrappingCode += wrapAndSetPtr(wrap, failed)
         else:
             if descriptor.notflattened:
                 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
             else:
                 getIID = ""
             wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID)
             wrappingCode += wrapAndSetPtr(wrap)
-        return wrappingCode
+        return (wrappingCode, False)
 
     if type.isString():
         if type.nullable():
-            return wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalPtr})" % result)
+            return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalPtr})" % result), False)
         else:
-            return wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr})" % result)
+            return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr})" % result), False)
 
     if type.isEnum():
         if type.nullable():
             raise TypeError("We don't support nullable enumerated return types "
                             "yet")
-        return """MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
+        return ("""MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
 JSString* %(resultStr)s = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
 if (!%(resultStr)s) {
   return false;
 }
 """ % { "result" : result,
         "resultStr" : result + "_str",
-        "strings" : type.inner.identifier.name + "Values::strings" } + setValue("JS::StringValue(%s_str)" % result)
+        "strings" : type.inner.identifier.name + "Values::strings" } +
+        setValue("JS::StringValue(%s_str)" % result), False)
 
     if type.isCallback():
         assert not type.isInterface()
         # XXXbz we're going to assume that callback types are always
         # nullable and always have [TreatNonCallableAsNull] for now.
         # See comments in WrapNewBindingObject explaining why we need
         # to wrap here.
-        return setValue("JS::ObjectOrNullValue(%s)" % result, True)
+        # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+        return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False)
 
     if type.tag() == IDLType.Tags.any:
         # See comments in WrapNewBindingObject explaining why we need
         # to wrap here.
-        return setValue(result, True)
+        # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+        return (setValue(result, True), False)
 
     if type.isObject():
         # See comments in WrapNewBindingObject explaining why we need
         # to wrap here.
         if type.nullable():
             toValue = "JS::ObjectOrNullValue(%s)"
         else:
             toValue = "JS::ObjectValue(*%s)"
-        return setValue(toValue % result, True)
+        # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+        return (setValue(toValue % result, True), False)
 
     if not type.isPrimitive():
         raise TypeError("Need to learn to wrap %s" % type)
 
     if type.nullable():
+        (recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider,
+                                                         "%s.Value()" % result, successCode,
+                                                         isCreator)
         return ("if (%s.IsNull()) {\n" % result +
                 CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
-                "}\n" +
-                getWrapTemplateForType(type.inner, descriptorProvider,
-                                       "%s.Value()" % result, successCode,
-                                       isCreator))
+                "}\n" + recTemplate, recInfal)
     
     tag = type.tag()
     
     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                IDLType.Tags.uint16, IDLType.Tags.int32]:
-        return setValue("INT_TO_JSVAL(int32_t(%s))" % result)
+        return (setValue("INT_TO_JSVAL(int32_t(%s))" % result), True)
 
     elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
                  IDLType.Tags.double]:
         # XXXbz will cast to double do the "even significand" thing that webidl
         # calls for for 64-bit ints?  Do we care?
-        return setValue("JS_NumberValue(double(%s))" % result)
+        return (setValue("JS_NumberValue(double(%s))" % result), True)
 
     elif tag == IDLType.Tags.uint32:
-        return setValue("UINT_TO_JSVAL(%s)" % result)
+        return (setValue("UINT_TO_JSVAL(%s)" % result), True)
 
     elif tag == IDLType.Tags.bool:
-        return setValue("BOOLEAN_TO_JSVAL(%s)" % result)
+        return (setValue("BOOLEAN_TO_JSVAL(%s)" % result), True)
 
     else:
         raise TypeError("Need to learn to wrap primitive: %s" % type)
 
 def wrapForType(type, descriptorProvider, templateValues):
     """
     Reflect a C++ value of IDL type "type" into JS.  TemplateValues is a dict
     that should contain:
@@ -2621,21 +2627,35 @@ def wrapForType(type, descriptorProvider
                                   the conversion, if not supplied 'return true;'
                                   will be used as the code
       * 'isCreator' (optional): If true, we're wrapping for the return value of
                                 a [Creator] method.  Assumed false if not set.
     """
     wrap = getWrapTemplateForType(type, descriptorProvider,
                                   templateValues.get('result', 'result'),
                                   templateValues.get('successCode', None),
-                                  templateValues.get('isCreator', False))
+                                  templateValues.get('isCreator', False))[0]
 
     defaultValues = {'obj': 'obj'}
     return string.Template(wrap).substitute(defaultValues, **templateValues)
 
+def infallibleForAttr(attr, descriptorProvider):
+    """
+    Determine the fallibility of changing a C++ value of IDL type "type" into
+    JS for the given attribute. Apart from isCreator, all the defaults are used,
+    since the fallbility does not change based on the boolean values,
+    and the template will be discarded.
+
+    CURRENT ASSUMPTIONS:
+        We assume that successCode for wrapping up return values cannot contain
+        failure conditions.
+    """
+    return getWrapTemplateForType(attr.type, descriptorProvider, 'result', None,\
+                                  memberIsCreator(attr))[1]
+
 def typeNeedsCx(type):
     return (type is not None and
             (type.isCallback() or type.isAny() or type.isObject() or
              (type.isUnion() and
               any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes))))
 
 # Returns a CGThing containing the type of the return value, or None
 # if there is no need for a return value.
@@ -2820,17 +2840,17 @@ class CGPerSignatureCall(CGThing):
         return "\nJS::Value* argv = JS_ARGV(cx, vp);\n"
     def getArgc(self):
         return "argc"
 
     def isFallible(self):
         return not 'infallible' in self.extendedAttributes
 
     def wrap_return_value(self):
-        isCreator = self.idlNode.getExtendedAttribute("Creator") is not None
+        isCreator = memberIsCreator(self.idlNode)
         if isCreator:
             # We better be returning addrefed things!
             assert isResultAlreadyAddRefed(self.descriptor,
                                            self.extendedAttributes)
 
         resultTemplateValues = { 'jsvalRef': '*vp', 'jsvalPtr': 'vp',
                                  'isCreator': isCreator}
         try:
@@ -3360,16 +3380,19 @@ class CGSpecializedSetter(CGAbstractStat
         CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
         name = self.attr.identifier.name
         nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name))
         return CGIndenter(CGSetterCall(self.attr.type, nativeName,
                                        self.descriptor, self.attr)).define()
 
+def memberIsCreator(member):
+    return member.getExtendedAttribute("Creator") is not None
+
 class CGPropertyJITInfo(CGThing):
     """
     A class for generating the JITInfo for a property that points to
     our specialized getter and setter.
     """
     def __init__(self, descriptor, attr):
         self.attr = attr
         self.descriptor = descriptor
@@ -3389,19 +3412,19 @@ class CGPropertyJITInfo(CGThing):
                 "  %s,  /* isInfallible. False for setters. */\n"
                 "  false  /* isConstant. False for setters. */\n"
                 "};\n" % (infoName, opName, protoID, depth, failstr))
 
     def define(self):
         getterinfo = ("%s_getterinfo" % self.attr.identifier.name)
         getter = ("(JSJitPropertyOp)specialized_get_%s" %
                   self.attr.identifier.name)
-        # For now, mark all getters fallible, until argument wrapping has been
-        # handled.
-        result = self.defineJitInfo(getterinfo, getter, False)
+        getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.attr, getter=True)
+        getterinfal = getterinfal and infallibleForAttr(self.attr, self.descriptor)
+        result = self.defineJitInfo(getterinfo, getter, getterinfal)
         if not self.attr.readonly:
             setterinfo = ("%s_setterinfo" % self.attr.identifier.name)
             setter = ("(JSJitPropertyOp)specialized_set_%s" %
                       self.attr.identifier.name)
             # Setters are always fallible, since they have to do a typed unwrap.
             result += self.defineJitInfo(setterinfo, setter, False)
         return result