Bug 816784 part 3 - Optimize the other places that iterate CCWs. r?jonco
The wrappers for strings have target compartment nullptr, which are stored
separately with the other wrappers, so we can simply skip or target them.
MozReview-Commit-ID: CEgU3q7cnmB
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -676,19 +676,19 @@ JSCompartment::getExistingTemplateLitera
}
void
JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
{
MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
MOZ_ASSERT(!zone()->isCollectingFromAnyThread() || trc->runtime()->gc.isHeapCompacting());
- for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
- Value v = e.front().value().unbarrieredGet();
+ for (NonStringWrapperEnum e(this); !e.empty(); e.popFront()) {
if (e.front().key().is<JSObject*>()) {
+ 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.
*/
TraceEdge(trc, wrapper->slotOfPrivate(), "cross-compartment wrapper");
}
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -251,40 +251,62 @@ class WrapperMap
DefaultHasher<JSCompartment*>,
SystemAllocPolicy>;
OuterMap map;
public:
class Enum
{
+ public:
+ enum SkipStrings : bool {
+ WithStrings = false,
+ WithoutStrings = true
+ };
+
+ private:
Enum(const Enum&) = delete;
void operator=(const Enum&) = delete;
void goToNext() {
if (outer.isNothing())
return;
- while (!outer->empty()) {
+ for (; !outer->empty(); outer->popFront()) {
+ JSCompartment* c = outer->front().key();
+ // Need to skip string at first, because the filter may not be
+ // happy with a nullptr.
+ if (!c && skipStrings)
+ continue;
+ if (filter && !filter->match(c))
+ continue;
InnerMap& m = outer->front().value();
if (!m.empty()) {
if (inner.isSome())
inner.reset();
inner.emplace(m);
outer->popFront();
return;
}
- outer->popFront();
}
}
mozilla::Maybe<OuterMap::Enum> outer;
mozilla::Maybe<InnerMap::Enum> inner;
+ const CompartmentFilter* filter;
+ SkipStrings skipStrings;
public:
- explicit Enum(WrapperMap& m) {
+ explicit Enum(WrapperMap& m, SkipStrings s = WithStrings) :
+ filter(nullptr), skipStrings(s) {
+ outer.emplace(m.map);
+ goToNext();
+ }
+
+ Enum(WrapperMap& m, const CompartmentFilter& f, SkipStrings s = WithStrings) :
+ filter(&f), skipStrings(s) {
outer.emplace(m.map);
goToNext();
}
Enum(WrapperMap& m, JSCompartment* target) {
// Leave the outer map as nothing and only iterate the inner map we
// find here.
auto p = m.map.lookup(target);
@@ -825,17 +847,26 @@ struct JSCompartment
}
void removeWrapper(js::WrapperMap::Ptr p) {
crossCompartmentWrappers.remove(p);
}
struct WrapperEnum : public js::WrapperMap::Enum {
explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
- explicit WrapperEnum(JSCompartment* c, JSCompartment* target) : js::WrapperMap::Enum(c->crossCompartmentWrappers, target) {}
+ };
+
+ struct NonStringWrapperEnum : public js::WrapperMap::Enum {
+ explicit NonStringWrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers, WithoutStrings) {}
+ explicit NonStringWrapperEnum(JSCompartment* c, const js::CompartmentFilter& f) : js::WrapperMap::Enum(c->crossCompartmentWrappers, f, WithoutStrings) {}
+ explicit NonStringWrapperEnum(JSCompartment* c, JSCompartment* target) : js::WrapperMap::Enum(c->crossCompartmentWrappers, target) { MOZ_ASSERT(target); }
+ };
+
+ struct StringWrapperEnum : public js::WrapperMap::Enum {
+ explicit StringWrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers, nullptr) {}
};
js::LexicalEnvironmentObject*
getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, js::HandleObject enclosing);
js::LexicalEnvironmentObject* getNonSyntacticLexicalEnvironment(JSObject* enclosing) const;
/*
* This method traces data that is live iff we know that this compartment's
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4017,19 +4017,17 @@ GCRuntime::markCompartments()
if (comp->maybeAlive) {
if (!workList.append(comp))
return;
}
}
while (!workList.empty()) {
JSCompartment* comp = workList.popCopy();
- for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
- if (e.front().key().is<JSString*>())
- continue;
+ for (JSCompartment::NonStringWrapperEnum e(comp); !e.empty(); e.popFront()) {
JSCompartment* dest = e.front().mutableKey().compartment();
if (dest && !dest->maybeAlive) {
dest->maybeAlive = true;
if (!workList.append(dest))
return;
}
}
}
@@ -4414,19 +4412,19 @@ static void
DropStringWrappers(JSRuntime* rt)
{
/*
* String "wrappers" are dropped on GC because their presence would require
* us to sweep the wrappers in all compartments every time we sweep a
* compartment group.
*/
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
- for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
- if (e.front().key().is<JSString*>())
- e.removeFront();
+ for (JSCompartment::StringWrapperEnum e(c); !e.empty(); e.popFront()) {
+ MOZ_ASSERT(e.front().key().is<JSString*>());
+ e.removeFront();
}
}
}
/*
* Group zones that must be swept at the same time.
*
* If compartment A has an edge to an unmarked object in compartment B, then we
@@ -4699,20 +4697,18 @@ AssertNotOnGrayList(JSObject* obj)
#endif
static void
AssertNoWrappersInGrayList(JSRuntime* rt)
{
#ifdef DEBUG
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
MOZ_ASSERT(!c->gcIncomingGrayPointers);
- for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
- if (!e.front().key().is<JSString*>())
- AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
- }
+ for (JSCompartment::NonStringWrapperEnum e(c); !e.empty(); e.popFront())
+ AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
}
#endif
}
static JSObject*
CrossCompartmentPointerReferent(JSObject* obj)
{
MOZ_ASSERT(IsGrayListObject(obj));
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -529,20 +529,21 @@ js::NukeCrossCompartmentWrappers(JSConte
continue;
// If the compartment matches both the source and target filter, we may
// want to cut both incoming and outgoing wrappers.
bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
target == c.get());
// Iterate only the wrappers that have target compartment matched unless
- // |nukeAll| is true. The wrappers for strings that we're not interested
- // in won't be here because they have compartment nullptr. Use Maybe to
- // avoid copying from conditionally initializing WrapperEnum.
- mozilla::Maybe<JSCompartment::WrapperEnum> e;
+ // |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
+ // initializing NonStringWrapperEnum.
+ mozilla::Maybe<JSCompartment::NonStringWrapperEnum> e;
if (MOZ_LIKELY(!nukeAll))
e.emplace(c, target);
else
e.emplace(c);
for (; !e->empty(); e->popFront()) {
// Skip debugger references because NukeCrossCompartmentWrapper()
// doesn't know how to nuke them yet, see bug 1084626 for more
// information.
@@ -686,26 +687,22 @@ js::RecomputeWrappers(JSContext* cx, con
AutoWrapperVector toRecompute(cx);
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
// Filter by source compartment.
if (!sourceFilter.match(c))
continue;
// Iterate over the wrappers, filtering appropriately.
- for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
+ for (JSCompartment::NonStringWrapperEnum e(c, targetFilter); !e.empty(); e.popFront()) {
// Filter out non-objects.
CrossCompartmentKey& k = e.front().mutableKey();
if (!k.is<JSObject*>())
continue;
- // Filter by target compartment.
- if (!targetFilter.match(k.compartment()))
- continue;
-
// Add it to the list.
if (!toRecompute.append(WrapperValue(e)))
return false;
}
}
// Recompute all the wrappers in the list.
for (const WrapperValue& v : toRecompute) {