Bug 748983. Fix the instanceof behavior for new bindings in situations where we don't need a custom hasInstance hook. r=peterv

This commit is contained in:
Boris Zbarsky 2012-05-03 00:35:38 -04:00
parent 2299a42041
commit 5b65f56e64
5 changed files with 107 additions and 25 deletions

View File

@ -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<true>(cx, NS_ERROR_FAILURE);
}
JSBool
ThrowingConstructorWorkers(JSContext* cx, unsigned argc, JS::Value* vp)
{
return Throw<false>(cx, NS_ERROR_FAILURE);
}
} // namespace dom
} // namespace mozilla

View File

@ -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

View File

@ -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():

View File

@ -13,6 +13,7 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_lookupGetter.html \
test_InstanceOf.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=748983
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 748983</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=748983">Mozilla Bug 748983</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 748983 **/
ok(document instanceof EventTarget, "document is an event target")
ok(new XMLHttpRequest() instanceof XMLHttpRequest, "instanceof should work on XHR");
</script>
</pre>
</body>
</html>