diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c9fc2986529..8b543394a62 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1977,10 +1977,12 @@ template static inline void PropagateTransitiveParseFlags(const T *inner, U *outer) { - if (inner->bindingsAccessedDynamically()) - outer->setBindingsAccessedDynamically(); - if (inner->hasDebuggerStatement()) - outer->setHasDebuggerStatement(); + if (inner->bindingsAccessedDynamically()) + outer->setBindingsAccessedDynamically(); + if (inner->hasDebuggerStatement()) + outer->setHasDebuggerStatement(); + if (inner->hasDirectEval()) + outer->setHasDirectEval(); } template @@ -7586,6 +7588,7 @@ Parser::memberExpr(TokenKind tt, bool allowCallSyntax) /* Select JSOP_EVAL and flag pc as heavyweight. */ op = pc->sc->strict ? JSOP_STRICTEVAL : JSOP_EVAL; pc->sc->setBindingsAccessedDynamically(); + pc->sc->setHasDirectEval(); /* * In non-strict mode code, direct calls to eval can add diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index aca90af8be2..93e82bdea59 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -58,11 +58,15 @@ class AnyContextFlags // scope chain. bool hasDebuggerStatement:1; + // A direct eval occurs in the body of the script. + bool hasDirectEval:1; + public: AnyContextFlags() : hasExplicitUseStrict(false), bindingsAccessedDynamically(false), - hasDebuggerStatement(false) + hasDebuggerStatement(false), + hasDirectEval(false) { } }; @@ -195,10 +199,12 @@ class SharedContext bool hasExplicitUseStrict() const { return anyCxFlags.hasExplicitUseStrict; } bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; } bool hasDebuggerStatement() const { return anyCxFlags.hasDebuggerStatement; } + bool hasDirectEval() const { return anyCxFlags.hasDirectEval; } void setExplicitUseStrict() { anyCxFlags.hasExplicitUseStrict = true; } void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; } void setHasDebuggerStatement() { anyCxFlags.hasDebuggerStatement = true; } + void setHasDirectEval() { anyCxFlags.hasDirectEval = true; } inline bool allLocalsAliased(); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9133e3ede49..b6622bc1d5a 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1422,12 +1422,19 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti RootedScript script(cx, lazy->maybeScript()); + // Only functions without inner functions or direct eval are + // re-lazified. Functions with either of those are on the static scope + // chain of their inner functions, or in the case of eval, possibly + // eval'd inner functions. This prohibits re-lazification as + // StaticScopeIter queries isHeavyweight of those functions, which + // requires a non-lazy script. + bool canRelazify = !lazy->numInnerFunctions() && !lazy->hasDirectEval(); + if (script) { fun->setUnlazifiedScript(script); // Remember the lazy script on the compiled script, so it can be // stored on the function again in case of re-lazification. - // Only functions without inner functions are re-lazified. - if (!lazy->numInnerFunctions()) + if (canRelazify) script->setLazyScript(lazy); return true; } @@ -1451,7 +1458,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti // Additionally, the lazy script cache is not used during incremental // GCs, to avoid resurrecting dead scripts after incremental sweeping // has started. - if (!lazy->numInnerFunctions() && !JS::IsIncrementalGCInProgress(cx->runtime())) { + if (canRelazify && !JS::IsIncrementalGCInProgress(cx->runtime())) { LazyScriptCache::Lookup lookup(cx, lazy); cx->runtime()->lazyScriptCache.lookup(lookup, script.address()); } @@ -1496,7 +1503,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti lazy->initScript(script); // Try to insert the newly compiled script into the lazy script cache. - if (!lazy->numInnerFunctions()) { + if (canRelazify) { // A script's starting column isn't set by the bytecode emitter, so // specify this from the lazy script so that if an identical lazy // script is encountered later a match can be determined. diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 25a761a3eac..418c7555dd7 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3789,6 +3789,7 @@ LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, p.strict = false; p.bindingsAccessedDynamically = false; p.hasDebuggerStatement = false; + p.hasDirectEval = false; p.directlyInsideEval = false; p.usesArgumentsApplyAndThis = false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index e3bfd55cab9..8ed02b22bd8 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1882,7 +1882,7 @@ class LazyScript : public gc::TenuredCell uint32_t version : 8; uint32_t numFreeVariables : 24; - uint32_t numInnerFunctions : 23; + uint32_t numInnerFunctions : 22; uint32_t generatorKindBits : 2; @@ -1890,6 +1890,7 @@ class LazyScript : public gc::TenuredCell uint32_t strict : 1; uint32_t bindingsAccessedDynamically : 1; uint32_t hasDebuggerStatement : 1; + uint32_t hasDirectEval : 1; uint32_t directlyInsideEval : 1; uint32_t usesArgumentsApplyAndThis : 1; uint32_t hasBeenCloned : 1; @@ -2015,6 +2016,13 @@ class LazyScript : public gc::TenuredCell p_.hasDebuggerStatement = true; } + bool hasDirectEval() const { + return p_.hasDirectEval; + } + void setHasDirectEval() { + p_.hasDirectEval = true; + } + bool directlyInsideEval() const { return p_.directlyInsideEval; }