Bug 1342012 - Support script and module private values which contain pointers to cycle-collected C++ objects r=jandem

This commit is contained in:
Jon Coppeard 2018-12-06 16:52:15 -05:00
Родитель 41dd1b00f4
Коммит 20a91427ef
11 изменённых файлов: 111 добавлений и 25 удалений

Просмотреть файл

@ -620,6 +620,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.")

Просмотреть файл

@ -3740,7 +3740,7 @@ JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module,
}
JS_PUBLIC_API JS::Value JS::GetModulePrivate(JSObject* module) {
return module->as<ModuleObject>().scriptSourceObject()->unwrappedPrivate();
return module->as<ModuleObject>().scriptSourceObject()->canonicalPrivate();
}
JS_PUBLIC_API void JS::SetScriptPrivate(JSScript* script,
@ -3749,7 +3749,19 @@ JS_PUBLIC_API void JS::SetScriptPrivate(JSScript* script,
}
JS_PUBLIC_API JS::Value JS::GetScriptPrivate(JSScript* script) {
return script->sourceObject()->unwrappedPrivate();
return script->sourceObject()->canonicalPrivate();
}
JS_PUBLIC_API JS::ScriptPrivateFinalizeHook JS::GetScriptPrivateFinalizeHook(
JSRuntime* rt) {
AssertHeapIsIdle();
return rt->scriptPrivateFinalizeHook;
}
JS_PUBLIC_API void JS::SetScriptPrivateFinalizeHook(
JSRuntime* rt, JS::ScriptPrivateFinalizeHook func) {
AssertHeapIsIdle();
rt->scriptPrivateFinalizeHook = func;
}
JS_PUBLIC_API bool JS::ModuleInstantiate(JSContext* cx,

Просмотреть файл

@ -3104,13 +3104,17 @@ using ModuleDynamicImportHook = bool (*)(JSContext* cx,
HandleObject promise);
/**
* Get the HostResolveImportedModule hook for the runtime.
* Get the HostImportModuleDynamically hook for the runtime.
*/
extern JS_PUBLIC_API ModuleDynamicImportHook
GetModuleDynamicImportHook(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 SetModuleDynamicImportHook(
JSRuntime* rt, ModuleDynamicImportHook func);
@ -3152,6 +3156,26 @@ extern JS_PUBLIC_API void SetScriptPrivate(JSScript* script,
*/
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(JSRuntime* rt);
/**
* Set the script private finalize hook for the runtime to the given function.
*/
extern JS_PUBLIC_API void SetScriptPrivateFinalizeHook(
JSRuntime* rt, ScriptPrivateFinalizeHook func);
/*
* Perform the ModuleInstantiate operation on the given source text module
* record.

Просмотреть файл

@ -1404,6 +1404,14 @@ JS_FRIEND_API void js::LogDtor(void* self, const char* type, uint32_t sz) {
}
}
JS_FRIEND_API JS::Value js::MaybeGetScriptPrivate(JSObject* object) {
if (!object->is<ScriptSourceObject>()) {
return UndefinedValue();
}
return object->as<ScriptSourceObject>().canonicalPrivate();
}
JS_FRIEND_API uint64_t js::GetGCHeapUsageForObjectZone(JSObject* obj) {
return obj->zone()->usage.gcBytes();
}

Просмотреть файл

@ -106,6 +106,22 @@ extern JS_FRIEND_API bool JS_IsDeadWrapper(JSObject* obj);
extern JS_FRIEND_API JSObject* JS_NewDeadWrapper(
JSContext* cx, JSObject* origObject = nullptr);
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.

Просмотреть файл

@ -3367,7 +3367,7 @@ ModuleObject* js::GetModuleObjectForScript(JSScript* script) {
Value js::FindScriptOrModulePrivateForScript(JSScript* script) {
while (script) {
ScriptSourceObject* sso = script->sourceObject();
Value value = sso->unwrappedPrivate();
Value value = sso->canonicalPrivate();
if (!value.isUndefined()) {
return value;
}

Просмотреть файл

@ -1317,6 +1317,16 @@ void ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj) {
MOZ_ASSERT(fop->onMainThread());
ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
sso->source()->decref();
Value value = sso->canonicalPrivate();
if (!value.isUndefined()) {
// The embedding may need to dispose of its private data.
JS::AutoSuppressGCAnalysis suppressGC;
if (JS::ScriptPrivateFinalizeHook hook =
fop->runtime()->scriptPrivateFinalizeHook) {
hook(fop, value);
}
}
}
void ScriptSourceObject::trace(JSTracer* trc, JSObject* obj) {

Просмотреть файл

@ -1176,8 +1176,10 @@ class ScriptSourceObject : public NativeObject {
setReservedSlot(PRIVATE_SLOT, value);
}
Value unwrappedPrivate() const {
return unwrappedCanonical()->getReservedSlot(PRIVATE_SLOT);
Value canonicalPrivate() const {
Value value = getReservedSlot(PRIVATE_SLOT);
MOZ_ASSERT_IF(!isCanonical(), value.isUndefined());
return value;
}
private:

Просмотреть файл

@ -163,7 +163,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
wasmInstances(mutexid::WasmRuntimeInstances),
moduleResolveHook(),
moduleMetadataHook(),
moduleDynamicImportHook() {
moduleDynamicImportHook(),
scriptPrivateFinalizeHook() {
JS_COUNT_CTOR(JSRuntime);
liveRuntimesCount++;

Просмотреть файл

@ -979,6 +979,9 @@ struct JSRuntime : public js::MallocProvider<JSRuntime> {
// module import and can accessed by off-thread parsing.
mozilla::Atomic<JS::ModuleDynamicImportHook> moduleDynamicImportHook;
// A hook called on script finalization.
js::MainThreadData<JS::ScriptPrivateFinalizeHook> scriptPrivateFinalizeHook;
public:
#if defined(JS_BUILD_BINAST)
js::BinaryASTSupport& binast() { return binast_; }

Просмотреть файл

@ -75,6 +75,7 @@
#include "mozilla/dom/ScriptSettings.h"
#include "js/Debug.h"
#include "js/GCAPI.h"
#include "jsfriendapi.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionNoteRootCallback.h"
#include "nsCycleCollectionParticipant.h"
@ -627,28 +628,36 @@ void CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(
// Nothing else to do!
return;
}
// 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()));
}
}