зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1278583 part 5. Switch DOM code from using hasInstance class hooks to using Symbol.hasInstance. r=peterv
This commit is contained in:
Родитель
ba5962ed70
Коммит
d7821950f4
|
@ -281,6 +281,7 @@ static const DOMIfaceAndProtoJSClass WindowNamedPropertiesClass = {
|
|||
PROXY_CLASS_DEF("WindowProperties",
|
||||
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS),
|
||||
eNamedPropertiesObject,
|
||||
false,
|
||||
prototypes::id::_ID_Count,
|
||||
0,
|
||||
sWindowNamedPropertiesNativePropertyHooks,
|
||||
|
|
|
@ -744,6 +744,17 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (DOMIfaceAndProtoJSClass::FromJSClass(constructorClass)->wantsInterfaceHasInstance) {
|
||||
JS::Rooted<jsid> hasInstanceId(cx,
|
||||
SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)));
|
||||
if (!JS_DefineFunctionById(cx, constructor, hasInstanceId,
|
||||
InterfaceHasInstance, 1,
|
||||
// Flags match those of Function[Symbol.hasInstance]
|
||||
JSPROP_READONLY | JSPROP_PERMANENT)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (properties) {
|
||||
if (properties->HasStaticMethods() &&
|
||||
!DefinePrefable(cx, constructor, properties->StaticMethods())) {
|
||||
|
@ -1232,7 +1243,15 @@ static JSObject*
|
|||
XrayCreateFunction(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JSNativeWrapper native, unsigned nargs, JS::Handle<jsid> id)
|
||||
{
|
||||
JSFunction* fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
|
||||
JSFunction* fun;
|
||||
if (JSID_IS_STRING(id)) {
|
||||
fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
|
||||
} else {
|
||||
// Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved;
|
||||
// just use an empty name for lack of anything better.
|
||||
fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
|
||||
}
|
||||
|
||||
if (!fun) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1651,6 +1670,26 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
|||
JSPROP_PERMANENT | JSPROP_READONLY,
|
||||
desc, cacheOnHolder);
|
||||
}
|
||||
|
||||
if (id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) &&
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))->
|
||||
wantsInterfaceHasInstance) {
|
||||
cacheOnHolder = true;
|
||||
JSNativeWrapper interfaceHasInstanceWrapper = { InterfaceHasInstance,
|
||||
nullptr };
|
||||
JSObject* funObj = XrayCreateFunction(cx, wrapper,
|
||||
interfaceHasInstanceWrapper, 1, id);
|
||||
if (!funObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
desc.value().setObject(*funObj);
|
||||
desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT);
|
||||
desc.object().set(wrapper);
|
||||
desc.setSetter(nullptr);
|
||||
desc.setGetter(nullptr);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(IsInterfacePrototype(type));
|
||||
|
||||
|
@ -1875,7 +1914,7 @@ const js::ClassOps sBoringInterfaceObjectClassClassOps = {
|
|||
nullptr, /* mayResolve */
|
||||
nullptr, /* finalize */
|
||||
ThrowingConstructor, /* call */
|
||||
InterfaceHasInstance, /* hasInstance */
|
||||
nullptr, /* hasInstance */
|
||||
ThrowingConstructor, /* construct */
|
||||
nullptr, /* trace */
|
||||
};
|
||||
|
@ -2215,24 +2254,69 @@ GlobalObject::GetAsSupports() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<JSObject*> instance,
|
||||
bool* bp)
|
||||
static bool
|
||||
CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args)
|
||||
{
|
||||
const DOMIfaceAndProtoJSClass* clasp =
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
|
||||
JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
|
||||
bool isInstance;
|
||||
if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(isInstance);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
// If the thing we were passed is not an object, return false like
|
||||
// OrdinaryHasInstance does.
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If "this" is not an object, likewise return false (again, like
|
||||
// OrdinaryHasInstance).
|
||||
if (!args.thisv().isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If "this" doesn't have a DOMIfaceAndProtoJSClass, it's not a DOM
|
||||
// constructor, so just fall back to OrdinaryHasInstance. But note that we
|
||||
// should CheckedUnwrap here, because otherwise we won't get the right
|
||||
// answers.
|
||||
JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject()));
|
||||
if (!thisObj) {
|
||||
// Just fall back on the normal thing, in case it still happens to work.
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
const js::Class* thisClass = js::GetObjectClass(thisObj);
|
||||
|
||||
if (!IsDOMIfaceAndProtoClass(thisClass)) {
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
const DOMIfaceAndProtoJSClass* clasp =
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(thisClass);
|
||||
|
||||
// If "this" isn't a DOM constructor or is a constructor for an interface
|
||||
// without a prototype, just fall back to OrdinaryHasInstance.
|
||||
if (clasp->mType != eInterface ||
|
||||
clasp->mPrototypeID == prototypes::id::_ID_Count) {
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
|
||||
const DOMJSClass* domClass =
|
||||
GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
|
||||
|
||||
MOZ_ASSERT(!domClass || clasp->mPrototypeID != prototypes::id::_ID_Count,
|
||||
"Why do we have a hasInstance hook if we don't have a prototype "
|
||||
"ID?");
|
||||
|
||||
if (domClass &&
|
||||
domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
|
||||
*bp = true;
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2242,49 +2326,11 @@ InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
|||
clasp->mDepth, &boolp)) {
|
||||
return false;
|
||||
}
|
||||
*bp = boolp;
|
||||
args.rval().setBoolean(boolp);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> protov(cx);
|
||||
DebugOnly<bool> ok = JS_GetProperty(cx, obj, "prototype", &protov);
|
||||
MOZ_ASSERT(ok, "Someone messed with our prototype property?");
|
||||
|
||||
JS::Rooted<JSObject*> interfacePrototype(cx, &protov.toObject());
|
||||
MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(interfacePrototype)),
|
||||
"Someone messed with our prototype property?");
|
||||
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
if (!JS_GetPrototype(cx, instance, &proto)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (proto) {
|
||||
if (proto == interfacePrototype) {
|
||||
*bp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!JS_GetPrototype(cx, proto, &proto)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*bp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp,
|
||||
bool* bp)
|
||||
{
|
||||
if (!vp.isObject()) {
|
||||
*bp = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> instanceObject(cx, &vp.toObject());
|
||||
return InterfaceHasInstance(cx, obj, instanceObject, bp);
|
||||
return CallOrdinaryHasInstance(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -2677,17 +2677,11 @@ nsresult
|
|||
ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj);
|
||||
|
||||
/**
|
||||
* Used to implement the hasInstance hook of an interface object.
|
||||
*
|
||||
* instance should not be a security wrapper.
|
||||
* Used to implement the Symbol.hasInstance property of an interface object.
|
||||
*/
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<JSObject*> instance,
|
||||
bool* bp);
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp,
|
||||
bool* bp);
|
||||
InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp);
|
||||
|
||||
bool
|
||||
InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
|
||||
JS::Handle<JSObject*> instance,
|
||||
|
|
|
@ -602,6 +602,7 @@ class CGPrototypeJSClass(CGThing):
|
|||
JS_NULL_OBJECT_OPS
|
||||
},
|
||||
${type},
|
||||
false,
|
||||
${prototypeID},
|
||||
${depth},
|
||||
${hooks},
|
||||
|
@ -673,12 +674,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
ctorname = "nullptr"
|
||||
else:
|
||||
ctorname = "ThrowingConstructor"
|
||||
if NeedsGeneratedHasInstance(self.descriptor):
|
||||
hasinstance = HASINSTANCE_HOOK_NAME
|
||||
elif self.descriptor.interface.hasInterfacePrototypeObject():
|
||||
hasinstance = "InterfaceHasInstance"
|
||||
else:
|
||||
hasinstance = "nullptr"
|
||||
needsHasInstance = (
|
||||
not NeedsGeneratedHasInstance(self.descriptor) and
|
||||
self.descriptor.interface.hasInterfacePrototypeObject())
|
||||
|
||||
prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
|
||||
slotCount = "DOM_INTERFACE_SLOTS_BASE"
|
||||
if len(self.descriptor.interface.namedConstructors) > 0:
|
||||
|
@ -687,10 +686,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
(protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
|
||||
forXrays=True)
|
||||
|
||||
if ctorname == "ThrowingConstructor" and hasinstance == "InterfaceHasInstance":
|
||||
if ctorname == "ThrowingConstructor":
|
||||
ret = ""
|
||||
classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
|
||||
elif ctorname == "nullptr" and hasinstance == "nullptr":
|
||||
elif ctorname == "nullptr":
|
||||
ret = ""
|
||||
classOpsPtr = "JS_NULL_CLASS_OPS"
|
||||
else:
|
||||
|
@ -706,14 +705,13 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
nullptr, /* mayResolve */
|
||||
nullptr, /* finalize */
|
||||
${ctorname}, /* call */
|
||||
${hasInstance}, /* hasInstance */
|
||||
nullptr, /* hasInstance */
|
||||
${ctorname}, /* construct */
|
||||
nullptr, /* trace */
|
||||
};
|
||||
|
||||
""",
|
||||
ctorname=ctorname,
|
||||
hasInstance=hasinstance)
|
||||
ctorname=ctorname)
|
||||
classOpsPtr = "&sInterfaceObjectClassOps"
|
||||
|
||||
if self.descriptor.interface.isNamespace():
|
||||
|
@ -744,6 +742,7 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
${objectOps}
|
||||
},
|
||||
eInterface,
|
||||
${needsHasInstance},
|
||||
${prototypeID},
|
||||
${depth},
|
||||
${hooks},
|
||||
|
@ -753,11 +752,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
""",
|
||||
classString=classString,
|
||||
slotCount=slotCount,
|
||||
ctorname=ctorname,
|
||||
hasInstance=hasinstance,
|
||||
classOpsPtr=classOpsPtr,
|
||||
hooks=NativePropertyHooks(self.descriptor),
|
||||
objectOps=objectOps,
|
||||
needsHasInstance=toStringBool(needsHasInstance),
|
||||
prototypeID=prototypeID,
|
||||
depth=depth,
|
||||
toStringResult=toStringResult,
|
||||
|
@ -1765,19 +1763,17 @@ class CGNamedConstructors(CGThing):
|
|||
namedConstructors=namedConstructors)
|
||||
|
||||
|
||||
class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
||||
class CGHasInstanceHook(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
args = [Argument('JSContext*', 'cx'),
|
||||
Argument('JS::Handle<JSObject*>', 'obj'),
|
||||
Argument('JS::MutableHandle<JS::Value>', 'vp'),
|
||||
Argument('bool*', 'bp')]
|
||||
Argument('unsigned', 'argc'),
|
||||
Argument('JS::Value*', 'vp')]
|
||||
assert descriptor.interface.hasInterfaceObject()
|
||||
assert NeedsGeneratedHasInstance(descriptor)
|
||||
CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
|
||||
'bool', args)
|
||||
|
||||
def define(self):
|
||||
if not NeedsGeneratedHasInstance(self.descriptor):
|
||||
return ""
|
||||
return CGAbstractStaticMethod.define(self)
|
||||
|
||||
def definition_body(self):
|
||||
|
@ -1785,12 +1781,13 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
|
||||
def generate_code(self):
|
||||
header = dedent("""
|
||||
if (!vp.isObject()) {
|
||||
*bp = false;
|
||||
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> instance(cx, &vp.toObject());
|
||||
JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
|
||||
""")
|
||||
if self.descriptor.interface.hasInterfacePrototypeObject():
|
||||
return (
|
||||
|
@ -1801,8 +1798,8 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
static_assert(IsBaseOf<nsISupports, ${nativeType}>::value,
|
||||
"HasInstance only works for nsISupports-based classes.");
|
||||
|
||||
bool ok = InterfaceHasInstance(cx, obj, instance, bp);
|
||||
if (!ok || *bp) {
|
||||
bool ok = InterfaceHasInstance(cx, argc, vp);
|
||||
if (!ok || args.rval().toBoolean()) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -1811,7 +1808,7 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
|
||||
js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
|
||||
nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
|
||||
*bp = !!qiResult;
|
||||
args.rval().setBoolean(!!qiResult);
|
||||
return true;
|
||||
""",
|
||||
nativeType=self.descriptor.nativeType,
|
||||
|
@ -1820,16 +1817,16 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
hasInstanceCode = dedent("""
|
||||
|
||||
const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
|
||||
*bp = false;
|
||||
if (!domClass) {
|
||||
// Not a DOM object, so certainly not an instance of this interface
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
""")
|
||||
if self.descriptor.interface.identifier.name == "ChromeWindow":
|
||||
setBp = "*bp = UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow()"
|
||||
setRval = "args.rval().setBoolean(UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow())"
|
||||
else:
|
||||
setBp = "*bp = true"
|
||||
setRval = "args.rval().setBoolean(true)"
|
||||
# Sort interaces implementing self by name so we get stable output.
|
||||
for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
|
||||
key=lambda iface: iface.identifier.name):
|
||||
|
@ -1837,13 +1834,14 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
"""
|
||||
|
||||
if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) {
|
||||
${setBp};
|
||||
${setRval};
|
||||
return true;
|
||||
}
|
||||
""",
|
||||
name=iface.identifier.name,
|
||||
setBp=setBp)
|
||||
hasInstanceCode += "return true;\n"
|
||||
setRval=setRval)
|
||||
hasInstanceCode += ("args.rval().setBoolean(false);\n"
|
||||
"return true;\n")
|
||||
return header + hasInstanceCode
|
||||
|
||||
|
||||
|
@ -2226,6 +2224,20 @@ class MethodDefiner(PropertyDefiner):
|
|||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
if (static and
|
||||
not unforgeable and
|
||||
descriptor.interface.hasInterfaceObject() and
|
||||
NeedsGeneratedHasInstance(descriptor)):
|
||||
self.regular.append({
|
||||
"name": "@@hasInstance",
|
||||
"methodInfo": False,
|
||||
"nativeName": HASINSTANCE_HOOK_NAME,
|
||||
"length": 1,
|
||||
# Flags match those of Function[Symbol.hasInstance]
|
||||
"flags": "JSPROP_READONLY | JSPROP_PERMANENT",
|
||||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
# Generate the keys/values/entries aliases for value iterables.
|
||||
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
if (not static and
|
||||
|
@ -11954,6 +11966,12 @@ class CGDescriptor(CGThing):
|
|||
for m in clearableCachedAttrs(descriptor):
|
||||
cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
|
||||
|
||||
# Need to output our generated hasinstance bits before
|
||||
# PropertyArrays tries to use them.
|
||||
if (descriptor.interface.hasInterfaceObject() and
|
||||
NeedsGeneratedHasInstance(descriptor)):
|
||||
cgThings.append(CGHasInstanceHook(descriptor))
|
||||
|
||||
properties = PropertyArrays(descriptor)
|
||||
cgThings.append(CGGeneric(define=str(properties)))
|
||||
cgThings.append(CGNativeProperties(descriptor, properties))
|
||||
|
@ -11974,7 +11992,6 @@ class CGDescriptor(CGThing):
|
|||
if descriptor.interface.hasInterfaceObject():
|
||||
cgThings.append(CGClassConstructor(descriptor,
|
||||
descriptor.interface.ctor()))
|
||||
cgThings.append(CGClassHasInstanceHook(descriptor))
|
||||
cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
|
||||
cgThings.append(CGNamedConstructors(descriptor))
|
||||
|
||||
|
|
|
@ -386,9 +386,14 @@ struct DOMIfaceAndProtoJSClass
|
|||
|
||||
// Either eInterface, eInterfacePrototype, eGlobalInterfacePrototype or
|
||||
// eNamedPropertiesObject.
|
||||
DOMObjectType mType;
|
||||
DOMObjectType mType; // uint8_t
|
||||
|
||||
const prototypes::ID mPrototypeID;
|
||||
// Boolean indicating whether this object wants a @@hasInstance property
|
||||
// pointing to InterfaceHasInstance defined on it. Only ever true for the
|
||||
// eInterface case.
|
||||
bool wantsInterfaceHasInstance;
|
||||
|
||||
const prototypes::ID mPrototypeID; // uint16_t
|
||||
const uint32_t mDepth;
|
||||
|
||||
const NativePropertyHooks* mNativeHooks;
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
propNames = Object.getOwnPropertyNames(obj);
|
||||
propNames = propNames.concat(Object.getOwnPropertySymbols(obj));
|
||||
for (var propName of propNames) {
|
||||
if (propName == "prototype" && obj == Promise) {
|
||||
// It's not configurable
|
||||
if ((propName == "prototype" ||
|
||||
propName == Symbol.hasInstance) &&
|
||||
obj == Promise) {
|
||||
// They're not configurable.
|
||||
continue;
|
||||
}
|
||||
Object.defineProperty(obj, propName,
|
||||
|
|
Загрузка…
Ссылка в новой задаче