From f3c6da59876d4dfcb9a57bf6b022de40553cdd37 Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Sun, 10 May 2026 19:17:00 -0400 Subject: [PATCH] Fix WeakRef constructor realm prototype --- js/src/builtin/WeakRefObject.cpp | 27 +++++++++++- js/xpconnect/tests/unit/test_weakref.js | 58 +++++++++++++++++++++++++ js/xpconnect/tests/unit/xpcshell.ini | 1 + 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 js/xpconnect/tests/unit/test_weakref.js diff --git a/js/src/builtin/WeakRefObject.cpp b/js/src/builtin/WeakRefObject.cpp index ed92f547d7..ee162836c1 100644 --- a/js/src/builtin/WeakRefObject.cpp +++ b/js/src/builtin/WeakRefObject.cpp @@ -13,6 +13,7 @@ #include "vm/GlobalObject.h" #include "vm/Symbol.h" +#include "jswrapper.h" #include "jsobjinlines.h" #include "vm/Interpreter-inl.h" @@ -32,6 +33,30 @@ IsWeakRef(HandleValue v) return v.isObject() && v.toObject().is(); } +static bool +GetPrototypeFromWeakRefConstructor(JSContext* cx, HandleObject newTarget, + MutableHandleObject proto) +{ + if (!GetPrototypeFromConstructor(cx, newTarget, proto)) + return false; + if (proto) + return true; + + RootedObject realmObject(cx, CheckedUnwrap(newTarget, /* stopAtWindowProxy = */ false)); + if (!realmObject) + return false; + + { + JSAutoCompartment ac(cx, realmObject); + Rooted global(cx, &realmObject->global()); + if (!GlobalObject::ensureConstructor(cx, global, JSProto_WeakRef)) + return false; + proto.set(&global->getPrototype(JSProto_WeakRef).toObject()); + } + + return cx->compartment()->wrap(cx, proto); +} + static bool WeakRef_deref_impl(JSContext* cx, const CallArgs& args) { @@ -155,7 +180,7 @@ WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp) RootedObject proto(cx); RootedObject newTarget(cx, &args.newTarget().toObject()); - if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + if (!GetPrototypeFromWeakRefConstructor(cx, newTarget, &proto)) return false; Rooted obj(cx, WeakRefObject::create(cx, target, proto)); diff --git a/js/xpconnect/tests/unit/test_weakref.js b/js/xpconnect/tests/unit/test_weakref.js new file mode 100644 index 0000000000..6533a3d425 --- /dev/null +++ b/js/xpconnect/tests/unit/test_weakref.js @@ -0,0 +1,58 @@ +/* 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/. */ + +function assertThrowsTypeError(fn) { + try { + fn(); + } catch (e) { + do_check_true(e instanceof TypeError); + return; + } + do_throw("expected TypeError"); +} + +add_task(function* test_weakref_api_surface() { + do_check_eq(typeof WeakRef, "function"); + do_check_eq(WeakRef.name, "WeakRef"); + do_check_eq(WeakRef.length, 1); + + assertThrowsTypeError(() => WeakRef({})); + assertThrowsTypeError(() => new WeakRef(1)); + assertThrowsTypeError(() => new WeakRef(Symbol.for("registered"))); + + let target = {}; + let ref = new WeakRef(target); + do_check_eq(Object.prototype.toString.call(ref), "[object WeakRef]"); + do_check_eq(ref.deref(), target); + do_check_eq(typeof WeakRef.prototype.deref, "function"); + do_check_eq(WeakRef.prototype.deref.length, 0); + + let desc = Object.getOwnPropertyDescriptor(WeakRef.prototype, + Symbol.toStringTag); + do_check_eq(desc.value, "WeakRef"); + do_check_eq(desc.writable, false); + do_check_eq(desc.enumerable, false); + do_check_eq(desc.configurable, true); +}); + +add_task(function* test_newtarget_prototype_is_not_object() { + function newTarget() {} + + for (let proto of [undefined, null, true, "", Symbol(), 1]) { + newTarget.prototype = proto; + let ref = Reflect.construct(WeakRef, [{}], newTarget); + do_check_true(Object.getPrototypeOf(ref) === WeakRef.prototype); + } +}); + +add_task(function* test_proto_from_constructor_realm() { + let other = new Components.utils.Sandbox("http://example.com", { freshZone: true }); + Components.utils.evalInSandbox("var newTarget = new Function();", other); + + for (let proto of [undefined, null, true, "", Symbol(), 1]) { + other.newTarget.prototype = proto; + let ref = Reflect.construct(WeakRef, [{}], other.newTarget); + do_check_true(Object.getPrototypeOf(ref) === other.WeakRef.prototype); + } +}); diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 2a1fb6760d..f51403cdc2 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -73,6 +73,7 @@ support-files = [test_isModuleLoaded.js] [test_finalization_registry.js] [test_js_weak_references.js] +[test_weakref.js] [test_onGarbageCollection-01.js] head = head_ongc.js [test_onGarbageCollection-02.js]