From ae2f03e9ab209fc810ed795813e1cc0113a787d9 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 3 May 2012 00:35:38 -0400 Subject: [PATCH] Bug 748983. Fix the instanceof behavior for new bindings in situations where we don't need a custom hasInstance hook. r=peterv --- dom/bindings/BindingUtils.cpp | 54 +++++++++++++++++++------- dom/bindings/BindingUtils.h | 16 +++++++- dom/bindings/Codegen.py | 33 +++++++++++----- dom/bindings/test/Makefile.in | 1 + dom/bindings/test/test_InstanceOf.html | 28 +++++++++++++ 5 files changed, 107 insertions(+), 25 deletions(-) create mode 100644 dom/bindings/test/test_InstanceOf.html diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index fedde656379..0e95e5d616d 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -25,17 +25,27 @@ DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs) static JSObject* CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver, - JSClass* constructorClass, JSObject* proto, + JSClass* constructorClass, JSNative constructorNative, + unsigned ctorNargs, JSObject* proto, JSFunctionSpec* staticMethods, ConstantSpec* constants, const char* name) { - JSObject* functionProto = JS_GetFunctionPrototype(cx, global); - if (!functionProto) { - return NULL; + JSObject* constructor; + if (constructorClass) { + JSObject* functionProto = JS_GetFunctionPrototype(cx, global); + if (!functionProto) { + return NULL; + } + constructor = JS_NewObject(cx, constructorClass, functionProto, global); + } else { + MOZ_ASSERT(constructorNative); + JSFunction* fun = JS_NewFunction(cx, constructorNative, ctorNargs, + JSFUN_CONSTRUCTOR, global, name); + if (!fun) { + return NULL; + } + constructor = JS_GetFunctionObject(fun); } - - JSObject* constructor = - JS_NewObject(cx, constructorClass, functionProto, global); if (!constructor) { return NULL; } @@ -92,17 +102,20 @@ CreateInterfacePrototypeObject(JSContext* cx, JSObject* global, JSObject* CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver, JSObject* protoProto, JSClass* protoClass, - JSClass* constructorClass, JSFunctionSpec* methods, + JSClass* constructorClass, JSNative constructor, + unsigned ctorNargs, JSFunctionSpec* methods, JSPropertySpec* properties, ConstantSpec* constants, JSFunctionSpec* staticMethods, const char* name) { - MOZ_ASSERT(protoClass || constructorClass, "Need at least one class!"); + MOZ_ASSERT(protoClass || constructorClass || constructor, + "Need at least one class or a constructor!"); MOZ_ASSERT(!(methods || properties) || protoClass, "Methods or properties but no protoClass!"); - MOZ_ASSERT(!staticMethods || constructorClass, - "Static methods but no constructorClass!"); - MOZ_ASSERT(bool(name) == bool(constructorClass), + MOZ_ASSERT(!staticMethods || constructorClass || constructor, + "Static methods but no constructorClass or constructor!"); + MOZ_ASSERT(bool(name) == bool(constructorClass || constructor), "Must have name precisely when we have an interface object"); + MOZ_ASSERT(!constructorClass || !constructor); JSObject* proto; if (protoClass) { @@ -117,9 +130,10 @@ CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver, } JSObject* interface; - if (constructorClass) { + if (constructorClass || constructor) { interface = CreateInterfaceObject(cx, global, receiver, constructorClass, - proto, staticMethods, constants, name); + constructor, ctorNargs, proto, + staticMethods, constants, name); if (!interface) { return NULL; } @@ -239,5 +253,17 @@ QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) return true; } +JSBool +ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) +{ + return Throw(cx, NS_ERROR_FAILURE); +} + +JSBool +ThrowingConstructorWorkers(JSContext* cx, unsigned argc, JS::Value* vp) +{ + return Throw(cx, NS_ERROR_FAILURE); +} + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 24becb6386b..fca8b546393 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -231,7 +231,14 @@ struct ConstantSpec * This is null if we should not create an interface prototype * object. * constructorClass is the JSClass to use for the interface object. - * This is null if we should not create an interface object. + * This is null if we should not create an interface object or + * if it should be a function object. + * constructor is the JSNative to use as a constructor. If this is non-null, it + * should be used as a JSNative to back the interface object, which + * should be a Function. If this is null, then we should create an + * object of constructorClass, unless that's also null, in which + * case we should not create an interface object at all. + * ctorNargs is the length of the constructor function; 0 if no constructor * methods and properties are to be defined on the interface prototype object; * these arguments are allowed to be null if there are no * methods or properties respectively. @@ -251,7 +258,8 @@ struct ConstantSpec JSObject* CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver, JSObject* protoProto, JSClass* protoClass, - JSClass* constructorClass, JSFunctionSpec* methods, + JSClass* constructorClass, JSNative constructor, + unsigned ctorNargs, JSFunctionSpec* methods, JSPropertySpec* properties, ConstantSpec* constants, JSFunctionSpec* staticMethods, const char* name); @@ -507,6 +515,10 @@ InitIds(JSContext* cx, Spec* specs, jsid* ids) JSBool QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp); +JSBool +ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); +JSBool +ThrowingConstructorWorkers(JSContext* cx, unsigned argc, JS::Value* vp); } // namespace dom } // namespace mozilla diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c03740a85ca..e3e6cb8b8e9 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -156,8 +156,10 @@ class CGInterfaceObjectJSClass(CGThing): # We're purely for internal consumption return "" def define(self): + if not self.descriptor.hasInstanceInterface: + return "" ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME - hasinstance = "NULL" if not self.descriptor.hasInstanceInterface else HASINSTANCE_HOOK_NAME + hasinstance = HASINSTANCE_HOOK_NAME return """ static JSClass InterfaceObjectClass = { "Function", 0, @@ -657,6 +659,12 @@ class PropertyDefiner: str += self.generateArray(self.chrome, self.variableName(True)) return str +# The length of a method is the maximum of the lengths of the +# argument lists of all its overloads. +def methodLength(method): + signatures = method.signatures() + return max([len(arguments) for (retType, arguments) in signatures]) + class MethodDefiner(PropertyDefiner): """ A class for defining methods on a prototype object. @@ -664,12 +672,6 @@ class MethodDefiner(PropertyDefiner): def __init__(self, descriptor, name, static): PropertyDefiner.__init__(self, descriptor, name) - # The length of a method is the maximum of the lengths of the - # argument lists of all its overloads. - def methodLength(method): - signatures = method.signatures() - return max([len(arguments) for (retType, arguments) in signatures]) - methods = [m for m in descriptor.interface.members if m.isMethod() and m.isStatic() == static] self.chrome = [{"name": m.identifier.name, @@ -858,12 +860,25 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): " return NULL;\n" "}") % getParentProto + needInterfaceObjectClass = (needInterfaceObject and + self.descriptor.hasInstanceInterface) + needConstructor = (needInterfaceObject and + not self.descriptor.hasInstanceInterface) + if self.descriptor.interface.ctor(): + constructHook = CONSTRUCT_HOOK_NAME + constructArgs = methodLength(self.descriptor.interface.ctor()) + else: + constructHook = "ThrowingConstructorWorkers" if self.descriptor.workers else "ThrowingConstructor" + constructArgs = 0 + call = CGGeneric(("return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,\n" - " %s, %s,\n" + " %s, %s, %s, %d,\n" " %%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s,\n" " %s);") % ( "&PrototypeClass" if needInterfacePrototypeObject else "NULL", - "&InterfaceObjectClass" if needInterfaceObject else "NULL", + "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL", + constructHook if needConstructor else "NULL", + constructArgs, '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")) if self.properties.hasChromeOnly(): diff --git a/dom/bindings/test/Makefile.in b/dom/bindings/test/Makefile.in index faca493a174..1aed8f050be 100644 --- a/dom/bindings/test/Makefile.in +++ b/dom/bindings/test/Makefile.in @@ -13,6 +13,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = \ test_lookupGetter.html \ + test_InstanceOf.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/dom/bindings/test/test_InstanceOf.html b/dom/bindings/test/test_InstanceOf.html new file mode 100644 index 00000000000..3a5a76b1b21 --- /dev/null +++ b/dom/bindings/test/test_InstanceOf.html @@ -0,0 +1,28 @@ + + + + + + Test for Bug 748983 + + + + +Mozilla Bug 748983 +

+ +
+
+
+ +