Bug 1238555 - Always update the LazyScript's static scope chain when emitting functions. (r=till)
authorShu-yu Guo <shu@rfrn.org>
Fri, 18 Mar 2016 15:07:26 -0700
changeset 289425 ed7f0e024ce8122abe08d98eba42587e9b349777
parent 289424 574e8c5132b9ced6db7395b9f85440f43649555f
child 289426 4f2f430361c4f37d51fefa27069cf7536ebdebc7
push id30102
push userryanvm@gmail.com
push dateSat, 19 Mar 2016 15:23:17 +0000
treeherdermozilla-central@720fb3d55e28 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1238555
milestone48.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 1238555 - Always update the LazyScript's static scope chain when emitting functions. (r=till)
js/src/frontend/BytecodeEmitter.cpp
js/src/jit-test/tests/gc/bug-1238555.js
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6385,21 +6385,26 @@ BytecodeEmitter::emitFunction(ParseNode*
      */
     if (fun->isInterpreted()) {
         bool singleton = checkRunOnceContext();
         if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
             return false;
 
         SharedContext* outersc = sc;
         if (fun->isInterpretedLazy()) {
-            if (!fun->lazyScript()->sourceObject()) {
-                JSObject* scope = innermostStaticScope();
-                JSObject* source = script->sourceObject();
-                fun->lazyScript()->setParent(scope, &source->as<ScriptSourceObject>());
-            }
+            // We need to update the static scope chain regardless of whether
+            // the LazyScript has already been initialized, due to the case
+            // where we previously successfully compiled an inner function's
+            // lazy script but failed to compile the outer script after the
+            // fact. If we attempt to compile the outer script again, the
+            // static scope chain will be newly allocated and will mismatch
+            // the previously compiled LazyScript's.
+            ScriptSourceObject* source = &script->sourceObject()->as<ScriptSourceObject>();
+            JSObject* scope = innermostStaticScope();
+            fun->lazyScript()->setEnclosingScopeAndSource(scope, source);
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
 
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1238555.js
@@ -0,0 +1,12 @@
+if (!('oomTest' in this))
+  quit();
+
+oomTest(
+  function x() {
+    try {
+      eval('let ')
+    } catch (ex) {
+      (function() {})()
+    }
+  }
+);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -4276,21 +4276,24 @@ LazyScript::initScript(JSScript* script)
 void
 LazyScript::resetScript()
 {
     MOZ_ASSERT(script_.unbarrieredGet());
     script_.set(nullptr);
 }
 
 void
-LazyScript::setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject)
+LazyScript::setEnclosingScopeAndSource(JSObject* enclosingScope, ScriptSourceObject* sourceObject)
 {
-    MOZ_ASSERT(!sourceObject_ && !enclosingScope_);
     MOZ_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment());
     MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
+    // This method may be called to update the enclosing scope. See comment
+    // above the callsite in BytecodeEmitter::emitFunction.
+    MOZ_ASSERT_IF(sourceObject_, sourceObject_ == sourceObject && enclosingScope_);
+    MOZ_ASSERT_IF(!sourceObject_, !enclosingScope_);
 
     enclosingScope_ = enclosingScope;
     sourceObject_ = sourceObject;
 }
 
 ScriptSourceObject*
 LazyScript::sourceObject() const
 {
@@ -4391,17 +4394,17 @@ LazyScript::Create(ExclusiveContext* cx,
 
     HeapPtrFunction* functions = res->innerFunctions();
     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
         functions[i].init(dummyFun);
 
     // Set the enclosing scope of the lazy function, this would later be
     // used to define the environment when the function would be used.
     MOZ_ASSERT(!res->sourceObject());
-    res->setParent(enclosingScope, &sourceObjectScript->scriptSourceUnwrap());
+    res->setEnclosingScopeAndSource(enclosingScope, &sourceObjectScript->scriptSourceUnwrap());
 
     MOZ_ASSERT(!res->hasScript());
     if (script)
         res->initScript(script);
 
     return res;
 }
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -2267,17 +2267,17 @@ class LazyScript : public gc::TenuredCel
     bool mutedErrors() const {
         return scriptSource()->mutedErrors();
     }
     JSVersion version() const {
         JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
         return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version);
     }
 
-    void setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject);
+    void setEnclosingScopeAndSource(JSObject* enclosingScope, ScriptSourceObject* sourceObject);
 
     uint32_t numFreeVariables() const {
         return p_.numFreeVariables;
     }
     FreeVariable* freeVariables() {
         return (FreeVariable*)table_;
     }