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"))) .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams"))) .setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
.setWeakRefs(GetWorkerPref<bool>(NS_LITERAL_CSTRING("weakrefs")))
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
.setArrayProtoValues(GetWorkerPref<bool>( .setArrayProtoValues(GetWorkerPref<bool>(
NS_LITERAL_CSTRING("array_prototype_values"))); 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.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED) WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_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("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED) WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK) 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) if (!obj)
return nullptr; return nullptr;
Referent* data = cx->new_<Referent>(target); Referent* data = cx->new_<Referent>(target, cx->options().weakRefs());
if (!data) if (!data)
return nullptr; return nullptr;
@ -146,14 +146,18 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
if (!target) if (!target)
return; return;
// Weak edges must be tenured; fall back to a strong trace while the // When pref-disabled, keep referent alive via strong trace so deref()
// referent is still in the nursery to avoid crashing the GC. // stays usable as a stub without touching GC internals.
if (IsInsideNursery(target)) 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"); TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef nursery referent");
else } else {
TraceWeakEdge(trc, &data->target, "WeakRef referent"); TraceWeakEdge(trc, &data->target, "WeakRef referent");
} }
} }
}
/* static */ void /* static */ void
WeakRefObject::finalize(FreeOp* fop, JSObject* obj) WeakRefObject::finalize(FreeOp* fop, JSObject* obj)

View file

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

View file

@ -998,7 +998,9 @@ class JS_PUBLIC_API(ContextOptions) {
werror_(false), werror_(false),
strictMode_(false), strictMode_(false),
extraWarnings_(false), extraWarnings_(false),
arrayProtoValues_(true) arrayProtoValues_(true),
streams_(true),
weakRefs_(false)
{ {
} }
@ -1138,6 +1140,16 @@ class JS_PUBLIC_API(ContextOptions) {
return *this; return *this;
} }
bool weakRefs() const { return weakRefs_; }
ContextOptions& setWeakRefs(bool flag) {
weakRefs_ = flag;
return *this;
}
ContextOptions& toggleWeakRefs() {
weakRefs_ = !weakRefs_;
return *this;
}
private: private:
bool baseline_ : 1; bool baseline_ : 1;
bool ion_ : 1; bool ion_ : 1;
@ -1155,6 +1167,7 @@ class JS_PUBLIC_API(ContextOptions) {
bool extraWarnings_ : 1; bool extraWarnings_ : 1;
bool arrayProtoValues_ : 1; bool arrayProtoValues_ : 1;
bool streams_ : 1; bool streams_ : 1;
bool weakRefs_ : 1;
}; };
JS_PUBLIC_API(ContextOptions&) 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 extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams"); 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"); bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects");
@ -1472,7 +1473,8 @@ ReloadPrefsCallback(const char* pref, void* data)
.setWerror(werror) .setWerror(werror)
.setExtraWarnings(extraWarnings) .setExtraWarnings(extraWarnings)
.setArrayProtoValues(arrayProtoValues) .setArrayProtoValues(arrayProtoValues)
.setStreams(streams); .setStreams(streams)
.setWeakRefs(weakRefs);
JS_SetParallelParsingEnabled(cx, parallelParsing); JS_SetParallelParsingEnabled(cx, parallelParsing);
JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation); JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);

View file

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