Bug 816784 part 3 - Optimize the other places that iterate CCWs. r?jonco draft
authorTing-Yu Chou <janus926@gmail.com>
Thu, 04 May 2017 17:46:43 +0800
changeset 582208 c2846145a6d5765366e41bf8c71c50982b7f93f6
parent 580986 14fe55de8a7768d745b34e26eabc208fd7d4960b
child 583481 e98603fa739af64d13ae713c18ca0825c51e4b32
push id60001
push userbmo:janus926@gmail.com
push dateMon, 22 May 2017 02:51:05 +0000
reviewersjonco
bugs816784
milestone55.0a1
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
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/proxy/CrossCompartmentWrapper.cpp
--- 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) {