From 0f91ae27d65b6b0311cd60cedd94ec9f3b15bbbe Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 17 Mar 2016 18:53:04 -0700 Subject: [PATCH] Bug 1238555 - Always update the LazyScript's static scope chain when emitting functions. (r=till) --- js/src/frontend/BytecodeEmitter.cpp | 15 ++++++++++----- js/src/jit-test/tests/gc/bug-1238555.js | 9 +++++++++ js/src/jsscript.cpp | 9 ++++++--- js/src/jsscript.h | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 js/src/jit-test/tests/gc/bug-1238555.js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c206960c6a0e..43122d3e4d45 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -6390,11 +6390,16 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) SharedContext* outersc = sc; if (fun->isInterpretedLazy()) { - if (!fun->lazyScript()->sourceObject()) { - JSObject* scope = innermostStaticScope(); - JSObject* source = script->sourceObject(); - fun->lazyScript()->setParent(scope, &source->as()); - } + // 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(); + JSObject* scope = innermostStaticScope(); + fun->lazyScript()->setEnclosingScopeAndSource(scope, source); if (emittingRunOnceLambda) fun->lazyScript()->setTreatAsRunOnce(); } else { diff --git a/js/src/jit-test/tests/gc/bug-1238555.js b/js/src/jit-test/tests/gc/bug-1238555.js new file mode 100644 index 000000000000..f9e139a89528 --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-1238555.js @@ -0,0 +1,9 @@ +oomTest( + function x() { + try { + eval('let ') + } catch (ex) { + (function() {})() + } + } +); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index d16a6fb0525f..ba87306a9baa 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -4281,11 +4281,14 @@ LazyScript::resetScript() } 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; @@ -4396,7 +4399,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun, // 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) diff --git a/js/src/jsscript.h b/js/src/jsscript.h index bc3a7fe4c9d5..dfdbe416630b 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -2272,7 +2272,7 @@ class LazyScript : public gc::TenuredCell 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;