зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1546817: Refactor and document Debugger support in js::CrossCompartmentKey. r=sfink
This replaces the various Debugger-related member classes in js::CrossCompartmentKey with a series of small structs that can be used directly as alternatives in CrossCompartmentKey::WrappedType. Thus, instead of having a two-level tag --- the Variant tag, and then for DebuggerAndObject case, a DebuggerObjectKind value --- the Variant tag can do all the work by itself. This also tightens up the types a bit: choosing the wrong DebuggerObjectKind would be a silent error, but using the wrong constructor might get you a type error (although unfortunately some of the types are not as specific as you might hope: NativeObject instead of WasmInstanceObject, for example). A new comment explains the rationale for giving Debugger API objects entries in the wrapper map. Differential Revision: https://phabricator.services.mozilla.com/D28781 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1602a76efe
Коммит
65825b234f
|
@ -23,22 +23,95 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
// A key in a WrapperMap, a compartment's map from entities in other
|
||||
// compartments to the local values the compartment's own code must use to refer
|
||||
// to them.
|
||||
//
|
||||
// WrapperMaps have a complex key type because, in addition to mapping JSObjects
|
||||
// to their cross-compartment wrappers, they must also map non-atomized
|
||||
// JSStrings to their copies in the local compartment, and debuggee entities
|
||||
// (objects, scripts, etc.) to their representative objects in the Debugger API.
|
||||
class CrossCompartmentKey {
|
||||
public:
|
||||
enum DebuggerObjectKind : uint8_t {
|
||||
DebuggerSource,
|
||||
DebuggerEnvironment,
|
||||
DebuggerObject,
|
||||
DebuggerWasmScript,
|
||||
DebuggerWasmSource
|
||||
// [SMDOC]: Cross-compartment wrapper map entries for Debugger API objects
|
||||
//
|
||||
// The Debugger API creates objects like Debugger.Object, Debugger.Script,
|
||||
// Debugger.Environment, etc. to refer to things in the debuggee. Each
|
||||
// Debugger gets at most one Debugger.Mumble for each referent:
|
||||
// Debugger.Mumbles are unique per referent per Debugger.
|
||||
//
|
||||
// Since a Debugger and its debuggee must be in different compartments, a
|
||||
// Debugger.Mumble's pointer to its referent is a cross-compartment edge, from
|
||||
// the debugger's compartment into the debuggee compartment. Like any other
|
||||
// sort of cross-compartment edge, the GC needs to be able to find all of
|
||||
// these edges readily.
|
||||
//
|
||||
// Our solution is to treat Debugger.Mumble objects as wrappers stored in
|
||||
// JSCompartment::crossCompartmentWrappers, where the GC already looks when it
|
||||
// needs to find any other sort of cross-compartment edges. This also meshes
|
||||
// nicely with existing sanity checks that trace the heap looking for
|
||||
// cross-compartment edges and check that each one has an entry in the right
|
||||
// wrapper map.
|
||||
//
|
||||
// That approach means that a given referent may have multiple entries in the
|
||||
// wrapper map: its ordinary cross-compartment wrapper, and then any
|
||||
// Debugger.Mumbles referring to it. If there are multiple Debuggers in a
|
||||
// compartment, each needs its own Debugger.Mumble for the referent, and each
|
||||
// of those needs its own entry in the WrapperMap. And some referents may have
|
||||
// more than one type of Debugger.Mumble that can refer to them: for example,
|
||||
// a WasmInstanceObject can be the referent of both a Debugger.Script and a
|
||||
// Debugger.Source.
|
||||
//
|
||||
// Hence, to look up a Debugger.Mumble in the WrapperMap, we need a key that
|
||||
// includes 1) the referent, 2) the Debugger to which the Mumble belongs, and
|
||||
// 3) the specific type of Mumble we're looking for. Since mozilla::Variant
|
||||
// distinguishes alternatives by type only, we include a distinct type in
|
||||
// WrappedType for each sort of Debugger.Mumble.
|
||||
|
||||
// Common structure for all Debugger.Mumble keys.
|
||||
template <typename Referent>
|
||||
struct Debuggee {
|
||||
Debuggee(NativeObject* debugger, Referent* referent)
|
||||
: debugger(debugger), referent(referent) {}
|
||||
|
||||
bool operator==(const Debuggee& other) const {
|
||||
return debugger == other.debugger && referent == other.referent;
|
||||
}
|
||||
|
||||
bool operator!=(const Debuggee& other) const { return !(*this == other); }
|
||||
|
||||
NativeObject* debugger;
|
||||
Referent* referent;
|
||||
};
|
||||
using DebuggerAndObject =
|
||||
mozilla::Tuple<NativeObject*, JSObject*, DebuggerObjectKind>;
|
||||
using DebuggerAndScript = mozilla::Tuple<NativeObject*, JSScript*>;
|
||||
using DebuggerAndLazyScript = mozilla::Tuple<NativeObject*, LazyScript*>;
|
||||
|
||||
// Key under which we find debugger's Debugger.Object referring to referent.
|
||||
struct DebuggeeObject : Debuggee<JSObject> {
|
||||
DebuggeeObject(NativeObject* debugger, JSObject* referent)
|
||||
: Debuggee(debugger, referent) {}
|
||||
};
|
||||
|
||||
// Keys under which we find Debugger.Scripts.
|
||||
using DebuggeeJSScript = Debuggee<JSScript>;
|
||||
using DebuggeeWasmScript = Debuggee<NativeObject>; // WasmInstanceObject
|
||||
using DebuggeeLazyScript = Debuggee<LazyScript>;
|
||||
|
||||
// Key under which we find debugger's Debugger.Environment referring to
|
||||
// referent.
|
||||
struct DebuggeeEnvironment : Debuggee<JSObject> {
|
||||
DebuggeeEnvironment(NativeObject* debugger, JSObject* referent)
|
||||
: Debuggee(debugger, referent) {}
|
||||
};
|
||||
|
||||
// Key under which we find debugger's Debugger.Source referring to referent.
|
||||
struct DebuggeeSource : Debuggee<NativeObject> {
|
||||
DebuggeeSource(NativeObject* debugger, NativeObject* referent)
|
||||
: Debuggee(debugger, referent) {}
|
||||
};
|
||||
|
||||
using WrappedType =
|
||||
mozilla::Variant<JSObject*, JSString*, DebuggerAndScript,
|
||||
DebuggerAndLazyScript, DebuggerAndObject>;
|
||||
mozilla::Variant<JSObject*, JSString*, DebuggeeObject, DebuggeeJSScript,
|
||||
DebuggeeWasmScript, DebuggeeLazyScript,
|
||||
DebuggeeEnvironment, DebuggeeSource>;
|
||||
|
||||
explicit CrossCompartmentKey(JSObject* obj) : wrapped(obj) {
|
||||
MOZ_RELEASE_ASSERT(obj);
|
||||
|
@ -49,22 +122,18 @@ class CrossCompartmentKey {
|
|||
explicit CrossCompartmentKey(const JS::Value& v)
|
||||
: wrapped(v.isString() ? WrappedType(v.toString())
|
||||
: WrappedType(&v.toObject())) {}
|
||||
explicit CrossCompartmentKey(NativeObject* debugger, JSObject* obj,
|
||||
DebuggerObjectKind kind)
|
||||
: wrapped(DebuggerAndObject(debugger, obj, kind)) {
|
||||
MOZ_RELEASE_ASSERT(debugger);
|
||||
MOZ_RELEASE_ASSERT(obj);
|
||||
}
|
||||
explicit CrossCompartmentKey(NativeObject* debugger, JSScript* script)
|
||||
: wrapped(DebuggerAndScript(debugger, script)) {
|
||||
MOZ_RELEASE_ASSERT(debugger);
|
||||
MOZ_RELEASE_ASSERT(script);
|
||||
}
|
||||
explicit CrossCompartmentKey(NativeObject* debugger, LazyScript* lazyScript)
|
||||
: wrapped(DebuggerAndLazyScript(debugger, lazyScript)) {
|
||||
MOZ_RELEASE_ASSERT(debugger);
|
||||
MOZ_RELEASE_ASSERT(lazyScript);
|
||||
}
|
||||
|
||||
// For most debuggee keys, we must let the caller choose the key type
|
||||
// themselves. But for JSScript and LazyScript, there is only one key type
|
||||
// that makes sense, so we provide an overloaded constructor.
|
||||
explicit CrossCompartmentKey(DebuggeeObject&& key) : wrapped(key) {}
|
||||
explicit CrossCompartmentKey(DebuggeeSource&& key) : wrapped(key) {}
|
||||
explicit CrossCompartmentKey(DebuggeeEnvironment&& key) : wrapped(key) {}
|
||||
explicit CrossCompartmentKey(DebuggeeWasmScript&& key) : wrapped(key) {}
|
||||
explicit CrossCompartmentKey(NativeObject* debugger, JSScript* referent)
|
||||
: wrapped(DebuggeeJSScript(debugger, referent)) {}
|
||||
explicit CrossCompartmentKey(NativeObject* debugger, LazyScript* referent)
|
||||
: wrapped(DebuggeeLazyScript(debugger, referent)) {}
|
||||
|
||||
bool operator==(const CrossCompartmentKey& other) const {
|
||||
return wrapped == other.wrapped;
|
||||
|
@ -82,63 +151,54 @@ class CrossCompartmentKey {
|
|||
return wrapped.as<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename F>
|
||||
struct ApplyToWrappedMatcher {
|
||||
F f_;
|
||||
explicit ApplyToWrappedMatcher(F f) : f_(f) {}
|
||||
auto operator()(JSObject*& obj) { return f_(&obj); }
|
||||
auto operator()(JSString*& str) { return f_(&str); }
|
||||
template <typename Referent>
|
||||
auto operator()(Debuggee<Referent>& dbg) {
|
||||
return f_(&dbg.referent);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct ApplyToDebuggerMatcher {
|
||||
F f_;
|
||||
explicit ApplyToDebuggerMatcher(F f) : f_(f) {}
|
||||
|
||||
using ReturnType = decltype(f_(static_cast<NativeObject**>(nullptr)));
|
||||
ReturnType operator()(JSObject*& obj) { return ReturnType(); }
|
||||
ReturnType operator()(JSString*& str) { return ReturnType(); }
|
||||
template <typename Referent>
|
||||
ReturnType operator()(Debuggee<Referent>& dbg) {
|
||||
return f_(&dbg.debugger);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename F>
|
||||
auto applyToWrapped(F f) {
|
||||
struct WrappedMatcher {
|
||||
F f_;
|
||||
explicit WrappedMatcher(F f) : f_(f) {}
|
||||
auto operator()(JSObject*& obj) { return f_(&obj); }
|
||||
auto operator()(JSString*& str) { return f_(&str); }
|
||||
auto operator()(DebuggerAndScript& tpl) {
|
||||
return f_(&mozilla::Get<1>(tpl));
|
||||
}
|
||||
auto operator()(DebuggerAndLazyScript& tpl) {
|
||||
return f_(&mozilla::Get<1>(tpl));
|
||||
}
|
||||
auto operator()(DebuggerAndObject& tpl) {
|
||||
return f_(&mozilla::Get<1>(tpl));
|
||||
}
|
||||
} matcher(f);
|
||||
return wrapped.match(matcher);
|
||||
return wrapped.match(ApplyToWrappedMatcher<F>(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
auto applyToDebugger(F f) {
|
||||
using ReturnType = decltype(f(static_cast<NativeObject**>(nullptr)));
|
||||
struct DebuggerMatcher {
|
||||
F f_;
|
||||
explicit DebuggerMatcher(F f) : f_(f) {}
|
||||
ReturnType operator()(JSObject*& obj) { return ReturnType(); }
|
||||
ReturnType operator()(JSString*& str) { return ReturnType(); }
|
||||
ReturnType operator()(DebuggerAndScript& tpl) {
|
||||
return f_(&mozilla::Get<0>(tpl));
|
||||
}
|
||||
ReturnType operator()(DebuggerAndLazyScript& tpl) {
|
||||
return f_(&mozilla::Get<0>(tpl));
|
||||
}
|
||||
ReturnType operator()(DebuggerAndObject& tpl) {
|
||||
return f_(&mozilla::Get<0>(tpl));
|
||||
}
|
||||
} matcher(f);
|
||||
return wrapped.match(matcher);
|
||||
return wrapped.match(ApplyToDebuggerMatcher<F>(f));
|
||||
}
|
||||
|
||||
bool isDebuggerKey() const {
|
||||
struct DebuggerMatcher {
|
||||
bool operator()(JSObject* const& obj) { return false; }
|
||||
bool operator()(JSString* const& str) { return false; }
|
||||
bool operator()(const DebuggerAndScript& tpl) {
|
||||
return true;
|
||||
}
|
||||
bool operator()(const DebuggerAndLazyScript& tpl) {
|
||||
return true;
|
||||
}
|
||||
bool operator()(const DebuggerAndObject& tpl) {
|
||||
return true;
|
||||
}
|
||||
} matcher;
|
||||
return wrapped.match(matcher);
|
||||
}
|
||||
struct IsDebuggerKeyMatcher {
|
||||
bool operator()(JSObject* const& obj) { return false; }
|
||||
bool operator()(JSString* const& str) { return false; }
|
||||
template <typename Referent>
|
||||
bool operator()(Debuggee<Referent> const& dbg) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool isDebuggerKey() const { return wrapped.match(IsDebuggerKeyMatcher()); }
|
||||
|
||||
JS::Compartment* compartment() {
|
||||
return applyToWrapped([](auto tp) { return (*tp)->maybeCompartment(); });
|
||||
|
@ -156,22 +216,13 @@ class CrossCompartmentKey {
|
|||
HashNumber operator()(JSString* str) {
|
||||
return DefaultHasher<JSString*>::hash(str);
|
||||
}
|
||||
HashNumber operator()(const DebuggerAndScript& tpl) {
|
||||
return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
|
||||
DefaultHasher<JSScript*>::hash(mozilla::Get<1>(tpl));
|
||||
}
|
||||
HashNumber operator()(const DebuggerAndLazyScript& tpl) {
|
||||
return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
|
||||
DefaultHasher<LazyScript*>::hash(mozilla::Get<1>(tpl));
|
||||
}
|
||||
HashNumber operator()(const DebuggerAndObject& tpl) {
|
||||
return DefaultHasher<NativeObject*>::hash(mozilla::Get<0>(tpl)) ^
|
||||
DefaultHasher<JSObject*>::hash(mozilla::Get<1>(tpl)) ^
|
||||
(mozilla::Get<2>(tpl) << 5);
|
||||
template <typename Referent>
|
||||
HashNumber operator()(const Debuggee<Referent>& dbg) {
|
||||
return mozilla::HashGeneric(dbg.debugger, dbg.referent);
|
||||
}
|
||||
};
|
||||
static HashNumber hash(const CrossCompartmentKey& key) {
|
||||
return key.wrapped.match(HashFunctor());
|
||||
return key.wrapped.addTagToHash(key.wrapped.match(HashFunctor()));
|
||||
}
|
||||
|
||||
static bool match(const CrossCompartmentKey& l,
|
||||
|
@ -190,6 +241,8 @@ class CrossCompartmentKey {
|
|||
|
||||
private:
|
||||
CrossCompartmentKey() = delete;
|
||||
explicit CrossCompartmentKey(WrappedType&& wrapped)
|
||||
: wrapped(std::move(wrapped)) {}
|
||||
WrappedType wrapped;
|
||||
};
|
||||
|
||||
|
|
|
@ -1274,8 +1274,8 @@ bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
|
|||
return false;
|
||||
}
|
||||
|
||||
CrossCompartmentKey key(object, env,
|
||||
CrossCompartmentKey::DebuggerEnvironment);
|
||||
CrossCompartmentKey key(
|
||||
CrossCompartmentKey::DebuggeeEnvironment(object, env));
|
||||
if (!object->compartment()->putWrapper(cx, key, ObjectValue(*envobj))) {
|
||||
NukeDebuggerWrapper(envobj);
|
||||
environments.remove(env);
|
||||
|
@ -1372,7 +1372,7 @@ bool Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
|
|||
}
|
||||
|
||||
if (obj->compartment() != object->compartment()) {
|
||||
CrossCompartmentKey key(object, obj, CrossCompartmentKey::DebuggerObject);
|
||||
CrossCompartmentKey key(CrossCompartmentKey::DebuggeeObject(object, obj));
|
||||
if (!object->compartment()->putWrapper(cx, key, ObjectValue(*dobj))) {
|
||||
NukeDebuggerWrapper(dobj);
|
||||
objects.remove(obj);
|
||||
|
@ -6082,9 +6082,7 @@ JSObject* Debugger::wrapVariantReferent(
|
|||
Handle<WasmInstanceObject*> untaggedReferent =
|
||||
referent.template as<WasmInstanceObject*>();
|
||||
Rooted<CrossCompartmentKey> key(
|
||||
cx, CrossCompartmentKey(
|
||||
object, untaggedReferent,
|
||||
CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmScript));
|
||||
cx, CrossCompartmentKey::DebuggeeWasmScript(object, untaggedReferent));
|
||||
obj = wrapVariantReferent<DebuggerScriptReferent, WasmInstanceObject*,
|
||||
WasmInstanceWeakMap>(cx, wasmInstanceScripts, key,
|
||||
referent);
|
||||
|
@ -8368,18 +8366,14 @@ JSObject* Debugger::wrapVariantReferent(
|
|||
Handle<ScriptSourceObject*> untaggedReferent =
|
||||
referent.template as<ScriptSourceObject*>();
|
||||
Rooted<CrossCompartmentKey> key(
|
||||
cx, CrossCompartmentKey(
|
||||
object, untaggedReferent,
|
||||
CrossCompartmentKey::DebuggerObjectKind::DebuggerSource));
|
||||
cx, CrossCompartmentKey::DebuggeeSource(object, untaggedReferent));
|
||||
obj = wrapVariantReferent<DebuggerSourceReferent, ScriptSourceObject*,
|
||||
SourceWeakMap>(cx, sources, key, referent);
|
||||
} else {
|
||||
Handle<WasmInstanceObject*> untaggedReferent =
|
||||
referent.template as<WasmInstanceObject*>();
|
||||
Rooted<CrossCompartmentKey> key(
|
||||
cx, CrossCompartmentKey(
|
||||
object, untaggedReferent,
|
||||
CrossCompartmentKey::DebuggerObjectKind::DebuggerWasmSource));
|
||||
cx, CrossCompartmentKey::DebuggeeSource(object, untaggedReferent));
|
||||
obj = wrapVariantReferent<DebuggerSourceReferent, WasmInstanceObject*,
|
||||
WasmInstanceWeakMap>(cx, wasmInstanceSources, key,
|
||||
referent);
|
||||
|
|
Загрузка…
Ссылка в новой задаче