Bug 1434305 - Part 2: Add IterateLazyScripts. r=jimb
authorTooru Fujisawa <arai_a@mac.com>
Thu, 26 Jul 2018 12:36:16 +0900
changeset 428520 dea04c3e53869104c0e79e6b5ecc4cd9df2f0d17
parent 428519 1a5bdbbfc37535b866b79015bbe849bf8f6e42bd
child 428521 49f82b7a2cb1991eec2db836efca5762d1e50a06
push id34337
push userncsoregi@mozilla.com
push dateThu, 26 Jul 2018 21:58:45 +0000
treeherdermozilla-central@8f2f847b2f9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1434305
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1434305 - Part 2: Add IterateLazyScripts. r=jimb
js/src/gc/GC.h
js/src/gc/PublicIterators.cpp
js/src/vm/Debugger.cpp
--- a/js/src/gc/GC.h
+++ b/js/src/gc/GC.h
@@ -107,23 +107,28 @@ IterateHeapUnbarrieredForZone(JSContext*
 /*
  * Invoke chunkCallback on every in-use chunk.
  */
 extern void
 IterateChunks(JSContext* cx, void* data, IterateChunkCallback chunkCallback);
 
 typedef void (*IterateScriptCallback)(JSRuntime* rt, void* data, JSScript* script,
                                       const JS::AutoRequireNoGC& nogc);
+typedef void (*IterateLazyScriptCallback)(JSRuntime* rt, void* data, LazyScript* lazyScript,
+                                          const JS::AutoRequireNoGC& nogc);
 
 /*
  * Invoke scriptCallback on every in-use script for the given realm or for all
  * realms if it is null.
  */
 extern void
 IterateScripts(JSContext* cx, JS::Realm* realm, void* data, IterateScriptCallback scriptCallback);
+extern void
+IterateLazyScripts(JSContext* cx, JS::Realm* realm, void* data,
+                   IterateLazyScriptCallback lazyScriptCallback);
 
 JS::Realm*
 NewRealm(JSContext* cx, JSPrincipals* principals, const JS::RealmOptions& options);
 
 namespace gc {
 
 void FinishGC(JSContext* cx);
 
--- a/js/src/gc/PublicIterators.cpp
+++ b/js/src/gc/PublicIterators.cpp
@@ -79,38 +79,112 @@ js::IterateChunks(JSContext* cx, void* d
 {
     AutoPrepareForTracing prep(cx);
     AutoLockGC lock(cx->runtime());
 
     for (auto chunk = cx->runtime()->gc.allNonEmptyChunks(lock); !chunk.done(); chunk.next())
         chunkCallback(cx->runtime(), data, chunk);
 }
 
-void
-js::IterateScripts(JSContext* cx, Realm* realm, void* data, IterateScriptCallback scriptCallback)
+static void
+TraverseInnerLazyScriptsForLazyScript(JSContext* cx, void* data, LazyScript* enclosingLazyScript,
+                                      IterateLazyScriptCallback lazyScriptCallback,
+                                      const JS::AutoRequireNoGC& nogc)
+{
+    GCPtrFunction* innerFunctions = enclosingLazyScript->innerFunctions();
+    for (size_t i = 0, len = enclosingLazyScript->numInnerFunctions(); i < len; i++) {
+        JSFunction* fun = innerFunctions[i];
+
+        // LazyScript::CreateForXDR temporarily initializes innerFunctions with
+        // its own function, but it should be overwritten with correct
+        // inner functions before getting inserted into parent's innerFunctions.
+        MOZ_ASSERT(fun != enclosingLazyScript->functionNonDelazifying());
+
+        if (!fun->isInterpretedLazy())
+            return;
+
+        LazyScript* lazyScript = fun->lazyScript();
+        MOZ_ASSERT(lazyScript->hasEnclosingScope() || lazyScript->hasEnclosingLazyScript());
+        MOZ_ASSERT_IF(lazyScript->hasEnclosingLazyScript(),
+                      lazyScript->enclosingLazyScript() == enclosingLazyScript);
+
+        lazyScriptCallback(cx->runtime(), data, lazyScript, nogc);
+
+        TraverseInnerLazyScriptsForLazyScript(cx, data, lazyScript, lazyScriptCallback, nogc);
+    }
+}
+
+static inline void
+DoScriptCallback(JSContext* cx,  void* data, LazyScript* lazyScript,
+                 IterateLazyScriptCallback lazyScriptCallback,
+                 const JS::AutoRequireNoGC& nogc)
+{
+    // We call the callback only for the LazyScript that:
+    //   (a) its enclosing script has ever been fully compiled and
+    //       itself is delazifyable (handled in this function)
+    //   (b) it is contained in the (a)'s inner function tree
+    //       (handled in TraverseInnerLazyScriptsForLazyScript)
+    if (!lazyScript->enclosingScriptHasEverBeenCompiled())
+        return;
+
+    lazyScriptCallback(cx->runtime(), data, lazyScript, nogc);
+
+    TraverseInnerLazyScriptsForLazyScript(cx, data, lazyScript, lazyScriptCallback, nogc);
+}
+
+static inline void
+DoScriptCallback(JSContext* cx,  void* data, JSScript* script,
+                 IterateScriptCallback scriptCallback,
+                 const JS::AutoRequireNoGC& nogc)
+{
+    // We check for presence of script->isUncompleted() because it is
+    // possible that the script was created and thus exposed to GC, but *not*
+    // fully initialized from fullyInit{FromEmitter,Trivial} due to errors.
+    if (script->isUncompleted())
+        return;
+
+    scriptCallback(cx->runtime(), data, script, nogc);
+}
+
+template <typename T, typename Callback>
+static void
+IterateScriptsImpl(JSContext* cx, Realm* realm, void* data, Callback scriptCallback)
 {
     MOZ_ASSERT(!cx->suppressGC);
     AutoEmptyNursery empty(cx);
     AutoPrepareForTracing prep(cx);
     JS::AutoSuppressGCAnalysis nogc;
 
     if (realm) {
         Zone* zone = realm->zone();
-        for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next()) {
+        for (auto script = zone->cellIter<T>(empty); !script.done(); script.next()) {
             if (script->realm() == realm)
-                scriptCallback(cx->runtime(), data, script, nogc);
+                DoScriptCallback(cx, data, script, scriptCallback, nogc);
         }
     } else {
         for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
-            for (auto script = zone->cellIter<JSScript>(empty); !script.done(); script.next())
-                scriptCallback(cx->runtime(), data, script, nogc);
+            for (auto script = zone->cellIter<T>(empty); !script.done(); script.next())
+                DoScriptCallback(cx, data, script, scriptCallback, nogc);
         }
     }
 }
 
+void
+js::IterateScripts(JSContext* cx, Realm* realm, void* data, IterateScriptCallback scriptCallback)
+{
+    IterateScriptsImpl<JSScript>(cx, realm, data, scriptCallback);
+}
+
+void
+js::IterateLazyScripts(JSContext* cx, Realm* realm, void* data,
+                       IterateLazyScriptCallback scriptCallback)
+{
+    IterateScriptsImpl<LazyScript>(cx, realm, data, scriptCallback);
+}
+
 static void
 IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data)
 {
     for (auto kind : ObjectAllocKinds()) {
         for (GrayObjectIter obj(zone, kind); !obj.done(); obj.next()) {
             if (obj->asTenured().isMarkedGray())
                 cellCallback(data, JS::GCCellPtr(obj.get()));
         }
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4568,21 +4568,17 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
     }
 
     /*
      * If |script| matches this query, append it to |vector| or place it in
      * |innermostForRealm|, as appropriate. Set |oom| if an out of memory
      * condition occurred.
      */
     void consider(JSScript* script, const JS::AutoRequireNoGC& nogc) {
-        // We check for presence of script->isUncompleted() because it is
-        // possible that the script was created and thus exposed to GC, but
-        // *not* fully initialized from fullyInit{FromEmitter,Trivial} due to
-        // errors.
-        if (oom || script->selfHosted() || script->isUncompleted())
+        if (oom || script->selfHosted())
             return;
         Realm* realm = script->realm();
         if (!realms.has(realm))
             return;
         if (urlCString.ptr()) {
             bool gotFilename = false;
             if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0)
                 gotFilename = true;