Bug 1278583 part 5. Switch DOM code from using hasInstance class hooks to using Symbol.hasInstance. r=peterv

This commit is contained in:
Boris Zbarsky 2016-08-03 18:32:07 -07:00
Родитель ba5962ed70
Коммит d7821950f4
6 изменённых файлов: 163 добавлений и 98 удалений

Просмотреть файл

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