Issue #1691 - Part 6a: Support private values which contain pointers to cycle-collected C++ objects https://bugzilla.mozilla.org/show_bug.cgi?id=1342012

(cherry picked from commit 1286cca73d58332ba0becd0011f747713b8acfac)
This commit is contained in:
Brian Smith 2023-04-09 12:22:33 -05:00 committed by roytam1
commit 1b811bd711
10 changed files with 107 additions and 23 deletions

View file

@ -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.")

View file

@ -4756,7 +4756,7 @@ JS::SetModulePrivate(JSObject* module, const JS::Value& value)
JS_PUBLIC_API(JS::Value)
JS::GetModulePrivate(JSObject* module)
{
return module->as<ModuleObject>().scriptSourceObject()->getPrivate();
return module->as<ModuleObject>().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)

View file

@ -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.

View file

@ -1321,6 +1321,15 @@ js::GetAllocationMetadata(JSObject* obj)
return nullptr;
}
JS_FRIEND_API(JS::Value)
js::MaybeGetScriptPrivate(JSObject* object) {
if (!object->is<ScriptSourceObject>()) {
return UndefinedValue();
}
return object->as<ScriptSourceObject>().canonicalPrivate();
}
JS_FRIEND_API(bool)
js::ReportIsNotFunction(JSContext* cx, HandleValue v)
{

View file

@ -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.

View file

@ -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,

View file

@ -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;
}

View file

@ -245,7 +245,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
js::StackFormat::SpiderMonkey),
moduleResolveHook(),
moduleMetadataHook(),
moduleDynamicImportHook()
moduleDynamicImportHook(),
scriptPrivateFinalizeHook()
{
setGCStoreBufferPtr(&gc.storeBuffer);

View file

@ -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 {

View file

@ -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<nsISupports*>(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<nsISupports>(aObj));
} else if (domClass->mParticipant) {
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(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<nsISupports>(aObj));
} else if (domClass->mParticipant) {
aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
domClass->mParticipant);
}
return;
}
JS::Value value = js::MaybeGetScriptPrivate(aObj);
if (!value.isUndefined()) {
aCb.NoteXPCOMChild(static_cast<nsISupports*>(value.toPrivate()));
}
}