Bug 820665 part 3. WebIDL spec updates to changes in how callback interfaces work. r=khuey
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 03 Jan 2013 14:03:11 -0500
changeset 123049 ed9af04edd8c73649151ad77fd5458943b9ba668
parent 123048 e78f9e6ea4d9977c9b1e98f7e9a3fa65ee971459
child 123050 e72009158e15dc3f17af1c70ac358a2264058b73
push idunknown
push userunknown
push dateunknown
reviewerskhuey
bugs820665
milestone20.0a1
Bug 820665 part 3. WebIDL spec updates to changes in how callback interfaces work. r=khuey Specifically, any object that's not a Date or RegExp can implement a callback interface.
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -228,16 +228,22 @@ IsConvertibleToDictionary(JSContext* cx,
 
 MOZ_ALWAYS_INLINE bool
 IsConvertibleToDictionary(JSContext* cx, JS::Value val)
 {
   return val.isNullOrUndefined() ||
     (val.isObject() && IsConvertibleToDictionary(cx, &val.toObject()));
 }
 
+MOZ_ALWAYS_INLINE bool
+IsConvertibleToCallbackInterface(JSContext* cx, JSObject* obj)
+{
+  return IsNotDateOrRegExp(cx, obj);
+}
+
 inline bool
 IsPlatformObject(JSContext* cx, JSObject* obj)
 {
   MOZ_ASSERT(obj);
   // Fast-path the common cases
   JSClass* clasp = js::GetObjectJSClass(obj);
   if (js::Valueify(clasp) == &js::ObjectClass) {
     return false;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1931,22 +1931,26 @@ class CallbackObjectUnwrapper:
                 '%s' % (descriptor.name, exceptionCode))
         self.descriptor = descriptor
         self.substitution = { "nativeType" : descriptor.nativeType,
                               "source" : source,
                               "target" : target,
                               "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define() }
 
     def __str__(self):
+        checkObjectType = CGIfWrapper(
+            CGGeneric(self.substitution["codeOnFailure"]),
+            "!IsConvertibleToCallbackInterface(cx, %(source)s)" %
+            self.substitution).define() + "\n\n"
         if self.descriptor.workers:
-            return string.Template(
+            return checkObjectType + string.Template(
                 "${target} = ${source};"
                 ).substitute(self.substitution)
 
-        return string.Template(
+        return checkObjectType + string.Template(
             """nsresult rv;
 XPCCallContext ccx(JS_CALLER, cx);
 if (!ccx.IsValid()) {
   rv = NS_ERROR_XPC_BAD_CONVERT_JS;
 ${codeOnFailure}
 }
 
 const nsIID& iid = NS_GET_IID(${nativeType});
@@ -2343,21 +2347,16 @@ for (uint32_t i = 0; i < length; ++i) {
             assert len(callbackMemberTypes) == 1
             memberType = callbackMemberTypes[0]
             name = memberType.name
             callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${obj}, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
             names.append(name)
         else:
             callbackObject = None
 
-        if callbackObject and callbackMemberTypes[0].isCallbackInterface():
-            callbackObject = CGWrapper(CGIndenter(callbackObject),
-                                       pre="if (!IsPlatformObject(cx, &argObj)) {\n",
-                                       post="\n}")
-
         dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
         if len(dictionaryMemberTypes) > 0:
             raise TypeError("No support for unwrapping dictionaries as member "
                             "of a union")
         else:
             dictionaryObject = None
 
         objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
@@ -4027,26 +4026,23 @@ class CGMethodCall(CGThing):
             # Now check for distinguishingArg being various kinds of objects.
             # The spec says to check for the following things in order:
             # 1)  A platform object that's not a platform array object, being
             #     passed to an interface or "object" arg.
             # 2)  A Date object being passed to a Date or "object" arg.
             # 3)  A RegExp object being passed to a RegExp or "object" arg.
             # 4)  A callable object being passed to a callback or "object" arg.
             # 5)  Any non-Date and non-RegExp object being passed to a
-            #     dictionary or array or sequence or "object" arg.
-            # 6)  Some other kind of object being passed to a callback
-            #     interface or "object" arg.
+            #     array or sequence or callback interface dictionary or
+            #     "object" arg.
             #
-            # Unfortunately, we cannot push the "some other kind of object"
-            # check down into case 6, because callbacks interfaces _can_ normally be
-            # initialized from platform objects. But we can coalesce the other
-            # five cases together, as long as we make sure to check whether our
-            # object works as an interface argument before checking whether it
-            # works as an arraylike or dictionary or callback function.
+            # We can can coalesce these five cases together, as long as we make
+            # sure to check whether our object works as an interface argument
+            # before checking whether it works as an arraylike or dictionary or
+            # callback function or callback interface.
 
             # First grab all the overloads that have a non-callback interface
             # (which includes typed arrays and arraybuffers) at the
             # distinguishing index.  We can also include the ones that have an
             # "object" here, since if those are present no other object-typed
             # argument will be.
             objectSigs = [
                 s for s in possibleSignatures
@@ -4057,21 +4053,22 @@ class CGMethodCall(CGThing):
             objectSigs.extend(s for s in possibleSignatures
                               if distinguishingType(s).isDate())
 
             # And all the overloads that take callbacks
             objectSigs.extend(s for s in possibleSignatures
                               if distinguishingType(s).isCallback())
 
             # Now append all the overloads that take an array or sequence or
-            # dictionary:
+            # dictionary or callback interface:
             objectSigs.extend(s for s in possibleSignatures
                               if (distinguishingType(s).isArray() or
                                   distinguishingType(s).isSequence() or
-                                  distinguishingType(s).isDictionary()))
+                                  distinguishingType(s).isDictionary() or
+                                  distinguishingType(s).isCallbackInterface()))
 
             # There might be more than one thing in objectSigs; we need to check
             # which ones we unwrap to.
             if len(objectSigs) > 0:
                 # Here it's enough to guard on our argument being an object. The
                 # code for unwrapping non-callback interfaces, typed arrays,
                 # sequences, arrays, and Dates will just bail out and move on to
                 # the next overload if the object fails to unwrap correctly,
@@ -4087,21 +4084,16 @@ class CGMethodCall(CGThing):
                     caseBody.append(CGIndenter(CGGeneric("do {")));
                     # Indent by 4, since we need to indent further
                     # than our "do" statement
                     tryCall(sig, 4, isDefinitelyObject=True)
                     caseBody.append(CGIndenter(CGGeneric("} while (0);")))
 
                 caseBody.append(CGGeneric("}"))
 
-            # Check for vanilla JS objects
-            pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
-                               (distinguishingArg, distinguishingArg),
-                               lambda s: distinguishingType(s).isCallbackInterface())
-
             # The remaining cases are mutually exclusive.  The
             # pickFirstSignature calls are what change caseBody
             # Check for strings or enums
             if pickFirstSignature(None,
                                   lambda s: (distinguishingType(s).isString() or
                                              distinguishingType(s).isEnum())):
                 pass
             # Check for primitives
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -477,18 +477,25 @@ public:
   // Static methods and attributes
   static void StaticMethod(nsISupports*, bool);
   static bool StaticAttribute(nsISupports*);
   static void SetStaticAttribute(nsISupports*, bool);
 
   // Overload resolution tests
   bool Overload1(TestInterface&);
   TestInterface* Overload1(const nsAString&, TestInterface&);
+  void Overload2(TestInterface&);
   void Overload2(const Dict&);
   void Overload2(const nsAString&);
+  void Overload3(TestInterface&);
+  void Overload3(const TestCallback&);
+  void Overload3(const nsAString&);
+  void Overload4(TestInterface&);
+  void Overload4(TestCallbackInterface&);
+  void Overload4(const nsAString&);
 
   // Variadic handling
   void PassVariadicThirdArg(const nsAString&, int32_t,
                             const Sequence<OwningNonNull<TestInterface> >&);
 
   // Miscellania
   int32_t AttrWithLenientThis();
   void SetAttrWithLenientThis(int32_t);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -427,18 +427,25 @@ interface TestInterface {
   // Static methods and attributes
   static attribute boolean staticAttribute;
   static void staticMethod(boolean arg);
 
   // Overload resolution tests
   //void overload1(DOMString... strs);
   boolean overload1(TestInterface arg);
   TestInterface overload1(DOMString strs, TestInterface arg);
+  void overload2(TestInterface arg);
   void overload2(optional Dict arg);
   void overload2(DOMString arg);
+  void overload3(TestInterface arg);
+  void overload3(TestCallback arg);
+  void overload3(DOMString arg);
+  void overload4(TestInterface arg);
+  void overload4(TestCallbackInterface arg);
+  void overload4(DOMString arg);
 
   // Variadic handling
   void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3);
 
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -346,18 +346,25 @@ interface TestExampleInterface {
   // Static methods and attributes
   static attribute boolean staticAttribute;
   static void staticMethod(boolean arg);
 
   // Overload resolution tests
   //void overload1(DOMString... strs);
   boolean overload1(TestInterface arg);
   TestInterface overload1(DOMString strs, TestInterface arg);
+  void overload2(TestInterface arg);
   void overload2(optional Dict arg);
   void overload2(DOMString arg);
+  void overload3(TestInterface arg);
+  void overload3(TestCallback arg);
+  void overload3(DOMString arg);
+  void overload4(TestInterface arg);
+  void overload4(TestCallbackInterface arg);
+  void overload4(DOMString arg);
 
   // Variadic handling
   void passVariadicThirdArg(DOMString arg1, long arg2, TestInterface... arg3);
 
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;