Add interface return value wrapping
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 13 Feb 2012 17:51:55 -0500
changeset 86818 0824a12c5564992db4685507c0915273afbf87cd
parent 86817 54ad6268014c39a07498c2e99b85ac43369b568e
child 86819 0f84963ad48e9175134e557e3ec8b743d783f202
push id121
push userbzbarsky@mozilla.com
push dateMon, 13 Feb 2012 22:52:44 +0000
milestone13.0a1
Add interface return value wrapping
dom/bindings/Codegen.py
dom/bindings/Configuration.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -567,16 +567,19 @@ def getArgumentConversionTemplate(type, 
             "    return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
             "  }\n")
 
         if not type.nullable():
             template += "  ${typeName} &${name} = *${name}_ptr;\n"
             
         return template
 
+    if type.isInterface():
+        raise TypeError("Interface type with no descriptor: " + type)
+
     if type.isString():
         # XXXbz Need to figure out string behavior?  Also, how to
         # detect them?  Also, nullability?
 
         return (
             "  xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n"
             "                       xpc_qsDOMString::eDefaultNullBehavior,\n"
             "                       xpc_qsDOMString::eDefaultUndefinedBehavior);\n"
@@ -595,32 +598,27 @@ def getArgumentConversionTemplate(type, 
             "    bool ok;\n"
             "    $name = (%(enumtype)s) FindEnumStringIndex(cx, ${argVal}, %(values)s, &ok);\n"
             "    if (!ok) {\n"
             "      return false;\n"
             "    }\n"
             "  }" % { "enumtype" : enum + "::value",
                       "values" : enum + "::strings" })
 
-    if (isinstance(type, IDLCallbackType) or
-        (isinstance(type, IDLWrapperType) and
-         isinstance(type.inner, IDLCallbackType))):
+    if type.isCallback():
         # XXXbz we're going to assume that callback types are always
         # nullable and always have [TreatNonCallableAsNull] for now.
         return (
             "  JSObject *${name};\n"
             "  if (${argVal}.isObject() && JS_ObjectIsCallable(cx, &${argVal}.toObject())) {\n"
             "    ${name} = &${argVal}.toObject();\n"
             "  } else {\n"
             "    ${name} = NULL;\n"
             "  }\n")
 
-    if isinstance(type, IDLNullableType) and isinstance(type.inner, IDLCallbackType):
-        raise TypeError("nullable callback")
-
     if not type.isPrimitive():
         return """
   // XXXbz Need conversion for argument type '%s'""" % type
 
     tag = type.tag()
     replacements = dict()
     if type.nullable():
         replacements["declareArg"] = (
@@ -736,41 +734,62 @@ class ArgumentConverter():
                 "&" + self.replacementVariables["argVal"])
         self.descriptor = None
         if argument.type.isPrimitive():
             self.replacementVariables["typeName"] = builtinNames[argument.type.tag()]
         elif argument.type.isInterface() and not argument.type.isArrayBuffer():
             descriptor = descriptorProvider.getDescriptor(
                 argument.type.inner.identifier.name)
             self.descriptor = descriptor
-            if descriptor.concrete:
-                self.replacementVariables["typeName"] = descriptor.nativeClass
-            else:
-                self.replacementVariables["typeName"] = descriptor.nativeInterface
+            self.replacementVariables["typeName"] = descriptor.typeName
 
     def __str__(self):
         return string.Template(
             "\n" + getArgumentConversionTemplate(self.argument.type,
                                                  self.descriptor)
             ).substitute(self.replacementVariables)
 
-def getWrapTemplateForTypeImpl(type, result):
+def getWrapTemplateForTypeImpl(type, result, descriptorProvider,
+                               resultAlreadyAddRefed):
     if type.isSequence() or type.isArray():
         raise TypeError("Can't handle sequence or array return values yet")
 
     if type.isVoid():
         return """
   ${jsvalRef} = JSVAL_VOID;
   return true;"""
 
     if type.isInterface() and not type.isArrayBuffer():
-        # Wrap the object
-        return """
-  // XXXbz need to learn to wrap objects
-  return false;"""
+        descriptor = descriptorProvider.getDescriptor(type.inner.identifier.name)
+        wrappingCode = ("""
+  if (!%s) {
+    ${jsvalRef} = JSVAL_NULL;
+    return true;
+  }""" % result) if type.nullable() else ""
+        if descriptor.concrete:
+            # All our concrete objects inherit from wrapper cache for now
+            wrappingCode += """
+  JSObject* result_obj = %s->GetWrapper();
+  if (result_obj &&
+      js::GetObjectCompartment(result_obj) == js::GetObjectCompartment(obj)) {
+    ${jsvalRef} = JS::ObjectValue(*result_obj);
+    return true;
+  }
+  // XXXbz figure out the actual security wrapper story here
+  bool triedToWrap;
+  result_obj = %s->WrapObject(cx, obj, &triedToWrap);
+  if (!result_obj) {
+    return false;
+  }
+  ${jsvalRef} = JS::ObjectValue(*result_obj);
+  return true;
+""" % (result, result);
+        else:
+            raise TypeError("Don't know how to wrap non-concrete types yet")
+        return wrappingCode
 
     if type.isString():
         if type.nullable():
             return """
   return xpc::StringToJsval(cx, %s, ${jsvalPtr});""" % result
         else:
             return """
   return xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr});""" % result
@@ -784,19 +803,17 @@ def getWrapTemplateForTypeImpl(type, res
   JSString* result_str = JS_NewStringCopyZ(cx, %(strings)s[uint32_t(%(result)s)]);
   if (!result_str) {
     return false;
   }
   ${jsvalRef} = JS::StringValue(result_str);
   return true;""" % { "result" : result,
                       "strings" : type.inner.identifier.name + "::strings" }
 
-    if (isinstance(type, IDLCallbackType) or
-        (isinstance(type, IDLWrapperType) and
-         isinstance(type.inner, IDLCallbackType))):
+    if type.isCallback() and not type.isInterface():
         # XXXbz we're going to assume that callback types are always
         # nullable and always have [TreatNonCallableAsNull] for now.
         return """
   ${jsvalRef} = JS::ObjectOrNullValue(%s);
   return true;""" % result
 
     if not type.isPrimitive():
         return """
@@ -804,17 +821,19 @@ def getWrapTemplateForTypeImpl(type, res
   return false;""" % type
 
     if type.nullable():
         return """
   if (%s.IsNull()) {
     ${jsvalRef} = JSVAL_NULL;
     return true;
   }
-%s""" % (result, getWrapTemplateForTypeImpl(type.inner, "%s.Value()" % result))
+%s""" % (result, getWrapTemplateForTypeImpl(type.inner, "%s.Value()" % result,
+                                            descriptorProvider,
+                                            resultAlreadyAddRefed))
     
     tag = type.tag()
     
     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                IDLType.Tags.uint16, IDLType.Tags.int32]:
         return """
   ${jsvalRef} = INT_TO_JSVAL(int32_t(%s));
   return true;""" % result
@@ -836,18 +855,19 @@ def getWrapTemplateForTypeImpl(type, res
   ${jsvalRef} = BOOLEAN_TO_JSVAL(%s);
   return true;""" % result
 
     else:
         return """
   // XXXbz need to learn to wrap other things
   return false;"""
 
-def getWrapTemplateForType(type):
-    return getWrapTemplateForTypeImpl(type, "result")
+def getWrapTemplateForType(type, descriptorProvider, resultAlreadyAddRefed):
+    return getWrapTemplateForTypeImpl(type, "result", descriptorProvider,
+                                      resultAlreadyAddRefed)
 
 
 class PerSignatureCall():
     """
     This class handles the guts of generating code for a particular
     call signature.  A call signature consists of three things:
 
     1) A return type, which can be None to indicate that there is no
@@ -877,37 +897,30 @@ class PerSignatureCall():
     def __init__(self, returnType, arguments, nativeMethodName,
                  descriptor, idlNode, extendedAttributes):
         self.returnType = returnType
         self.arguments = arguments
         self.nativeMethodName = nativeMethodName
         self.descriptor = descriptor
         self.idlNode = idlNode
         self.extendedAttributes = extendedAttributes
+        # Default to already_AddRefed on the main thread, raw pointer in workers
+        self.resultAlreadyAddRefed = not descriptor.workers
 
     def getArgv(self):
         assert(False) # Override me
     def getArgc(self):
         assert(False) # Override me
     def getErrorReport(self):
         assert(False) # Override me
 
     def isFallible(self):
         return not 'infallible' in self.extendedAttributes
 
     def unwrap_arguments(self):
-
-        # XXXbholley - This is just proof of concept code for bz to do something
-        # useful with.
-        for i in range(len(self.arguments)):
-            argType = self.arguments[i].type
-            if argType.isInterface():
-                argDescriptor = self.descriptor.getDescriptor(str(argType))
-                assert self.descriptor.workers == argDescriptor.workers
-
         args = [ArgumentConverter(self.arguments[i], i, self.getArgv(),
                                   self.getArgc(), self.descriptor) for
                 i in range(len(self.arguments))]
         return "".join([str(arg) for arg in args])
 
     def generate_call(self):
         nativeArgs = ["arg" + str(i) for i in range(len(self.arguments))]
         # XXXbz arguments that have to go in outparams go here?
@@ -923,19 +936,25 @@ class PerSignatureCall():
             else:
                 resultDeclaration = "  %s result = 0;\n" % builtinNames[self.returnType.tag()]
         elif self.returnType.isString():
             resultDeclaration = "  nsString result;\n"
         elif self.returnType.isEnum():
             if self.returnType.nullable():
                 raise TypeError("We don't support nullable enum return values")
             resultDeclaration = "  %s result;\n" % (self.returnType.inner.identifier.name + "::value")
-        elif (isinstance(self.returnType, IDLCallbackType) or
-              (isinstance(self.returnType, IDLWrapperType) and
-               isinstance(self.returnType.inner, IDLCallbackType))):
+        elif self.returnType.isInterface() and not self.returnType.isArrayBuffer():
+            if not self.resultAlreadyAddRefed:
+                resultDeclaration = "  %s *result = NULL;\n"
+            else:
+                resultDeclaration = "  nsRefPtr<%s> result;\n"
+            resultDescriptor = self.descriptor.getDescriptor(
+                self.returnType.inner.identifier.name)
+            resultDeclaration = resultDeclaration % resultDescriptor.typeName
+        elif self.returnType.isCallback():
             # XXXbz we're going to assume that callback types are always
             # nullable for now.
             resultDeclaration = "  JSObject* result = NULL;\n"
         else:
             resultDeclaration = "  // XXX need to declare |result| for type %s.\n" % self.returnType
 
         if self.returnType is None or self.returnType.isVoid():
             resultAssignment = ""
@@ -960,17 +979,18 @@ class PerSignatureCall():
                                "resultAssign": resultAssignment,
                                "method": self.nativeMethodName,
                                "args": ', '.join(nativeArgs),
                                "errorCheck": errorCheckClause })
 
     def wrap_return_value(self):
         resultTemplateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp'}
         return string.Template(
-            getWrapTemplateForType(self.returnType)
+            getWrapTemplateForType(self.returnType, self.descriptor,
+                                   self.resultAlreadyAddRefed)
             ).substitute(resultTemplateValues)
 
     def getErrorReport(self):
         return 'return xpc_qsThrowMethodFailedWithDetails(cx, rv, "%s", "%s");'\
                % (self.descriptor.name, self.idlNode.identifier.name)
 
     def __str__(self):
         return (self.unwrap_arguments() + self.generate_call() +
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -60,20 +60,22 @@ class Descriptor:
         self.interface = interface
 
         # Read the desc, and fill in the relevant defaults.
         self.concrete = desc['concrete']
         self.workers = desc.get('workers', False)
         self.nativeIsISupports = not self.workers
         if self.concrete:
             self.nativeClass = desc['nativeClass']
+            self.typeName = self.nativeClass
         else:
             self.nativeInterface = desc.get('nativeInterface', 'XXXFillMeInbz!')
+            self.typeName = self.nativeInterface
 
-        headerDefault = self.nativeClass if self.concrete else self.nativeInterface
+        headerDefault = self.typeName
         headerDefault = headerDefault.split("::")[-1] + ".h"
         self.headerFile = desc.get('headerFile', headerDefault)
 
         self.customTrace = desc.get('customTrace', self.workers)
         self.customFinalize = desc.get('customFinalize', self.workers)
 
         def make_name(name):
             return name + "_workers" if self.workers else name