Issue MoonchildProductions/UXP#2306: Add pref-gated WeakRef with safe stub when disabled

* Add javascript.options.weakrefs and plumb it through context options, XPConnect, and workers
* Keep referents alive via strong tracing when the pref is off so deref() still returns the target
* Retain weak-edge semantics when the pref is enabled
This commit is contained in:
Basilisk-Dev 2025-12-17 16:35:41 -05:00 • committed by wuggy
commit 3cf82e2ca1
7 changed files with 32 additions and 8 deletions

View file

@ -301,6 +301,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
.setWeakRefs(GetWorkerPref<bool>(NS_LITERAL_CSTRING("weakrefs")))
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
.setArrayProtoValues(GetWorkerPref<bool>(
NS_LITERAL_CSTRING("array_prototype_values")));

View file

@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, O
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
WORKER_SIMPLE_PREF("javascript.options.weakrefs", WeakRefsEnabled, WEAKREFS_ENABLED)
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)

View file

@ -89,7 +89,7 @@ WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /*
if (!obj)
return nullptr;
Referent* data = cx->new_<Referent>(target);
Referent* data = cx->new_<Referent>(target, cx->options().weakRefs());
if (!data)
return nullptr;
@ -146,12 +146,16 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
if (!target)
return;
// Weak edges must be tenured; fall back to a strong trace while the
// referent is still in the nursery to avoid crashing the GC.
if (IsInsideNursery(target))
// When pref-disabled, keep referent alive via strong trace so deref()
// stays usable as a stub without touching GC internals.
if (!data->enabled) {
TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef stub referent");
} else if (IsInsideNursery(target)) {
// Weak edges must be tenured; trace strongly while referent is in the nursery.
TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef nursery referent");
else
} else {
TraceWeakEdge(trc, &data->target, "WeakRef referent");
}
}
}

View file

@ -15,8 +15,10 @@ class WeakRefObject : public NativeObject
{
public:
struct Referent {
explicit Referent(JSObject* obj) : target(obj) {}
explicit Referent(JSObject* obj, bool enabled)
: target(obj), enabled(enabled) {}
WeakRef<JSObject*> target;
bool enabled;
};
static const Class class_;

View file

@ -998,7 +998,9 @@ class JS_PUBLIC_API(ContextOptions) {
werror_(false),
strictMode_(false),
extraWarnings_(false),
arrayProtoValues_(true)
arrayProtoValues_(true),
streams_(true),
weakRefs_(false)
{
}
@ -1138,6 +1140,16 @@ class JS_PUBLIC_API(ContextOptions) {
return *this;
}
bool weakRefs() const { return weakRefs_; }
ContextOptions& setWeakRefs(bool flag) {
weakRefs_ = flag;
return *this;
}
ContextOptions& toggleWeakRefs() {
weakRefs_ = !weakRefs_;
return *this;
}
private:
bool baseline_ : 1;
bool ion_ : 1;
@ -1155,6 +1167,7 @@ class JS_PUBLIC_API(ContextOptions) {
bool extraWarnings_ : 1;
bool arrayProtoValues_ : 1;
bool streams_ : 1;
bool weakRefs_ : 1;
};
JS_PUBLIC_API(ContextOptions&)

View file

@ -1446,6 +1446,7 @@ ReloadPrefsCallback(const char* pref, void* data)
bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
bool weakRefs = Preferences::GetBool(JS_OPTIONS_DOT_STR "weakrefs");
bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects");
@ -1472,7 +1473,8 @@ ReloadPrefsCallback(const char* pref, void* data)
.setWerror(werror)
.setExtraWarnings(extraWarnings)
.setArrayProtoValues(arrayProtoValues)
.setStreams(streams);
.setStreams(streams)
.setWeakRefs(weakRefs);
JS_SetParallelParsingEnabled(cx, parallelParsing);
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);

View file

@ -1347,6 +1347,7 @@ pref("javascript.options.dynamicImport", true);
// Streams API
pref("javascript.options.streams", true);
pref("javascript.options.weakrefs", false);
// advanced prefs
pref("advanced.mailftp", false);