Bug 748267 part 3. Codegen for sequence return values. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 15 May 2012 14:23:29 -0400
changeset 98553 a23d7ab147321ae42dcd8565172cca9ea4c07ad8
parent 98552 7ba14e6312eaaae329e5c0d74b5635db8962e029
child 98554 095eef67ec7baa978791459ce71e432f9653aa43
push id173
push userlsblakk@mozilla.com
push dateFri, 24 Aug 2012 15:39:16 +0000
treeherdermozilla-release@bcc45eb1fb41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs748267
milestone15.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 748267 part 3. Codegen for sequence return values. r=peterv The big block in getRetvalDeclarationForType is just direct cut/paste from CGCallGenerator plus the addition of the sequence case. The IDL parser changes were OKed by khuey; they're needed so that we don't have to worry about the ordering of sequence with conversions for strings and primitives.
dom/bindings/Codegen.py
dom/bindings/Nullable.h
dom/bindings/parser/WebIDL.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1648,18 +1648,55 @@ def getWrapTemplateForType(type, descrip
                CGIndenter(CGGeneric(failureCode)).define() + "\n" +
                "}\n" +
                successCode) % (wrapCall)
         return str
     
     if type is None or type.isVoid():
         return setValue("JSVAL_VOID")
 
-    if type.isSequence() or type.isArray():
-        raise TypeError("Can't handle sequence or array return values yet")
+    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 """
+if (%s.IsNull()) {
+%s
+}
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(),
+         getWrapTemplateForType(type.inner, descriptorProvider,
+                                "%s.Value()" % result, successCode))
+
+        # 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,
+                'successCode': ("if (!JS_SetElement(cx, returnArray, i, &tmp)) {\n"
+                                "  return false;\n"
+                                "}"),
+                'jsvalRef': "tmp",
+                'jsvalPtr': "&tmp"
+                }
+            )
+        innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
+        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)")
 
     if type.isInterface() and not type.isArrayBuffer():
         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:
@@ -1770,64 +1807,83 @@ def wrapForType(type, descriptorProvider
     """
     wrap = getWrapTemplateForType(type, descriptorProvider,
                                   templateValues.get('result', 'result'),
                                   templateValues.get('successCode', None))
 
     defaultValues = {'obj': 'obj'}
     return string.Template(wrap).substitute(defaultValues, **templateValues)
 
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+                                resultAlreadyAddRefed):
+    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)
+    elif returnType.isInterface() and not returnType.isArrayBuffer():
+        result = CGGeneric(descriptorProvider.getDescriptor(
+            returnType.unroll().inner.identifier.name).nativeType)
+        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("JS::Value")
+    elif returnType.isSequence():
+        nullable = returnType.nullable()
+        if nullable:
+            returnType = returnType.inner
+        # Assume no need to addref for now
+        result = CGWrapper(getRetvalDeclarationForType(returnType.inner,
+                                                       descriptorProvider,
+                                                       False),
+                           pre="nsTArray< ", post=" >")
+        if nullable:
+            result = CGWrapper(result, pre="Nullable< ", post=" >")
+    else:
+        raise TypeError("Don't know how to declare return value for %s" %
+                        returnType)
+    return result
+
+
 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, argsPre, returnType,
                  resultAlreadyAddRefed, descriptorProvider, nativeMethodName, static):
         CGThing.__init__(self)
 
         isFallible = errorReport is not None
 
         args = CGList([CGGeneric("arg" + str(i)) for i in range(argCount)], ", ")
-        resultOutParam = returnType is not None and returnType.isString()
+        resultOutParam = (returnType is not None and
+                          (returnType.isString() or returnType.isSequence()))
         # 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)
-        elif returnType.isInterface() and not returnType.isArrayBuffer():
-            result = CGGeneric(descriptorProvider.getDescriptor(
-                returnType.unroll().inner.identifier.name).nativeType)
-            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("JS::Value")
-        else:
-            raise TypeError("Don't know how to declare return value for %s" %
-                            returnType)
+        result = getRetvalDeclarationForType(returnType, descriptorProvider,
+                                             resultAlreadyAddRefed)
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
 
         call = CGGeneric(nativeMethodName)
         if static:
             call = CGWrapper(call, pre="%s::" % (descriptorProvider.getDescriptor(
                 returnType.unroll().inner.identifier.name).nativeType))
--- a/dom/bindings/Nullable.h
+++ b/dom/bindings/Nullable.h
@@ -47,16 +47,21 @@ public:
     mIsNull = true;
   }
 
   const T& Value() const {
     MOZ_ASSERT(!mIsNull);
     return mValue;
   }
 
+  T& Value() {
+    MOZ_ASSERT(!mIsNull);
+    return mValue;
+  }
+
   bool IsNull() const {
     return mIsNull;
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -809,20 +809,20 @@ class IDLSequenceType(IDLType):
 
     def __str__(self):
         return self.inner.__str__() + "Sequence"
 
     def nullable(self):
         return False
 
     def isPrimitive(self):
-        return self.inner.isPrimitive()
+        return False;
 
     def isString(self):
-        return self.inner.isString()
+        return False;
 
     def isVoid(self):
         return False
 
     def isSequence(self):
         return True
 
     def isArray(self):