From f2854c5a537913ed56036bb5297665054e02debe Mon Sep 17 00:00:00 2001 From: roytam1 Date: Wed, 20 May 2026 10:22:32 +0800 Subject: [PATCH] ported from UXP: Make WeakRef support always enabled (6861bedf) --- dom/workers/RuntimeService.cpp | 1 - dom/workers/WorkerPrefs.h | 1 - js/src/builtin/WeakRefObject.cpp | 95 ++++++++++++++++++-------- js/src/builtin/WeakRefObject.h | 25 +++++-- js/src/js.msg | 1 + js/src/jsapi-tests/testGCWeakRef.cpp | 60 +++++++++++++++- js/src/jsapi.cpp | 9 +++ js/src/jsapi.h | 15 ++-- js/src/shell/js.cpp | 5 +- js/src/tests/manual/weakref-smoke.js | 12 ++-- js/src/vm/Runtime.cpp | 58 ++++++++++++++++ js/src/vm/Runtime.h | 7 ++ js/xpconnect/src/XPCJSContext.cpp | 4 +- modules/libpref/init/all.js | 3 - xpcom/base/CycleCollectedJSContext.cpp | 4 ++ 15 files changed, 244 insertions(+), 56 deletions(-) diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index c10acd855..d6f5b55a1 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -303,7 +303,6 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */) .setAsyncStack(GetWorkerPref(NS_LITERAL_CSTRING("asyncstack"))) .setWerror(GetWorkerPref(NS_LITERAL_CSTRING("werror"))) .setStreams(GetWorkerPref(NS_LITERAL_CSTRING("streams"))) - .setWeakRefs(GetWorkerPref(NS_LITERAL_CSTRING("weakrefs"))) .setExtraWarnings(GetWorkerPref(NS_LITERAL_CSTRING("strict"))) .setArrayProtoValues(GetWorkerPref( NS_LITERAL_CSTRING("array_prototype_values"))); diff --git a/dom/workers/WorkerPrefs.h b/dom/workers/WorkerPrefs.h index 67e2b87c8..c37c8ff36 100644 --- a/dom/workers/WorkerPrefs.h +++ b/dom/workers/WorkerPrefs.h @@ -36,7 +36,6 @@ 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) diff --git a/js/src/builtin/WeakRefObject.cpp b/js/src/builtin/WeakRefObject.cpp index 856002dbb..b929c65f5 100644 --- a/js/src/builtin/WeakRefObject.cpp +++ b/js/src/builtin/WeakRefObject.cpp @@ -11,6 +11,7 @@ #include "gc/Nursery.h" #include "gc/Tracer.h" #include "vm/GlobalObject.h" +#include "vm/Symbol.h" #include "jsobjinlines.h" @@ -37,11 +38,34 @@ WeakRef_deref_impl(JSContext* cx, const CallArgs& args) MOZ_ASSERT(IsWeakRef(args.thisv())); WeakRefObject::Referent* data = GetReferent(&args.thisv().toObject()); - JSObject* target = data ? data->target.get() : nullptr; - if (target) - args.rval().setObject(*target); - else + if (!data) { args.rval().setUndefined(); + return true; + } + + if (data->isObject()) { + JSObject* target = data->objectTarget.get(); + if (target) { + RootedValue kept(cx, ObjectValue(*target)); + if (!cx->runtime()->addWeakRefKeptObject(cx, kept)) + return false; + args.rval().set(kept); + } else { + args.rval().setUndefined(); + } + } else { + MOZ_ASSERT(data->isSymbol()); + JS::Symbol* target = data->symbolTarget.get(); + if (target) { + RootedValue kept(cx, SymbolValue(target)); + if (!cx->runtime()->addWeakRefKeptObject(cx, kept)) + return false; + args.rval().set(kept); + } else { + args.rval().setUndefined(); + } + } + return true; } @@ -83,13 +107,18 @@ InitWeakRefClass(JSContext* cx, HandleObject obj, bool defineMembers) } /* static */ WeakRefObject* -WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /* = nullptr */) +WeakRefObject::create(JSContext* cx, HandleValue target, HandleObject proto /* = nullptr */) { Rooted obj(cx, NewObjectWithClassProto(cx, proto)); if (!obj) return nullptr; - Referent* data = cx->new_(target, cx->options().weakRefs()); + Referent* data; + if (target.isObject()) + data = cx->new_(&target.toObject()); + else + data = cx->new_(target.toSymbol()); + if (!data) return nullptr; @@ -97,6 +126,18 @@ WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /* return obj; } +static bool +CanBeHeldWeakly(HandleValue target) +{ + if (target.isObject()) + return true; + + if (target.isSymbol()) + return target.toSymbol()->code() != JS::SymbolCode::InSymbolRegistry; + + return false; +} + /* static */ bool WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp) { @@ -105,18 +146,12 @@ WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp) if (!ThrowIfNotConstructing(cx, args, "WeakRef")) return false; - if (!args.get(0).isObject()) { - UniqueChars bytes = - DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr); - if (!bytes) - return false; - - JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, - bytes.get()); + if (!CanBeHeldWeakly(args.get(0))) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_WEAKREF_TARGET); return false; } - RootedObject target(cx, &args[0].toObject()); + RootedValue target(cx, args[0]); RootedObject proto(cx); RootedObject newTarget(cx, &args.newTarget().toObject()); @@ -127,6 +162,9 @@ WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp) if (!obj) return false; + if (!cx->runtime()->addWeakRefKeptObject(cx, target)) + return false; + args.rval().setObject(*obj); return true; } @@ -142,19 +180,23 @@ WeakRefObject::deref(JSContext* cx, unsigned argc, Value* vp) WeakRefObject::trace(JSTracer* trc, JSObject* obj) { if (Referent* data = GetReferent(obj)) { - JSObject* target = data->target.unbarrieredGet(); - if (!target) - return; + if (data->isObject()) { + JSObject* target = data->objectTarget.unbarrieredGet(); + if (!target) + return; - // 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"); + if (IsInsideNursery(target)) { + // Weak edges must be tenured; trace strongly while referent is in the nursery. + TraceManuallyBarrieredEdge(trc, data->objectTarget.unsafeGet(), + "WeakRef nursery referent"); + } else { + TraceWeakEdge(trc, &data->objectTarget, "WeakRef referent"); + } } else { - TraceWeakEdge(trc, &data->target, "WeakRef referent"); + MOZ_ASSERT(data->isSymbol()); + JS::Symbol* target = data->symbolTarget.unbarrieredGet(); + if (target) + TraceWeakEdge(trc, &data->symbolTarget, "WeakRef symbol referent"); } } } @@ -162,7 +204,6 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj) /* static */ void WeakRefObject::finalize(FreeOp* fop, JSObject* obj) { - MOZ_ASSERT(!fop->maybeOffMainThread()); if (Referent* data = GetReferent(obj)) fop->delete_(data); } diff --git a/js/src/builtin/WeakRefObject.h b/js/src/builtin/WeakRefObject.h index 8507b82fb..385fcfacf 100644 --- a/js/src/builtin/WeakRefObject.h +++ b/js/src/builtin/WeakRefObject.h @@ -15,16 +15,28 @@ class WeakRefObject : public NativeObject { public: struct Referent { - explicit Referent(JSObject* obj, bool enabled) - : target(obj), enabled(enabled) {} - WeakRef target; - bool enabled; + enum class Kind { + Object, + Symbol + }; + + explicit Referent(JSObject* obj) + : kind(Kind::Object), objectTarget(obj), symbolTarget(nullptr) {} + explicit Referent(JS::Symbol* sym) + : kind(Kind::Symbol), objectTarget(nullptr), symbolTarget(sym) {} + + bool isObject() const { return kind == Kind::Object; } + bool isSymbol() const { return kind == Kind::Symbol; } + + Kind kind; + WeakRef objectTarget; + WeakRef symbolTarget; }; static const Class class_; static JSObject* initClass(JSContext* cx, HandleObject obj); - static WeakRefObject* create(JSContext* cx, HandleObject target, HandleObject proto = nullptr); + static WeakRefObject* create(JSContext* cx, HandleValue target, HandleObject proto = nullptr); static void trace(JSTracer* trc, JSObject* obj); static void finalize(FreeOp* fop, JSObject* obj); @@ -37,7 +49,8 @@ class WeakRefObject : public NativeObject WeakRef& target() { MOZ_ASSERT(getData()); - return getData()->target; + MOZ_ASSERT(getData()->isObject()); + return getData()->objectTarget; } static const JSPropertySpec properties[]; diff --git a/js/src/js.msg b/js/src/js.msg index 20d6aec35..43d35ad39 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -80,6 +80,7 @@ MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 0, JSEXN_TYPEERR, "reduce of empty array MSG_DEF(JSMSG_UNEXPECTED_TYPE, 2, JSEXN_TYPEERR, "{0} is {1}") MSG_DEF(JSMSG_MISSING_FUN_ARG, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}") MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 1, JSEXN_TYPEERR, "{0} is not a non-null object") +MSG_DEF(JSMSG_NOT_WEAKREF_TARGET, 0, JSEXN_TYPEERR, "WeakRef target must be an object or a non-registered symbol") MSG_DEF(JSMSG_SET_NON_OBJECT_RECEIVER, 1, JSEXN_TYPEERR, "can't assign to properties of {0}: not an object") MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified") MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0}: Object is not extensible") diff --git a/js/src/jsapi-tests/testGCWeakRef.cpp b/js/src/jsapi-tests/testGCWeakRef.cpp index d658106b2..31e59a2ba 100644 --- a/js/src/jsapi-tests/testGCWeakRef.cpp +++ b/js/src/jsapi-tests/testGCWeakRef.cpp @@ -22,6 +22,64 @@ struct MyHeap BEGIN_TEST(testGCWeakRef) { + CHECK(cx->options().weakRefs()); + cx->options().setWeakRefs(false); + CHECK(cx->options().weakRefs()); + + JS::RootedValue v(cx); + EXEC("var weakRefTarget = { x: 42 };\n" + "var weakRef = new WeakRef(weakRefTarget);\n" + "weakRefTarget = null;\n"); + + // The constructor keeps the target alive until the host clears kept + // objects. This must remain true even after callers try the old disabled + // option path above. + JS_GC(cx); + JS_GC(cx); + EVAL("weakRef.deref()", &v); + CHECK(v.isObject()); + + JS::ClearWeakRefKeptObjects(cx); + v = JS::UndefinedValue(); + JS_GC(cx); + JS_GC(cx); + EVAL("weakRef.deref()", &v); + CHECK(v.isUndefined()); + + EXEC("var weakRefSymbol = Symbol('weak-ref-symbol');\n" + "var symbolRef = new WeakRef(weakRefSymbol);\n" + "if (symbolRef.deref() !== weakRefSymbol)\n" + " throw new Error('WeakRef must accept unique symbols');\n" + "var registeredSymbolRejected = false;\n" + "try {\n" + " new WeakRef(Symbol.for('weak-ref-symbol'));\n" + "} catch (e) {\n" + " registeredSymbolRejected = e instanceof TypeError;\n" + "}\n" + "if (!registeredSymbolRejected)\n" + " throw new Error('WeakRef must reject registered symbols');\n"); + + EXEC("var keptTarget = { y: 7 };\n" + "var keptRef = new WeakRef(keptTarget);\n"); + JS::ClearWeakRefKeptObjects(cx); + EXEC("var keptResult = keptRef.deref();\n" + "if (keptResult !== keptTarget)\n" + " throw new Error('WeakRef deref must return the target');\n" + "keptTarget = null;\n" + "keptResult = null;\n"); + + JS_GC(cx); + JS_GC(cx); + EVAL("keptRef.deref()", &v); + CHECK(v.isObject()); + + JS::ClearWeakRefKeptObjects(cx); + v = JS::UndefinedValue(); + JS_GC(cx); + JS_GC(cx); + EVAL("keptRef.deref()", &v); + CHECK(v.isUndefined()); + // Create an object and add a property to it so that we can read the // property back later to verify that object internals are not garbage. JS::RootedObject obj(cx, JS_NewPlainObject(cx)); @@ -39,7 +97,6 @@ BEGIN_TEST(testGCWeakRef) // references. CHECK(heap.get().weak.unbarrieredGet() != nullptr); obj = heap.get().weak; - JS::RootedValue v(cx); CHECK(JS_GetProperty(cx, obj, "x", &v)); CHECK(v.isInt32()); CHECK(v.toInt32() == 42); @@ -62,4 +119,3 @@ BEGIN_TEST(testGCWeakRef) return true; } END_TEST(testGCWeakRef) - diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 05e6f7cae..7313747b3 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -629,6 +629,15 @@ JS::InitSelfHostedCode(JSContext* cx) return true; } +JS_PUBLIC_API(void) +JS::ClearWeakRefKeptObjects(JSContext* cx) +{ + MOZ_ASSERT(cx); + MOZ_ASSERT(!cx->runtime()->isHeapBusy()); + + cx->runtime()->clearWeakRefKeptObjects(); +} + JS_PUBLIC_API(const char*) JS_GetImplementationVersion(void) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 5c0da9f44..ba0a9ff84 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1012,7 +1012,7 @@ class JS_PUBLIC_API(ContextOptions) { strictMode_(false), extraWarnings_(false), arrayProtoValues_(true), - weakRefs_(false), + streams_(false), #ifdef NIGHTLY_BUILD forEachStatement_(false) #else @@ -1168,13 +1168,12 @@ class JS_PUBLIC_API(ContextOptions) { return *this; } - bool weakRefs() const { return weakRefs_; } + bool weakRefs() const { return true; } ContextOptions& setWeakRefs(bool flag) { - weakRefs_ = flag; + (void) flag; return *this; } ContextOptions& toggleWeakRefs() { - weakRefs_ = !weakRefs_; return *this; } @@ -1196,7 +1195,6 @@ class JS_PUBLIC_API(ContextOptions) { bool extraWarnings_ : 1; bool arrayProtoValues_ : 1; bool streams_ : 1; - bool weakRefs_ : 1; bool forEachStatement_: 1; }; @@ -1218,6 +1216,13 @@ InitSelfHostedCode(JSContext* cx); JS_PUBLIC_API(void) AssertObjectBelongsToCurrentThread(JSObject* obj); +/** + * Clear objects and symbols that WeakRef.prototype.deref kept alive for the + * current synchronous JavaScript execution. + */ +JS_PUBLIC_API(void) +ClearWeakRefKeptObjects(JSContext* cx); + } /* namespace JS */ extern JS_PUBLIC_API(const char*) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 0c2e14668..bc657078b 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -677,7 +677,9 @@ RunFile(JSContext* cx, const char* filename, FILE* file, bool compileOnly) AnalyzeEntrainedVariables(cx, script); #endif if (!compileOnly) { - if (!JS_ExecuteScript(cx, script)) + bool ok = JS_ExecuteScript(cx, script); + JS::ClearWeakRefKeptObjects(cx); + if (!ok) return false; int64_t t2 = PRMJ_Now() - t1; if (printTiming) @@ -860,6 +862,7 @@ DrainJobQueue(JSContext* cx) } sc->jobQueue.clear(); sc->drainingJobQueue = false; + JS::ClearWeakRefKeptObjects(cx); // It's possible a job added an async task, and it's also possible // that task has already finished. diff --git a/js/src/tests/manual/weakref-smoke.js b/js/src/tests/manual/weakref-smoke.js index da959467b..e6dfa9b82 100644 --- a/js/src/tests/manual/weakref-smoke.js +++ b/js/src/tests/manual/weakref-smoke.js @@ -1,15 +1,13 @@ // Manual WeakRef smoke tests for browser console use. // // Usage: -// 1) Optionally flip `javascript.options.weakrefs` in about:config and reload. -// 2) Paste this file into the browser console (or load via file://) and run: +// 1) Paste this file into the browser console (or load via file://) and run: // runWeakRefManual(); -// 3) Inspect the returned array of results; no throws or crashes are expected. +// 2) Inspect the returned array of results; no throws or crashes are expected. // // Notes: -// - When the pref is ON, deref() should return the target. -// - When the pref is OFF, the stub traces strongly so deref() should also -// return the target. +// - WeakRef is always enabled. Once all strong references are gone, a full GC +// may clear the referent and make deref() return undefined. function runWeakRefManual() { const results = []; @@ -21,7 +19,7 @@ function runWeakRefManual() { log("initial deref tag", ref.deref()?.tag); log("repeat deref identity", ref.deref() === target); - // Clear the strong reference; the WeakRef should still be able to return it. + // Clear the strong reference. A future full GC may clear the WeakRef. target = null; const afterClear = ref.deref(); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 85a068d5c..9e30a8186 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -196,6 +196,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) simulator_(nullptr), #endif scriptAndCountsVector(nullptr), + weakRefKeptObjects(nullptr), lcovOutput(), NaNValue(DoubleNaNValue()), negativeInfinityValue(DoubleValue(NegativeInfinity())), @@ -375,6 +376,8 @@ JSRuntime::destroyRuntime() MOZ_ASSERT(!isHeapBusy()); MOZ_ASSERT(childRuntimeCount == 0); + clearWeakRefKeptObjects(); + fx.destroyInstance(); sharedIntlData.destroyInstance(); @@ -476,6 +479,61 @@ JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback rt->telemetryCallback = callback; } +static bool +SameWeakRefKeptObject(const JS::Value& kept, JS::HandleValue target) +{ + MOZ_ASSERT(kept.isObject() || kept.isSymbol()); + MOZ_ASSERT(target.isObject() || target.isSymbol()); + + if (kept.isObject()) + return target.isObject() && &kept.toObject() == &target.toObject(); + + return target.isSymbol() && kept.toSymbol() == target.toSymbol(); +} + +bool +JSRuntime::addWeakRefKeptObject(JSContext* cx, JS::HandleValue target) +{ + MOZ_ASSERT(cx->runtime() == this); + MOZ_ASSERT(target.isObject() || target.isSymbol()); + MOZ_ASSERT(!isHeapBusy()); + + if (!weakRefKeptObjects) { + auto* keptObjects = + cx->new_>( + cx, js::WeakRefKeptObjectVector(js::SystemAllocPolicy())); + if (!keptObjects) + return false; + + weakRefKeptObjects = keptObjects; + } + + for (size_t i = 0; i < weakRefKeptObjects->length(); i++) { + const JS::Value& kept = (*weakRefKeptObjects)[i]; + if (SameWeakRefKeptObject(kept, target)) + return true; + } + + if (!weakRefKeptObjects->append(target.get())) { + ReportOutOfMemory(cx); + return false; + } + + return true; +} + +void +JSRuntime::clearWeakRefKeptObjects() +{ + MOZ_ASSERT(!isHeapBusy()); + + if (!weakRefKeptObjects) + return; + + defaultFreeOp()->delete_(weakRefKeptObjects); + weakRefKeptObjects = nullptr; +} + void JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes) { diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 3e8cfd349..fe16597bf 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -361,6 +361,7 @@ class PerThreadData }; using ScriptAndCountsVector = GCVector; +using WeakRefKeptObjectVector = JS::GCVector; class AutoLockForExclusiveAccess; } // namespace js @@ -901,6 +902,12 @@ struct JSRuntime : public JS::shadow::Runtime, /* Strong references on scripts held for PCCount profiling API. */ JS::PersistentRooted* scriptAndCountsVector; + /* Strong references to live WeakRef targets kept until the next job boundary. */ + JS::PersistentRooted* weakRefKeptObjects; + + [[nodiscard]] bool addWeakRefKeptObject(JSContext* cx, JS::HandleValue target); + void clearWeakRefKeptObjects(); + /* Code coverage output. */ js::coverage::LCovRuntime lcovOutput; diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index e10501fec..18cc4ea68 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -1510,7 +1510,6 @@ 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 inlining = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.inlining"); @@ -1535,8 +1534,7 @@ ReloadPrefsCallback(const char* pref, void* data) .setWerror(werror) .setExtraWarnings(extraWarnings) .setArrayProtoValues(arrayProtoValues) - .setStreams(streams) - .setWeakRefs(weakRefs); + .setStreams(streams); JS_SetParallelParsingEnabled(cx, parallelParsing); JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e8a2f4f82..e394f5afc 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1411,9 +1411,6 @@ pref("javascript.options.dynamicImport", true); // Streams API pref("javascript.options.streams", true); -// Enable garbage collection of weakrefed objects -pref("javascript.options.weakrefs", false); - // advanced prefs pref("advanced.mailftp", false); pref("image.animation_mode", "normal"); diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index 97256ff2f..afdea406f 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -1506,6 +1506,8 @@ CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth) // Step 4.2 Execute any events that were waiting for a stable state. ProcessStableStateQueue(); + + JS::ClearWeakRefKeptObjects(mJSContext); } void @@ -1544,6 +1546,8 @@ CycleCollectedJSContext::AfterProcessMicrotasks() }); RunInStableState(cleanupRunnable.forget()); }; + + JS::ClearWeakRefKeptObjects(mJSContext); } uint32_t