Bug 1157963 - Fix LazyScript::hasUncompiledEnclosingScript and don't delazify functions about to be finalized. r=jimb, a=lizzard
authorShu-yu Guo <shu@rfrn.org>
Sun, 26 Apr 2015 00:00:09 -0700
changeset 267345 f5bd249ff2b09a2ee637d6e6457d9a7059ca63fc
parent 267344 1cc599a8ff531f2ecb70b9ae2adc09a70aeb69a0
child 267346 34a7e8ac4f2d0ee051b34c943fa6257fae0c9cca
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb, lizzard
bugs1157963
milestone39.0a2
Bug 1157963 - Fix LazyScript::hasUncompiledEnclosingScript and don't delazify functions about to be finalized. r=jimb, a=lizzard
js/src/jit-test/tests/debug/bug980585.js
js/src/jscompartment.cpp
js/src/jsfun.h
js/src/jsscript.cpp
--- a/js/src/jit-test/tests/debug/bug980585.js
+++ b/js/src/jit-test/tests/debug/bug980585.js
@@ -1,10 +1,10 @@
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 try {
-  g.eval("function f() { var array = ['a', 'b']; [1].map(function () {}); return {array}; }");
+  g.eval("function f() { [1].map(function () {}); const x = 42; x = 43; } f();");
 } catch (e) {
   // Ignore the syntax error.
 }
 
 dbg.findScripts();
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -752,26 +752,35 @@ CreateLazyScriptsForCompartment(JSContex
     // script did not escape the script.
     //
     // Note that while we ideally iterate over LazyScripts, LazyScripts do not
     // currently stand in 1-1 relation with JSScripts; JSFunctions with the
     // same LazyScript may create different JSScripts due to relazification of
     // clones. See bug 1105306.
     for (gc::ZoneCellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) {
         JSObject* obj = i.get<JSObject>();
-        if (obj->compartment() == cx->compartment() && obj->is<JSFunction>()) {
-            JSFunction* fun = &obj->as<JSFunction>();
-            if (fun->isInterpretedLazy()) {
-                LazyScript* lazy = fun->lazyScriptOrNull();
-                if (lazy && lazy->sourceObject() && !lazy->maybeScript() &&
-                    !lazy->hasUncompiledEnclosingScript())
-                {
-                    if (!lazyFunctions.append(fun))
-                        return false;
-                }
+
+        // Sweeping is incremental; take care to not delazify functions that
+        // are about to be finalized. GC things referenced by objects that are
+        // about to be finalized (e.g., in slots) may already be freed.
+        if (gc::IsAboutToBeFinalizedUnbarriered(&obj) ||
+            obj->compartment() != cx->compartment() ||
+            !obj->is<JSFunction>())
+        {
+            continue;
+        }
+
+        JSFunction* fun = &obj->as<JSFunction>();
+        if (fun->isInterpretedLazy()) {
+            LazyScript* lazy = fun->lazyScriptOrNull();
+            if (lazy && lazy->sourceObject() && !lazy->maybeScript() &&
+                !lazy->hasUncompiledEnclosingScript())
+            {
+                if (!lazyFunctions.append(fun))
+                    return false;
             }
         }
     }
 
     // Create scripts for each lazy function, updating the list of functions to
     // process with any newly exposed inner functions in created scripts.
     // A function cannot be delazified until its outer script exists.
     for (size_t i = 0; i < lazyFunctions.length(); i++) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -313,19 +313,26 @@ class JSFunction : public js::NativeObje
 
             flags_ &= ~INTERPRETED_LAZY;
             flags_ |= INTERPRETED;
             initScript(script);
         }
         return nonLazyScript();
     }
 
-    JSScript* nonLazyScript() const {
+    // The state of a JSFunction whose script errored out during bytecode
+    // compilation. Such JSFunctions are only reachable via GC iteration and
+    // not from script.
+    bool hasUncompiledScript() const {
         MOZ_ASSERT(hasScript());
-        MOZ_ASSERT(u.i.s.script_);
+        return !u.i.s.script_;
+    }
+
+    JSScript* nonLazyScript() const {
+        MOZ_ASSERT(!hasUncompiledScript());
         return u.i.s.script_;
     }
 
     bool getLength(JSContext* cx, uint16_t* length) {
         JS::RootedFunction self(cx, this);
         if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
             return false;
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3878,17 +3878,17 @@ LazyScript::hasUncompiledEnclosingScript
     //
     // If the enclosing scope is a function with a null script or has a script
     // without code, it was not successfully compiled.
 
     if (!enclosingScope() || !enclosingScope()->is<JSFunction>())
         return false;
 
     JSFunction& fun = enclosingScope()->as<JSFunction>();
-    return fun.isInterpreted() && (!fun.hasScript() || !fun.nonLazyScript()->code());
+    return !fun.hasScript() || fun.hasUncompiledScript() || !fun.nonLazyScript()->code();
 }
 
 uint32_t
 LazyScript::staticLevel(JSContext* cx) const
 {
     for (StaticScopeIter<NoGC> ssi(enclosingScope()); !ssi.done(); ssi++) {
         if (ssi.type() == StaticScopeIter<NoGC>::Function)
             return ssi.funScript()->staticLevel() + 1;