Set up basic interface and string argument unwrapping
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 10 Feb 2012 23:34:05 -0500
changeset 86752 c109aaca3efd5933ed09fea14e2a4c50e792b0a3
parent 86751 27d34da3ffcd4213118dc1bd19bdc5757b231e0f
child 86753 36b356cfa9429acc28f33c0871bb2ad86c54beb4
push id105
push userbzbarsky@mozilla.com
push dateSat, 11 Feb 2012 04:34:19 +0000
milestone13.0a1
Set up basic interface and string argument unwrapping
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/Utils.h
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -38,16 +38,30 @@ DOMInterfaces = {
     'nativeClass': 'nsXMLHttpRequest',
 },
 {
     'concrete': True,
     'nativeClass' : 'mozilla::dom::workers::xhr::XMLHttpRequestPrivate',
     'workers': True
 }],
 
+'XMLHttpRequestUpload': [
+{
+     'concrete': True,
+     'nativeClass': 'nsXMLHttpRequestUpload',
+     'headerFile': 'nsXMLHttpRequest.h'
+},
+{
+     'concrete': True,
+   # XXX Doesn't exist yet: 'nativeClass': 'mozilla::dom::workers::xhr:XMLHttpRequestUpload',
+     'nativeClass': 'nsXMLHttpRequestUpload',
+     'headerFile': 'nsXMLHttpRequest.h',
+     'workers': True
+}],
+
 'EventTarget': [
 {
     'concrete': True,
     'nativeClass': 'nsDOMEventTargetHelper'
 },
 {
     'concrete': True,
     # XXX Doesn't exist yet: 'nativeClass': 'mozilla::dom::workers::EventTargetPrivate',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -224,16 +224,35 @@ class CGHeaders(CGWrapper):
         interfaceDeps.extend([d.parent for d in interfaceDeps if d.parent])
         filenames = set([os.path.basename(d.filename()) for d in interfaceDeps])
         bindingIncludes = ['mozilla/dom/bindings/' + f.replace('.webidl', 'Binding.h')
                            for f in filenames]
 
         # Grab all the implementation declaration files we need.
         implementationIncludes = [f for f in set([d.headerFile for d in descriptors])]
 
+        # Now find all the things we'll need as arguments because we
+        # need to wrap or unwrap them.
+        typeHeaders = []
+        for d in descriptors:
+            members = [m for m in d.interface.members]
+            signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+            types = [s[0] for s in signatures]
+            types.extend([t.type for s in signatures for t in s[1]])
+
+            attrs = [a for a in members if a.isAttr()]
+            types.extend([a.type for a in attrs])
+
+            typeDescriptors = [d.getDescriptor(t.inner.identifier.name)
+                               for t in types
+                               if t.isInterface() and not t.isArrayBuffer()]
+            typeHeaders.extend(desc.headerFile for desc in typeDescriptors)
+            
+        implementationIncludes.extend([f for f in set(typeHeaders)])
+
         # Let the machinery do its thing.
         def _includeString(includes):
             return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
         CGWrapper.__init__(self, child,
                            declarePre=_includeString(declareIncludes),
                            definePre=_includeString(implementationIncludes)
                                      + _includeString(bindingIncludes))
 
@@ -476,32 +495,89 @@ builtinNames = {
     IDLType.Tags.uint8: 'uint8_t',
     IDLType.Tags.uint16: 'uint16_t',
     IDLType.Tags.uint32: 'uint32_t',
     IDLType.Tags.uint64: 'uint64_t',
     IDLType.Tags.float: 'float',
     IDLType.Tags.double: 'double'
 }
 
-def getArgumentConversionTemplate(type):
-    if type.isInterface():
-        # XXXbz need more checking to figure out what to do.  In particular,
-        # need to know here how interface names map to internal types
-        return """
-  // XXXbz Need conversion for argument type '%s'""" % type
+class ConcreteObjectUnwrapper():
+    """
+    A class for unwrapping an object named by the "source" argument
+    based on the passed-in descriptor and storing it in a variable
+    called by the name in the "target" argument.
+
+    codeOnFailure is the code to run if unwrapping fails.
+    """
+    def __init__(self, descriptor, source, target, codeOnFailure):
+        self.substitution = { "type" : descriptor.nativeClass,
+                              "protoID" : "id::" + descriptor.name,
+                              "protoIDIndex" : "depth::" + descriptor.name,
+                              "source" : source,
+                              "target" : target,
+                              "codeOnFailure" : codeOnFailure }
+
+    def __str__(self):
+        return string.Template("""
+  {
+    nsresult rv = UnwrapObject(cx, ${source}, ${protoID},
+                               ${protoIDIndex}, &${target});
+    if (NS_FAILED(rv)) {
+      ${codeOnFailure}
+    }
+  }""").substitute(self.substitution)
+
+class FailureFatalConcreteObjectUnwrapper(ConcreteObjectUnwrapper):
+    """
+    As ConcreteObjectUnwrapper, but defaulting to throwing if unwrapping fails
+    """
+    def __init__(self, descriptor, source, target):
+        ConcreteObjectUnwrapper.__init__(self, descriptor, source, target,
+                                         "xpc_qsThrow(cx, rv);")
 
-    # XXXbz handle strings here eventually.  Need to figure
-    # out string behavior?  Also, how to detect them?
-#            return """
-#  xpc_qsDOMString arg%(index)d(cx, %(argVal)s, %(argPtr)s,
-#                        xpc_qsDOMString::eDefaultNullBehavior,
-#                        xpc_qsDOMString::eDefaultUndefinedBehavior);
-#  if (!arg%(index)d.IsValid()) {
-#    return false;
-#  }"""
+def getArgumentConversionTemplate(type, descriptor):
+    if descriptor is not None:
+        # This is an interface that we implement as a concrete class
+        # or an XPCOM interface.
+        template = ("  ${typeName} *${name};\n"
+                    "  if (${argVal}.isObject()) {")
+        if descriptor.concrete:
+            template += str(FailureFatalConcreteObjectUnwrapper(descriptor,
+                                                                "&${argVal}.toObject()",
+                                                                "${name}")).replace("\n", "\n  ") + "\n"
+        else:
+            raise TypeError("Can't handle this interface type yet, becase we "
+                            "have no support for unwrapping non-concrete types: " + type)
+        if type.nullable():
+            template = template + (
+                "  } else if (${argVal}.isNullOrUndefined()) {\n"
+                "    ${name} = NULL;\n")
+
+        template = template + (
+            "  } else {\n"
+            "    return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+            "  }\n")
+        return template
+
+    try:
+        if type.tag() is IDLType.Tags.domstring:
+            # 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"
+                "  if (!${name}.IsValid()) {\n"
+                "    return false;\n"
+                "  }\n")
+    except:
+        # Some types just throw when you call tag() on them....
+        pass
 
     if not type.isPrimitive():
         return """
   // XXXbz Need conversion for argument type '%s'""" % type
 
     tag = type.tag()
     replacements = dict()
     if type.nullable():
@@ -587,17 +663,17 @@ def getArgumentConversionTemplate(type):
             "  }\n" % replacements)
 
 class ArgumentConverter():
     """
     A class that takes an IDL argument object, its index in the
     argument list, and the argv and argc strings and generates code to
     unwrap the argument to the right native type.
     """
-    def __init__(self, argument, index, argv, argc):
+    def __init__(self, argument, index, argv, argc, descriptorProvider):
         self.argument = argument
         # XXXbz should optional jsval args get JSVAL_VOID? What about
         # others?
         self.replacementVariables = {
             "index" : index,
             "argc" : argc,
             "argv" : argv,
             "defaultValue" : "JSVAL_NULL",
@@ -611,33 +687,43 @@ class ArgumentConverter():
                 "(${index} < ${argc} ? &${argv}[${index}] : NULL)"
                 ).substitute(self.replacementVariables)
         else:
             self.replacementVariables["argVal"] = string.Template(
                 "${argv}[${index}]"
                 ).substitute(self.replacementVariables)
             self.replacementVariables["argPtr"] = (
                 "&" + 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
 
     def __str__(self):
         return string.Template(
-            "\n" + getArgumentConversionTemplate(self.argument.type)
+            "\n" + getArgumentConversionTemplate(self.argument.type,
+                                                 self.descriptor)
             ).substitute(self.replacementVariables)
 
 def getWrapTemplateForType(type):
     if type.isVoid():
         return """
   ${jsvalRef} = JSVAL_VOID;
   return true;"""
 
-    if type.isInterface():
+    if type.isInterface() and not type.isArrayBuffer():
         # Wrap the object
-            return """
+        return """
   // XXXbz need to learn to wrap objects
   return false;"""
 
     if not type.isPrimitive():
         return """
   // XXXbz need to learn to wrap other things
   return false;"""
 
@@ -729,17 +815,17 @@ class PerSignatureCall():
         # 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()) for
+                                  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?
         if self.isFallible():
             nativeArgs.append("rv")
@@ -840,23 +926,20 @@ class SetterCall(GetterSetterCall):
 class CGAbstractBindingMethod(CGAbstractStaticMethod):
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractStaticMethod.__init__(self, descriptor, name,
                                         returnType, args)
     def definition_body(self):
         return (self.unwrap_this() + self.generate_code())
 
     def unwrap_this(self):
-        returnStr = ' false' if self.returnType == 'JSBool' else ''
-        return """
-  %s *self;
-  if (!UnwrapThis(cx, obj, %s, %s, &self))
-    return%s;
-""" % (self.descriptor.nativeClass, "id::" + self.descriptor.name,
-       "depth::" + self.descriptor.name, returnStr)
+        return (("""
+  %s *self;""" % self.descriptor.nativeClass) +
+                str(FailureFatalConcreteObjectUnwrapper(self.descriptor,
+                                                        "obj", "self")))
 
     def generate_code(self):
         assert(False) # Override me
 
 def MakeNativeName(name):
     return name[0].upper() + name[1:]
 
 class CGNativeBindingMethod(CGAbstractBindingMethod):
--- a/dom/bindings/Utils.h
+++ b/dom/bindings/Utils.h
@@ -215,61 +215,16 @@ UnwrapObject(JSContext *cx,
     *value = UnwrapDOMObject<T>(obj);
     return NS_OK;
   }
 
   /* It's the wrong sort of DOM object */
   return NS_ERROR_XPC_BAD_CONVERT_JS;
 }
 
-template<class T>
-inline bool
-UnwrapThis(JSContext *cx,
-           JSObject *obj,
-           prototypes::ID protoID,
-           prototypes::Depth protoIDIndex,
-           T **value)
-{
-  nsresult rv = UnwrapObject(cx, obj, protoID, protoIDIndex, value);
-  if (NS_FAILED(rv)) {
-    return xpc_qsThrow(cx, rv);
-  }
-  return true;
-}
-
-template<class T>
-inline nsresult
-UnwrapInterfaceArg(JSContext *cx,
-                   jsval v,
-                   prototypes::ID protoID,
-                   prototypes::Depth protoIDIndex,
-                   T **value,
-                   nsISupports **argRef,
-                   jsval *vp)
-{
-  if (v.isObject()) {
-    // Fast-path the case when we have a DOM object.
-    nsresult rv = UnwrapObject(cx, &v.toObject(), protoID, protoIDIndex, value);
-    if (NS_SUCCEEDED(rv)) {
-      // It was a DOM object.  We're done.
-      *vp = v;
-      return rv;
-    }
-  
-    if (rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO) {
-      // This is a fatal failure; just return
-      return rv;
-    }
-  }
-
-  // Fall back on unwrapping old-style arguments (possibly including
-  // nodelist bindings).
-  return xpc_qsUnwrapArg(cx, v, value, argRef, vp);
-}
-
 inline JSObject **
 GetProtoArray(JSObject *global)
 {
   MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
   return static_cast<JSObject**>(
     js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
 }