Bug 1571021 - Key wrapper maps by wrapped type directly and remove CrossCompartmentKey class r=jandem?

Differential Revision: https://phabricator.services.mozilla.com/D41184

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jon Coppeard 2019-08-09 10:05:45 +00:00
Родитель a4a756e0e6
Коммит 3fb04aa631
8 изменённых файлов: 55 добавлений и 190 удалений

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

@ -119,6 +119,7 @@ struct GCPointerPolicy {
}
return false;
}
static bool isTenured(T v) { return !js::gc::IsInsideNursery(v); }
static bool isValid(T v) { return js::gc::IsCellPointerValidOrNull(v); }
};
#define EXPAND_SPECIALIZE_GCPOLICY(Type) \

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

@ -4614,7 +4614,7 @@ void GCRuntime::markCompartments() {
while (!workList.empty()) {
Compartment* comp = workList.popCopy();
for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) {
Compartment* dest = e.front().mutableKey().compartment();
Compartment* dest = e.front().key()->compartment();
if (dest && !dest->gcState.maybeAlive) {
dest->gcState.maybeAlive = true;
if (!workList.append(dest)) {
@ -5014,10 +5014,7 @@ static void DropStringWrappers(JSRuntime* rt) {
* compartment group.
*/
for (CompartmentsIter c(rt); !c.done(); c.next()) {
for (Compartment::StringWrapperEnum e(c); !e.empty(); e.popFront()) {
MOZ_ASSERT(e.front().key().is<JSString*>());
e.removeFront();
}
c->dropStringWrappersOnGC();
}
}
@ -5041,9 +5038,9 @@ static void DropStringWrappers(JSRuntime* rt) {
bool Compartment::findSweepGroupEdges() {
Zone* source = zone();
for (ObjectWrapperEnum e(this); !e.empty(); e.popFront()) {
CrossCompartmentKey& key = e.front().mutableKey();
JSObject* key = e.front().mutableKey();
Zone* target = key.zone();
Zone* target = key->zone();
if (!target->isGCMarking()) {
continue;
}
@ -5052,7 +5049,7 @@ bool Compartment::findSweepGroupEdges() {
// is not still being marked when we start sweeping the wrapped zone. As an
// optimization, if the wrapped object is already marked black there is no
// danger of later marking and we can skip this.
if (key.as<JSObject*>()->asTenured().isMarkedBlack()) {
if (key->asTenured().isMarkedBlack()) {
continue;
}

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

@ -174,6 +174,11 @@ class NurseryAwareHashMap {
map.sweep();
}
void clear() {
map.clear();
nurseryEntries.clear();
}
bool hasNurseryEntries() const { return !nurseryEntries.empty(); }
};

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

@ -108,7 +108,7 @@ JS_PUBLIC_API void JS::TraceIncomingCCWs(
continue;
}
for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) {
JSObject* obj = e.front().key().as<JSObject*>();
JSObject* obj = e.front().key();
Compartment* comp = obj->compartment();
if (compartments.has(comp)) {
mozilla::DebugOnly<JSObject*> prior = obj;

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

@ -537,11 +537,11 @@ JS_FRIEND_API void js::VisitGrayWrapperTargets(Zone* zone,
void* closure) {
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) {
e.front().mutableKey().applyToWrapped([callback, closure](auto tp) {
if ((*tp)->isMarkedGray()) {
callback(closure, JS::GCCellPtr(*tp));
}
});
JSObject* target = e.front().key();
if (target->isMarkedGray()) {
JS::AutoSuppressGCAnalysis nogc;
callback(closure, JS::GCCellPtr(target));
}
}
}
}

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

@ -436,9 +436,7 @@ JS_FRIEND_API bool js::NukeCrossCompartmentWrappers(
target->compartment() == c.get() && NukedAllRealms(c.get()));
// Iterate only the wrappers that have target compartment matched unless
// |nukeAll| is true. The string wrappers that we're not interested in
// won't be iterated, we can exclude them easily because they have
// compartment nullptr. Use Maybe to avoid copying from conditionally
// |nukeAll| is true. Use Maybe to avoid copying from conditionally
// initializing ObjectWrapperEnum.
mozilla::Maybe<Compartment::ObjectWrapperEnum> e;
if (MOZ_LIKELY(!nukeAll)) {
@ -448,19 +446,13 @@ JS_FRIEND_API bool js::NukeCrossCompartmentWrappers(
c.get()->nukedOutgoingWrappers = true;
}
for (; !e->empty(); e->popFront()) {
// Skip debugger references because NukeCrossCompartmentWrapper()
// doesn't know how to nuke them yet, see bug 1084626 for more
// information.
const CrossCompartmentKey& k = e->front().key();
if (!k.is<JSObject*>()) {
continue;
}
JSObject* key = e->front().key();
AutoWrapperRooter wobj(cx, WrapperValue(*e));
// Unwrap from the wrapped object in CrossCompartmentKey instead of
// the wrapper, this could save us a bit of time.
JSObject* wrapped = UncheckedUnwrap(k.as<JSObject*>());
// Unwrap from the wrapped object in key instead of the wrapper, this
// could save us a bit of time.
JSObject* wrapped = UncheckedUnwrap(key);
// Don't nuke wrappers for objects in other realms in the target
// compartment unless nukeAll is set because in that case we want to nuke
@ -642,16 +634,10 @@ JS_FRIEND_API bool js::RecomputeWrappers(
evictedNursery = true;
}
// Iterate over the wrappers, filtering appropriately.
// Iterate over object wrappers, filtering appropriately.
for (Compartment::ObjectWrapperEnum e(c, targetFilter); !e.empty();
e.popFront()) {
// Filter out non-objects.
CrossCompartmentKey& k = e.front().mutableKey();
if (!k.is<JSObject*>()) {
continue;
}
// Add it to the list.
// Add the wrapper to the list.
if (!toRecompute.append(WrapperValue(e))) {
return false;
}

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

@ -54,13 +54,11 @@ static inline void CheckWrapperMapEntry(const Map& map, Entry& entry) {
* wrapperMap that points into the nursery, and that the hash table entries
* are discoverable.
*/
auto& key = entry.front().mutableKey();
key.applyToWrapped([&](auto tp) {
CheckGCThingAfterMovingGC(*tp);
auto key = entry.front().key();
CheckGCThingAfterMovingGC(key);
auto ptr = map.lookup(CrossCompartmentKey(*tp));
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &entry.front());
});
auto ptr = map.lookup(key);
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &entry.front());
}
void Compartment::checkWrapperMapAfterMovingGC() {
@ -74,10 +72,8 @@ void Compartment::checkWrapperMapAfterMovingGC() {
#endif // JSGC_HASH_TABLE_CHECKS
bool Compartment::putWrapper(JSContext* cx, JSObject* obj,
bool Compartment::putWrapper(JSContext* cx, JSObject* wrapped,
const js::Value& wrapper) {
CrossCompartmentKey wrapped(obj);
MOZ_ASSERT(wrapper.isObject());
MOZ_ASSERT(!js::IsProxy(&wrapper.toObject()) ||
js::GetProxyHandler(&wrapper.toObject())->family() !=
js::GetDOMRemoteProxyHandlerFamily());
@ -90,11 +86,8 @@ bool Compartment::putWrapper(JSContext* cx, JSObject* obj,
return true;
}
bool Compartment::putWrapper(JSContext* cx, JSString* str,
bool Compartment::putWrapper(JSContext* cx, JSString* wrapped,
const js::Value& wrapper) {
CrossCompartmentKey wrapped(str);
MOZ_ASSERT(wrapper.isString());
if (!crossCompartmentStringWrappers.put(wrapped, wrapper)) {
ReportOutOfMemory(cx);
return false;
@ -443,16 +436,14 @@ void Compartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc) {
trc->runtime()->gc.isHeapCompacting());
for (ObjectWrapperEnum e(this); !e.empty(); e.popFront()) {
if (e.front().key().is<JSObject*>()) {
Value v = e.front().value().unbarrieredGet();
ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
Value v = e.front().value().unbarrieredGet();
ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
/*
* We have a cross-compartment wrapper. Its private pointer may
* point into the compartment being collected, so we should mark it.
*/
ProxyObject::traceEdgeToTarget(trc, wrapper);
}
/*
* We have a cross-compartment wrapper. Its private pointer may
* point into the compartment being collected, so we should mark it.
*/
ProxyObject::traceEdgeToTarget(trc, wrapper);
}
}
@ -469,6 +460,11 @@ void Compartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc) {
DebugAPI::traceCrossCompartmentEdges(trc);
}
void Compartment::dropStringWrappersOnGC() {
MOZ_ASSERT(JS::RuntimeHeapIsCollecting());
crossCompartmentStringWrappers.clear();
}
void Compartment::sweepAfterMinorGC(JSTracer* trc) {
crossCompartmentObjectWrappers.sweepAfterMinorGC(trc);
crossCompartmentStringWrappers.sweepAfterMinorGC(trc);
@ -488,16 +484,6 @@ void Compartment::sweepCrossCompartmentWrappers() {
crossCompartmentStringWrappers.sweep();
}
void CrossCompartmentKey::trace(JSTracer* trc) {
applyToWrapped(
[trc](auto tp) { TraceRoot(trc, tp, "CrossCompartmentKey::wrapped"); });
}
bool CrossCompartmentKey::needsSweep() {
auto needsSweep = [](auto tp) { return IsAboutToBeFinalizedUnbarriered(tp); };
return applyToWrapped(needsSweep);
}
/* static */
void Compartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc) {
MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());

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

@ -25,103 +25,6 @@
namespace js {
// A key in a WrapperMap, a compartment's map from entities in other
// compartments to the wrapper objects the compartment's own code must use to
// refer to them.
//
// All cross-compartment edges must be represented either in the source
// compartment's wrapper map or in one of the debugger weakmaps.
//
// 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.
class CrossCompartmentKey {
public:
using WrappedType = mozilla::Variant<JSObject*, JSString*>;
explicit CrossCompartmentKey(JSObject* obj) : wrapped(obj) {
MOZ_RELEASE_ASSERT(obj);
}
explicit CrossCompartmentKey(JSString* str) : wrapped(str) {
MOZ_RELEASE_ASSERT(str);
}
explicit CrossCompartmentKey(const JS::Value& v)
: wrapped(v.isString() ? WrappedType(v.toString())
: WrappedType(&v.toObject())) {}
bool operator==(const CrossCompartmentKey& other) const {
return wrapped == other.wrapped;
}
bool operator!=(const CrossCompartmentKey& other) const {
return wrapped != other.wrapped;
}
template <typename T>
bool is() const {
return wrapped.is<T>();
}
template <typename T>
const T& as() const {
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); }
};
public:
template <typename F>
auto applyToWrapped(F f) {
return wrapped.match(ApplyToWrappedMatcher<F>(f));
}
JS::Compartment* compartment() {
return applyToWrapped([](auto tp) { return (*tp)->maybeCompartment(); });
}
JS::Zone* zone() {
return applyToWrapped([](auto tp) { return (*tp)->zone(); });
}
struct Hasher : public DefaultHasher<CrossCompartmentKey> {
struct HashFunctor {
HashNumber operator()(JSObject* obj) {
return DefaultHasher<JSObject*>::hash(obj);
}
HashNumber operator()(JSString* str) {
return DefaultHasher<JSString*>::hash(str);
}
};
static HashNumber hash(const CrossCompartmentKey& key) {
return key.wrapped.addTagToHash(key.wrapped.match(HashFunctor()));
}
static bool match(const CrossCompartmentKey& l,
const CrossCompartmentKey& k) {
return l.wrapped == k.wrapped;
}
};
bool isTenured() const {
auto self = const_cast<CrossCompartmentKey*>(this);
return self->applyToWrapped([](auto tp) { return (*tp)->isTenured(); });
}
void trace(JSTracer* trc);
bool needsSweep();
private:
CrossCompartmentKey() = delete;
explicit CrossCompartmentKey(WrappedType&& wrapped)
: wrapped(std::move(wrapped)) {}
WrappedType wrapped;
};
// The data structure for storing JSObject CCWs, which has a map per target
// compartment so we can access them easily. String CCWs are stored in a
// separate map.
@ -129,8 +32,8 @@ class ObjectWrapperMap {
static const size_t InitialInnerMapSize = 4;
using InnerMap =
NurseryAwareHashMap<CrossCompartmentKey, JS::Value,
CrossCompartmentKey::Hasher, ZoneAllocPolicy>;
NurseryAwareHashMap<JSObject*, JS::Value, DefaultHasher<JSObject*>,
ZoneAllocPolicy>;
using OuterMap = GCHashMap<JS::Compartment*, InnerMap,
DefaultHasher<JS::Compartment*>, ZoneAllocPolicy>;
@ -239,11 +142,10 @@ class ObjectWrapperMap {
return true;
}
Ptr lookup(const CrossCompartmentKey& k) const {
MOZ_ASSERT(k.is<JSObject*>());
auto op = map.lookup(const_cast<CrossCompartmentKey&>(k).compartment());
Ptr lookup(JSObject* obj) const {
auto op = map.lookup(obj->compartment());
if (op) {
auto ip = op->value().lookup(k);
auto ip = op->value().lookup(obj);
if (ip) {
return Ptr(ip, op->value());
}
@ -257,10 +159,8 @@ class ObjectWrapperMap {
}
}
MOZ_MUST_USE bool put(const CrossCompartmentKey& k, const JS::Value& v) {
MOZ_ASSERT(k.is<JSObject*>());
JS::Compartment* c = const_cast<CrossCompartmentKey&>(k).compartment();
MOZ_ASSERT(c);
MOZ_MUST_USE bool put(JSObject* obj, const JS::Value& v) {
JS::Compartment* c = obj->compartment();
auto p = map.lookupForAdd(c);
if (!p) {
InnerMap m(zone, InitialInnerMapSize);
@ -268,7 +168,7 @@ class ObjectWrapperMap {
return false;
}
}
return p->value().put(k, v);
return p->value().put(obj, v);
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
@ -322,9 +222,8 @@ class ObjectWrapperMap {
};
using StringWrapperMap =
js::NurseryAwareHashMap<js::CrossCompartmentKey, JS::Value,
js::CrossCompartmentKey::Hasher,
js::ZoneAllocPolicy>;
NurseryAwareHashMap<JSString*, JS::Value, DefaultHasher<JSString*>,
ZoneAllocPolicy>;
} // namespace js
@ -448,11 +347,11 @@ class JS::Compartment {
const js::Value& wrapper);
js::ObjectWrapperMap::Ptr lookupWrapper(JSObject* obj) const {
return crossCompartmentObjectWrappers.lookup(js::CrossCompartmentKey(obj));
return crossCompartmentObjectWrappers.lookup(obj);
}
js::StringWrapperMap::Ptr lookupWrapper(JSString* str) const {
return crossCompartmentStringWrappers.lookup(js::CrossCompartmentKey(str));
return crossCompartmentStringWrappers.lookup(str);
}
void removeWrapper(js::ObjectWrapperMap::Ptr p) {
@ -489,6 +388,7 @@ class JS::Compartment {
*/
void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
void dropStringWrappersOnGC();
void sweepRealms(js::FreeOp* fop, bool keepAtleastOne,
bool destroyingRuntime);
@ -597,14 +497,4 @@ class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter {
} /* namespace js */
namespace JS {
template <>
struct GCPolicy<js::CrossCompartmentKey>
: public StructGCPolicy<js::CrossCompartmentKey> {
static bool isTenured(const js::CrossCompartmentKey& key) {
return key.isTenured();
}
};
} // namespace JS
#endif /* vm_Compartment_h */