diff --git a/js/src/js.msg b/js/src/js.msg index b66de521b2..06915af842 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -593,6 +593,7 @@ MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status") MSG_DEF(JSMSG_NO_DYNAMIC_IMPORT, 0, JSEXN_SYNTAXERR, "dynamic module import is not implemented") MSG_DEF(JSMSG_IMPORT_SCRIPT_NOT_FOUND, 0, JSEXN_TYPEERR, "can't find referencing script for dynamic module import") +MSG_DEF(JSMSG_BAD_MODULE_SPECIFIER, 1, JSEXN_TYPEERR, "error resolving module specifier '{0}'") // Promise MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b675a1b4fc..6c10eba821 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4756,7 +4756,7 @@ JS::SetModulePrivate(JSObject* module, const JS::Value& value) JS_PUBLIC_API(JS::Value) JS::GetModulePrivate(JSObject* module) { - return module->as().scriptSourceObject()->getPrivate(); + return module->as().scriptSourceObject()->canonicalPrivate(); } JS_PUBLIC_API(void) @@ -4768,7 +4768,21 @@ JS::SetScriptPrivate(JSScript* script, const JS::Value& value) JS_PUBLIC_API(JS::Value) JS::GetScriptPrivate(JSScript* script) { - return script->scriptSourceUnwrap().getPrivate(); + return script->scriptSourceUnwrap().canonicalPrivate(); +} + +JS_PUBLIC_API(JS::ScriptPrivateFinalizeHook) +JS::GetScriptPrivateFinalizeHook(JSContext* cx) +{ + AssertHeapIsIdle(cx); + return cx->runtime()->scriptPrivateFinalizeHook; +} + +JS_PUBLIC_API(void) +JS::SetScriptPrivateFinalizeHook(JSContext* cx, JS::ScriptPrivateFinalizeHook func) +{ + AssertHeapIsIdle(cx); + cx->runtime()->scriptPrivateFinalizeHook = func; } JS_PUBLIC_API(bool) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index ad63d9d315..dc3af6bf87 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4327,13 +4327,17 @@ Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options, using ModuleResolveHook = JSObject* (*)(JSContext*, HandleValue, HandleString); /** - * Get the HostResolveImportedModule hook for the runtime. + * Get the HostImportModuleDynamically hook for the runtime. */ extern JS_PUBLIC_API(ModuleResolveHook) GetModuleResolveHook(JSRuntime* rt); /** - * Set the HostResolveImportedModule hook for the runtime to the given function. + * Set the HostImportModuleDynamically hook for the runtime to the given + * function. + * + * If this hook is not set (or set to nullptr) then the JS engine will throw an + * exception if dynamic module import is attempted. */ extern JS_PUBLIC_API(void) SetModuleResolveHook(JSRuntime* rt, ModuleResolveHook func); @@ -4406,6 +4410,26 @@ SetScriptPrivate(JSScript* script, const JS::Value& value); extern JS_PUBLIC_API(JS::Value) GetScriptPrivate(JSScript* script); +/** + * A hook that's called whenever a script or module which has a private value + * set with SetScriptPrivate() or SetModulePrivate() is finalized. This can be + * used to clean up the private state. The private value is passed as an + * argument. + */ +using ScriptPrivateFinalizeHook = void (*)(JSFreeOp*, const JS::Value&); + +/** + * Get the script private finalize hook for the runtime. + */ +extern JS_PUBLIC_API(ScriptPrivateFinalizeHook) +GetScriptPrivateFinalizeHook(JSContext* cx); + +/** + * Set the script private finalize hook for the runtime to the given function. + */ +extern JS_PUBLIC_API(void) +SetScriptPrivateFinalizeHook(JSContext* cx, ScriptPrivateFinalizeHook func); + /* * Perform the ModuleInstantiate operation on the given source text module * record. diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 665a6bf6fb..aad0756f03 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1321,6 +1321,15 @@ js::GetAllocationMetadata(JSObject* obj) return nullptr; } +JS_FRIEND_API(JS::Value) +js::MaybeGetScriptPrivate(JSObject* object) { + if (!object->is()) { + return UndefinedValue(); + } + + return object->as().canonicalPrivate(); +} + JS_FRIEND_API(bool) js::ReportIsNotFunction(JSContext* cx, HandleValue v) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 34ab6ca15e..d1c2289634 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -95,6 +95,22 @@ JS_PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp = nullptr) extern JS_FRIEND_API(bool) JS_IsDeadWrapper(JSObject* obj); +namespace js { + +/** + * Get the script private value associated with an object, if any. + * + * The private value is set with SetScriptPrivate() or SetModulePrivate() and is + * internally stored on the relevant ScriptSourceObject. + * + * This is used by the cycle collector to trace through + * ScriptSourceObjects. This allows private values to contain an nsISupports + * pointer and hence support references to cycle collected C++ objects. + */ +JS_FRIEND_API(JS::Value) MaybeGetScriptPrivate(JSObject* object); + +} // namespace js + /* * Used by the cycle collector to trace through a shape or object group and * all cycle-participating data it reaches, using bounded stack space. diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 1943634b15..fb5d7c79bf 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -641,10 +641,17 @@ class ScriptSourceObject : public NativeObject void setPrivate(const Value& value) { setReservedSlot(PRIVATE_SLOT, value); } + Value getPrivate() const { return getReservedSlot(PRIVATE_SLOT); } + Value canonicalPrivate() const { + Value value = getReservedSlot(PRIVATE_SLOT); + MOZ_ASSERT_IF(!isCanonical(), value.isUndefined()); + return value; + } + private: enum { SOURCE_SLOT = 0, diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index a736ba4f27..364bd45caf 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -3088,7 +3088,7 @@ js::FindScriptOrModulePrivateForScript(JSScript* script) { while (script) { ScriptSourceObject* sso = &script->scriptSourceUnwrap(); - Value value = sso->getPrivate(); + Value value = sso->canonicalPrivate(); if (!value.isUndefined()) { return value; } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index c837769e09..ceb7a498b0 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -245,7 +245,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) js::StackFormat::SpiderMonkey), moduleResolveHook(), moduleMetadataHook(), - moduleDynamicImportHook() + moduleDynamicImportHook(), + scriptPrivateFinalizeHook() { setGCStoreBufferPtr(&gc.storeBuffer); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 7ac332e007..c1c28ca7c6 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1304,6 +1304,9 @@ struct JSRuntime : public JS::shadow::Runtime, // A hook that implements the abstract operation // HostImportModuleDynamically. JS::ModuleDynamicImportHook moduleDynamicImportHook; + + // A hook called on script finalization. + JS::ScriptPrivateFinalizeHook scriptPrivateFinalizeHook; }; namespace js { diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp index 17a989c833..a28b1ed3ba 100644 --- a/xpcom/base/CycleCollectedJSContext.cpp +++ b/xpcom/base/CycleCollectedJSContext.cpp @@ -72,6 +72,7 @@ #include "mozilla/dom/ScriptSettings.h" #include "jsprf.h" #include "js/Debug.h" +#include "jsfriendapi.h" #include "nsContentUtils.h" #include "nsCycleCollectionNoteRootCallback.h" #include "nsCycleCollectionParticipant.h" @@ -642,25 +643,33 @@ CycleCollectedJSContext::NoteGCThingXPCOMChildren(const js::Class* aClasp, } // XXX This test does seem fragile, we should probably whitelist classes // that do hold a strong reference, but that might not be possible. - else if (aClasp->flags & JSCLASS_HAS_PRIVATE && - aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { + if (aClasp->flags & JSCLASS_HAS_PRIVATE && + aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "js::GetObjectPrivate(obj)"); aCb.NoteXPCOMChild(static_cast(js::GetObjectPrivate(aObj))); - } else { - const DOMJSClass* domClass = GetDOMClass(aObj); - if (domClass) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)"); - // It's possible that our object is an unforgeable holder object, in - // which case it doesn't actually have a C++ DOM object associated with - // it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in - // that case, since NoteXPCOMChild/NoteNativeChild are null-safe. - if (domClass->mDOMObjectIsISupports) { - aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject(aObj)); - } else if (domClass->mParticipant) { - aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject(aObj), - domClass->mParticipant); - } - } + return; + } + + const DOMJSClass* domClass = GetDOMClass(aObj); + if (domClass) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)"); + // It's possible that our object is an unforgeable holder object, in + // which case it doesn't actually have a C++ DOM object associated with + // it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in + // that case, since NoteXPCOMChild/NoteNativeChild are null-safe. + if (domClass->mDOMObjectIsISupports) { + aCb.NoteXPCOMChild( + UnwrapPossiblyNotInitializedDOMObject(aObj)); + } else if (domClass->mParticipant) { + aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject(aObj), + domClass->mParticipant); + } + return; + } + + JS::Value value = js::MaybeGetScriptPrivate(aObj); + if (!value.isUndefined()) { + aCb.NoteXPCOMChild(static_cast(value.toPrivate())); } }