Bug 1270746 part 3 - Replace hasInstance JSClass hook on wrapped natives with a Symbol.hasInstance native. r=peterv

Resolve a `Symbol.hasInstance` native function that works like the class hook.

Differential Revision: https://phabricator.services.mozilla.com/D141344
This commit is contained in:
Jan de Mooij 2022-03-20 11:28:03 +00:00
parent b2ea1ec05f
commit 07e7d34e88
4 changed files with 106 additions and 55 deletions

View File

@ -40,63 +40,59 @@ void XPC_WN_NoHelper_Finalize(JS::GCContext* gcx, JSObject* obj);
bool XPC_WN_Helper_Call(JSContext* cx, unsigned argc, JS::Value* vp);
bool XPC_WN_Helper_HasInstance(JSContext* cx, JS::HandleObject obj,
JS::MutableHandleValue valp, bool* bp);
bool XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, JS::Value* vp);
void XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj);
extern const js::ClassExtension XPC_WN_JSClassExtension;
#define XPC_MAKE_CLASS_OPS(_flags) \
{ \
/* addProperty */ \
((_flags)&XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY) ? nullptr \
: ((_flags)&XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
? XPC_WN_MaybeResolvingPropertyStub \
: XPC_WN_CannotModifyPropertyStub, \
\
/* delProperty */ \
((_flags)&XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY) ? nullptr \
: ((_flags)&XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
? XPC_WN_MaybeResolvingDeletePropertyStub \
: XPC_WN_CannotDeletePropertyStub, \
\
/* enumerate */ \
((_flags)&XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
? nullptr /* We will use newEnumerate set below in this case */ \
: XPC_WN_Shared_Enumerate, \
\
/* newEnumerate */ \
((_flags)&XPC_SCRIPTABLE_WANT_NEWENUMERATE) ? XPC_WN_NewEnumerate \
: nullptr, \
\
/* resolve */ /* We have to figure out resolve strategy at call time \
*/ \
XPC_WN_Helper_Resolve, \
\
/* mayResolve */ \
nullptr, \
\
/* finalize */ \
((_flags)&XPC_SCRIPTABLE_WANT_FINALIZE) ? XPC_WN_Helper_Finalize \
: XPC_WN_NoHelper_Finalize, \
\
/* call */ \
((_flags)&XPC_SCRIPTABLE_WANT_CALL) ? XPC_WN_Helper_Call : nullptr, \
\
/* hasInstance */ \
((_flags)&XPC_SCRIPTABLE_WANT_HASINSTANCE) ? XPC_WN_Helper_HasInstance \
: nullptr, \
\
/* construct */ \
((_flags)&XPC_SCRIPTABLE_WANT_CONSTRUCT) ? XPC_WN_Helper_Construct \
: nullptr, \
\
/* trace */ \
((_flags)&XPC_SCRIPTABLE_IS_GLOBAL_OBJECT) ? JS_GlobalObjectTraceHook \
: XPCWrappedNative_Trace, \
#define XPC_MAKE_CLASS_OPS(_flags) \
{ \
/* addProperty */ \
((_flags)&XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY) ? nullptr \
: ((_flags)&XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
? XPC_WN_MaybeResolvingPropertyStub \
: XPC_WN_CannotModifyPropertyStub, \
\
/* delProperty */ \
((_flags)&XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY) ? nullptr \
: ((_flags)&XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) \
? XPC_WN_MaybeResolvingDeletePropertyStub \
: XPC_WN_CannotDeletePropertyStub, \
\
/* enumerate */ \
((_flags)&XPC_SCRIPTABLE_WANT_NEWENUMERATE) \
? nullptr /* We will use newEnumerate set below in this case */ \
: XPC_WN_Shared_Enumerate, \
\
/* newEnumerate */ \
((_flags)&XPC_SCRIPTABLE_WANT_NEWENUMERATE) ? XPC_WN_NewEnumerate \
: nullptr, \
\
/* resolve */ /* We have to figure out resolve strategy at call time \
*/ \
XPC_WN_Helper_Resolve, \
\
/* mayResolve */ \
nullptr, \
\
/* finalize */ \
((_flags)&XPC_SCRIPTABLE_WANT_FINALIZE) ? XPC_WN_Helper_Finalize \
: XPC_WN_NoHelper_Finalize, \
\
/* call */ \
((_flags)&XPC_SCRIPTABLE_WANT_CALL) ? XPC_WN_Helper_Call : nullptr, \
\
/* hasInstance */ \
nullptr, \
\
/* construct */ \
((_flags)&XPC_SCRIPTABLE_WANT_CONSTRUCT) ? XPC_WN_Helper_Construct \
: nullptr, \
\
/* trace */ \
((_flags)&XPC_SCRIPTABLE_IS_GLOBAL_OBJECT) ? JS_GlobalObjectTraceHook \
: XPCWrappedNative_Trace, \
}
#define XPC_MAKE_CLASS(_name, _flags, _classOps) \

View File

@ -753,12 +753,25 @@ bool XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, Value* vp) {
POST_HELPER_STUB
}
bool XPC_WN_Helper_HasInstance(JSContext* cx, HandleObject obj,
MutableHandleValue valp, bool* bp) {
static bool XPC_WN_Helper_HasInstance(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "WrappedNative[Symbol.hasInstance]", 1)) {
return false;
}
if (!args.thisv().isObject()) {
JS_ReportErrorASCII(
cx, "WrappedNative[Symbol.hasInstance]: unexpected this value");
return false;
}
RootedObject obj(cx, &args.thisv().toObject());
RootedValue val(cx, args.get(0));
bool retval2;
PRE_HELPER_STUB
HasInstance(wrapper, cx, obj, valp, &retval2, &retval);
*bp = retval2;
HasInstance(wrapper, cx, obj, val, &retval2, &retval);
args.rval().setBoolean(retval2);
POST_HELPER_STUB
}
@ -795,6 +808,24 @@ bool XPC_WN_Helper_Resolve(JSContext* cx, HandleObject obj, HandleId id,
RootedId old(cx, ccx.SetResolveName(id));
nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
// Resolve a Symbol.hasInstance property if we want custom `instanceof`
// behavior.
if (scr && scr->WantHasInstance() &&
id.isWellKnownSymbol(SymbolCode::hasInstance)) {
mozilla::Maybe<AutoSetResolvingWrapper> asrw;
if (scr->AllowPropModsDuringResolve()) {
asrw.emplace(ccx, wrapper);
}
if (!JS_DefineFunctionById(
cx, obj, id, XPC_WN_Helper_HasInstance, 1,
JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING)) {
rv = NS_ERROR_FAILURE;
} else {
resolved = true;
}
}
if (scr && scr->WantResolve()) {
mozilla::Maybe<AutoSetResolvingWrapper> asrw;
if (scr->AllowPropModsDuringResolve()) {

View File

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Tests for custom `instanceof` behavior via XPC_SCRIPTABLE_WANT_HASINSTANCE.
add_task(function id_instanceof() {
// ID objects are instances of Components.ID.
let id = Components.ID("{f2f5c784-7f6c-43f5-81b0-45ff32c312b1}");
Assert.equal(id instanceof Components.ID, true);
Assert.equal({} instanceof Components.ID, false);
Assert.equal(null instanceof Components.ID, false);
// Components.ID has a Symbol.hasInstance function.
let desc = Object.getOwnPropertyDescriptor(Components.ID, Symbol.hasInstance);
Assert.equal(typeof desc, "object");
Assert.equal(typeof desc.value, "function");
// Test error handling when calling this function with unexpected values.
Assert.throws(() => desc.value.call(null), /At least 1 argument required/);
Assert.throws(() => desc.value.call(null, 1), /unexpected this value/);
Assert.throws(() => desc.value.call({}, {}), /NS_ERROR_XPC_BAD_OP_ON_WN_PROTO/);
});

View File

@ -142,6 +142,7 @@ head = head_watchdog.js
[test_watchdog_hibernate.js]
head = head_watchdog.js
[test_weak_keys.js]
[test_xpcwn_instanceof.js]
[test_xpcwn_tamperproof.js]
[test_xrayed_arguments.js]
[test_xrayed_iterator.js]