Bug 1165486 - Remove PollutedGlobalScopeOption in favor of using the static scope chain to detect non-syntactic scopes. (r=luke)
☠☠ backed out by b4e617011c42 ☠ ☠
authorShu-yu Guo <shu@rfrn.org>
Wed, 17 Jun 2015 21:26:57 -0700
changeset 280266 5e88d482f5e886939ab8daefc52b378a57fd1b2f
parent 280265 513bbca6fb6cff60299aba2cd111dbf8dffac538
child 280267 0e00d8a3ca1d33b2c2d7b5cd298b31f2d11ff853
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1165486
milestone41.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 1165486 - Remove PollutedGlobalScopeOption in favor of using the static scope chain to detect non-syntactic scopes. (r=luke)
js/src/builtin/Eval.cpp
js/src/builtin/TestingFunctions.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Debugger.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack.cpp
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -207,28 +207,16 @@ TryEvalJSON(JSContext* cx, JSLinearStrin
     if (!linearChars.init(cx, str))
         return EvalJSON_Failure;
 
     return linearChars.isLatin1()
            ? ParseEvalStringAsJSON(cx, linearChars.latin1Range(), rval)
            : ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
 }
 
-static bool
-HasPollutedScopeChain(JSObject* scopeChain)
-{
-    while (scopeChain) {
-        if (scopeChain->is<DynamicWithObject>())
-            return true;
-        scopeChain = scopeChain->enclosingScope();
-    }
-
-    return false;
-}
-
 // Define subset of ExecuteType so that casting performs the injection.
 enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
 
 // Common code implementing direct and indirect eval.
 //
 // Evaluate call.argv[2], if it is a string, in the context of the given calling
 // frame, with the provided scope chain, with the semantics of either a direct
 // or indirect eval (see ES5 10.4.2).  If this is an indirect eval, scopeobj
@@ -321,23 +309,18 @@ EvalKernel(JSContext* cx, const CallArgs
 
         RootedObject enclosing(cx);
         if (evalType == DIRECT_EVAL)
             enclosing = callerScript->innermostStaticScope(pc);
         Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosing));
         if (!staticScope)
             return false;
 
-        bool hasPollutedGlobalScope =
-            HasPollutedScopeChain(scopeobj) ||
-            (evalType == DIRECT_EVAL && callerScript->hasPollutedGlobalScope());
-
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
-               .setHasPollutedScope(hasPollutedGlobalScope)
                .setIsRunOnce(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc));
 
         AutoStableStringChars linearChars(cx);
@@ -399,31 +382,29 @@ js::DirectEvalStringFromIon(JSContext* c
 
     if (!esg.foundScript()) {
         RootedScript maybeScript(cx);
         const char* filename;
         unsigned lineno;
         bool mutedErrors;
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
-                                              &mutedErrors, CALLED_FROM_JSOP_EVAL);
+                                             &mutedErrors, CALLED_FROM_JSOP_EVAL);
 
         const char* introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
         RootedObject enclosing(cx, callerScript->innermostStaticScope(pc));
         Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosing));
         if (!staticScope)
             return false;
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
-               .setHasPollutedScope(HasPollutedScopeChain(scopeobj) ||
-                                    callerScript->hasPollutedGlobalScope())
                .setIsRunOnce(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(IsStrictEvalPC(pc));
 
         AutoStableStringChars linearChars(cx);
@@ -506,17 +487,20 @@ js::ExecuteInGlobalAndReturnScope(JSCont
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, global);
     MOZ_ASSERT(global->is<GlobalObject>());
     MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
 
     RootedScript script(cx, scriptArg);
     if (script->compartment() != cx->compartment()) {
-        script = CloneScript(cx, nullptr, nullptr, script);
+        Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr));
+        if (!staticScope)
+            return false;
+        script = CloneGlobalScript(cx, staticScope, script);
         if (!script)
             return false;
 
         Debugger::onNewScript(cx, script);
     }
 
     RootedObject scope(cx, JS_NewPlainObject(cx));
     if (!scope)
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2267,21 +2267,20 @@ EvalReturningScope(JSContext* cx, unsign
     JS::AutoFilename filename;
     unsigned lineno;
 
     DescribeScriptedCaller(cx, &filename, &lineno);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(filename.get(), lineno);
     options.setNoScriptRval(true);
-    options.setHasPollutedScope(true);
 
     JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
     RootedScript script(cx);
-    if (!JS::Compile(cx, options, srcBuf, &script))
+    if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script))
         return false;
 
     if (global) {
         global = CheckedUnwrap(global);
         if (!global) {
             JS_ReportError(cx, "Permission denied to access global");
             return false;
         }
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -139,20 +139,21 @@ MaybeCheckEvalFreeVariables(ExclusiveCon
             scope = scope->enclosingScope();
         }
     }
 
     return true;
 }
 
 static inline bool
-CanLazilyParse(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
+CanLazilyParse(ExclusiveContext* cx, HandleObject staticScope,
+               const ReadOnlyCompileOptions& options)
 {
     return options.canLazilyParse &&
-           !options.hasPollutedGlobalScope &&
+           !HasNonSyntacticStaticScopeChain(staticScope) &&
            !cx->compartment()->options().disableLazyParsing() &&
            !cx->compartment()->options().discardSource() &&
            !options.sourceIsLazy;
 }
 
 static void
 MarkFunctionsWithinEvalScript(JSScript* script)
 {
@@ -255,17 +256,17 @@ frontend::CompileScript(ExclusiveContext
 
     if (!cx->compartment()->options().discardSource()) {
         if (options.sourceIsLazy)
             ss->setSourceRetrievable();
         else if (!ss->setSourceCopy(cx, srcBuf, false, sct))
             return nullptr;
     }
 
-    bool canLazilyParse = CanLazilyParse(cx, options);
+    bool canLazilyParse = CanLazilyParse(cx, enclosingStaticScope, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
         syntaxParser.emplace(cx, alloc, options, srcBuf.get(), srcBuf.length(),
                              /* foldConstants = */ false,
                              (Parser<SyntaxParseHandler>*) nullptr,
                              (LazyScript*) nullptr);
 
@@ -556,17 +557,17 @@ CompileFunctionBody(JSContext* cx, Mutab
 
     SourceCompressionTask sct(cx);
     MOZ_ASSERT(!options.sourceIsLazy);
     if (!cx->compartment()->options().discardSource()) {
         if (!ss->setSourceCopy(cx, srcBuf, true, &sct))
             return false;
     }
 
-    bool canLazilyParse = CanLazilyParse(cx, options);
+    bool canLazilyParse = CanLazilyParse(cx, enclosingStaticScope, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
         syntaxParser.emplace(cx, &cx->tempLifoAlloc(),
                              options, srcBuf.get(), srcBuf.length(),
                              /* foldConstants = */ false,
                              (Parser<SyntaxParseHandler>*) nullptr,
                              (LazyScript*) nullptr);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3401,16 +3401,29 @@ BytecodeEmitter::emitFunctionScript(Pars
     /*
      * IonBuilder has assumptions about what may occur immediately after
      * script->main (e.g., in the case of destructuring params). Thus, put the
      * following ops into the range [script->code, script->main). Note:
      * execution starts from script->code, so this has no semantic effect.
      */
 
     FunctionBox* funbox = sc->asFunctionBox();
+
+    // Link the function and the script to each other, so that StaticScopeIter
+    // may walk the scope chain of currently compiling scripts.
+    RootedFunction fun(cx, funbox->function());
+    MOZ_ASSERT(fun->isInterpreted());
+
+    script->setFunction(fun);
+
+    if (fun->isInterpretedLazy())
+        fun->setUnlazifiedScript(script);
+    else
+        fun->setScript(script);
+
     if (funbox->argumentsHasLocalBinding()) {
         MOZ_ASSERT(offset() == 0);  /* See JSScript::argumentsBytecode. */
         switchToPrologue();
         if (!emit1(JSOP_ARGUMENTS))
             return false;
         InternalBindingsHandle bindings(script, &script->bindings);
         BindingIter bi = Bindings::argumentsBinding(cx, bindings);
         if (script->bindingIsAliased(bi)) {
@@ -3504,25 +3517,16 @@ BytecodeEmitter::emitFunctionScript(Pars
      * If this function is only expected to run once, mark the script so that
      * initializers created within it may be given more precise types.
      */
     if (runOnce) {
         script->setTreatAsRunOnce();
         MOZ_ASSERT(!script->hasRunOnce());
     }
 
-    /* Initialize fun->script() so that the debugger has a valid fun->script(). */
-    RootedFunction fun(cx, script->functionNonDelazifying());
-    MOZ_ASSERT(fun->isInterpreted());
-
-    if (fun->isInterpretedLazy())
-        fun->setUnlazifiedScript(script);
-    else
-        fun->setScript(script);
-
     tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 bool
 BytecodeEmitter::maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* result)
 {
@@ -5760,18 +5764,16 @@ BytecodeEmitter::emitFunction(ParseNode*
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the parent.
             Rooted<JSScript*> parent(cx, script);
             CompileOptions options(cx, parser->options());
             options.setMutedErrors(parent->mutedErrors())
-                   .setHasPollutedScope(parent->hasPollutedGlobalScope())
-                   .setSelfHostingMode(parent->selfHosted())
                    .setNoScriptRval(false)
                    .setForEval(false)
                    .setVersion(parent->getVersion());
 
             Rooted<JSObject*> enclosingScope(cx, enclosingStaticScope());
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
                                                           parent->staticLevel() + 1,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3278,22 +3278,32 @@ JS::GetSelfHostedFunction(JSContext* cx,
         return nullptr;
     RootedValue funVal(cx);
     if (!cx->global()->getSelfHostedFunction(cx, shName, name, nargs, &funVal))
         return nullptr;
     return &funVal.toObject().as<JSFunction>();
 }
 
 static bool
-CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
-                                MutableHandleObject dynamicScopeObj,
-                                MutableHandleObject staticScopeObj)
-{
-    return js::CreateScopeObjectsForScopeChain(cx, scopeChain, cx->global(),
-                                               dynamicScopeObj, staticScopeObj);
+CreateNonSyntacticScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
+                             MutableHandleObject dynamicScopeObj,
+                             MutableHandle<ScopeObject*> staticScopeObj)
+{
+    if (!js::CreateScopeObjectsForScopeChain(cx, scopeChain, cx->global(), dynamicScopeObj))
+        return false;
+
+    if (scopeChain.empty()) {
+        staticScopeObj.set(nullptr);
+    } else {
+        staticScopeObj.set(StaticNonSyntacticScopeObjects::create(cx, nullptr));
+        if (!staticScopeObj)
+            return false;
+    }
+
+    return true;
 }
 
 static bool
 IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope)
 {
     if (!fun->isInterpreted())
         return true;
 
@@ -3832,31 +3842,49 @@ JS::CompileOptions::CompileOptions(JSCon
     this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion();
 
     strictOption = cx->runtime()->options().strictMode();
     extraWarningsOption = cx->compartment()->options().extraWarnings(cx);
     werrorOption = cx->runtime()->options().werror();
     asmJSOption = cx->runtime()->options().asmJS();
 }
 
-bool
-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
-            SourceBufferHolder& srcBuf, MutableHandleScript script)
+static bool
+Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+        SourceBufferHolder& srcBuf, Handle<ScopeObject*> topStaticScope,
+        MutableHandleScript script)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     AutoLastFrameCheck lfc(cx);
 
     script.set(frontend::CompileScript(cx, &cx->tempLifoAlloc(), cx->global(),
-                                       nullptr, nullptr, options, srcBuf));
+                                       topStaticScope, nullptr, options, srcBuf));
     return !!script;
 }
 
 bool
+JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+                                SourceBufferHolder& srcBuf, MutableHandleScript script)
+{
+    Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr));
+    if (!staticScope)
+        return false;
+    return ::Compile(cx, options, srcBuf, staticScope, script);
+}
+
+bool
+JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+            SourceBufferHolder& srcBuf, MutableHandleScript script)
+{
+    return ::Compile(cx, options, srcBuf, nullptr, script);
+}
+
+bool
 JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
             const char16_t* chars, size_t length, MutableHandleScript script)
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
     return Compile(cx, options, srcBuf, script);
 }
 
 bool
@@ -4072,37 +4100,40 @@ CompileFunction(JSContext* cx, const Rea
     }
 
     fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom,
                                 gc::AllocKind::FUNCTION, TenuredObject,
                                 enclosingDynamicScope));
     if (!fun)
         return false;
 
-    // Make sure to handle cases when we have a polluted scopechain.
+    // Make sure the static scope chain matches up when we have a
+    // non-syntactic scope.
+    MOZ_ASSERT_IF(!enclosingDynamicScope->is<GlobalObject>(),
+                  HasNonSyntacticStaticScopeChain(enclosingStaticScope));
+
     CompileOptions options(cx, optionsArg);
-    if (!enclosingDynamicScope->is<GlobalObject>())
-        options.setHasPollutedScope(true);
-
     if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf,
                                        enclosingStaticScope))
+    {
         return false;
+    }
 
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain,
                     const ReadOnlyCompileOptions& options,
                     const char* name, unsigned nargs, const char* const* argnames,
                     SourceBufferHolder& srcBuf, MutableHandleFunction fun)
 {
     RootedObject dynamicScopeObj(cx);
-    RootedObject staticScopeObj(cx);
-    if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScopeObj, &staticScopeObj))
+    Rooted<ScopeObject*> staticScopeObj(cx);
+    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScopeObj, &staticScopeObj))
         return false;
 
     return CompileFunction(cx, options, name, nargs, argnames,
                            srcBuf, dynamicScopeObj, staticScopeObj, fun);
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain,
@@ -4166,43 +4197,44 @@ JS_DecompileFunctionBody(JSContext* cx, 
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, fun);
     return FunctionToString(cx, fun, true, !(indent & JS_DONT_PRETTY_PRINT));
 }
 
 MOZ_NEVER_INLINE static bool
-ExecuteScript(JSContext* cx, HandleObject obj, HandleScript scriptArg, jsval* rval)
-{
-    RootedScript script(cx, scriptArg);
-
+ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, jsval* rval)
+{
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, scriptArg);
-
-    if (!script->hasNonSyntacticScope() && !obj->is<GlobalObject>()) {
-        script = CloneScript(cx, nullptr, nullptr, script, HasPollutedGlobalScope);
-        if (!script)
-            return false;
-        js::Debugger::onNewScript(cx, script);
-    }
+    assertSameCompartment(cx, scope, script);
+    MOZ_ASSERT_IF(!scope->is<GlobalObject>(), script->hasNonSyntacticScope());
     AutoLastFrameCheck lfc(cx);
-    return Execute(cx, script, *obj, rval);
+    return Execute(cx, script, *scope, rval);
 }
 
 static bool
 ExecuteScript(JSContext* cx, AutoObjectVector& scopeChain, HandleScript scriptArg, jsval* rval)
 {
     RootedObject dynamicScope(cx);
-    RootedObject unusedStaticScope(cx);
-    if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
+    Rooted<ScopeObject*> staticScope(cx);
+    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
         return false;
-    return ExecuteScript(cx, dynamicScope, scriptArg, rval);
+
+    RootedScript script(cx, scriptArg);
+    if (!script->hasNonSyntacticScope()) {
+        script = CloneGlobalScript(cx, staticScope, script);
+        if (!script)
+            return false;
+        js::Debugger::onNewScript(cx, script);
+    }
+
+    return ExecuteScript(cx, dynamicScope, script, rval);
 }
 
 MOZ_NEVER_INLINE JS_PUBLIC_API(bool)
 JS_ExecuteScript(JSContext* cx, HandleScript scriptArg, MutableHandleValue rval)
 {
     return ExecuteScript(cx, cx->global(), scriptArg, rval.address());
 }
 
@@ -4238,33 +4270,36 @@ JS::CloneAndExecuteScript(JSContext* cx,
         js::Debugger::onNewScript(cx, script);
     }
     return ExecuteScript(cx, cx->global(), script, nullptr);
 }
 
 static const unsigned LARGE_SCRIPT_LENGTH = 500*1024;
 
 static bool
-Evaluate(JSContext* cx, HandleObject scope, const ReadOnlyCompileOptions& optionsArg,
+Evaluate(JSContext* cx, HandleObject scope, Handle<ScopeObject*> staticScope,
+         const ReadOnlyCompileOptions& optionsArg,
          SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     CompileOptions options(cx, optionsArg);
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, scope);
 
     AutoLastFrameCheck lfc(cx);
 
-    options.setHasPollutedScope(!scope->is<GlobalObject>());
+    MOZ_ASSERT_IF(!scope->is<GlobalObject>(), HasNonSyntacticStaticScopeChain(staticScope));
+
     options.setIsRunOnce(true);
     SourceCompressionTask sct(cx);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                    scope, nullptr, nullptr, options,
-                                                    srcBuf, nullptr, 0, &sct));
+                                                    scope, staticScope,
+                                                    /* evalCaller = */ nullptr, options,
+                                                    srcBuf, /* source = */ nullptr, 0, &sct));
     if (!script)
         return false;
 
     MOZ_ASSERT(script->getVersion() == options.version);
 
     bool result = Execute(cx, script, *scope,
                           options.noScriptRval ? nullptr : rval.address());
     if (!sct.complete())
@@ -4284,44 +4319,44 @@ Evaluate(JSContext* cx, HandleObject sco
     return result;
 }
 
 static bool
 Evaluate(JSContext* cx, AutoObjectVector& scopeChain, const ReadOnlyCompileOptions& optionsArg,
          SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     RootedObject dynamicScope(cx);
-    RootedObject unusedStaticScope(cx);
-    if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
+    Rooted<ScopeObject*> staticScope(cx);
+    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
         return false;
-    return ::Evaluate(cx, dynamicScope, optionsArg, srcBuf, rval);
+    return ::Evaluate(cx, dynamicScope, staticScope, optionsArg, srcBuf, rval);
 }
 
 static bool
 Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
          const char16_t* chars, size_t length, MutableHandleValue rval)
 {
   SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
-  return ::Evaluate(cx, cx->global(), optionsArg, srcBuf, rval);
+  return ::Evaluate(cx, cx->global(), nullptr, optionsArg, srcBuf, rval);
 }
 
 extern JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
              const char* bytes, size_t length, MutableHandleValue rval)
 {
     char16_t* chars;
     if (options.utf8)
         chars = UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(bytes, length), &length).get();
     else
         chars = InflateString(cx, bytes, &length);
     if (!chars)
         return false;
 
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::GiveOwnership);
-    bool ok = ::Evaluate(cx, cx->global(), options, srcBuf, rval);
+    bool ok = ::Evaluate(cx, cx->global(), nullptr, options, srcBuf, rval);
     return ok;
 }
 
 static bool
 Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
          const char* filename, MutableHandleValue rval)
 {
     FileContents buffer(cx);
@@ -4335,17 +4370,17 @@ Evaluate(JSContext* cx, const ReadOnlyCo
     options.setFileAndLine(filename, 1);
     return Evaluate(cx, options, buffer.begin(), buffer.length(), rval);
 }
 
 JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
              SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
-    return ::Evaluate(cx, cx->global(), optionsArg, srcBuf, rval);
+    return ::Evaluate(cx, cx->global(), nullptr, optionsArg, srcBuf, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext* cx, AutoObjectVector& scopeChain, const ReadOnlyCompileOptions& optionsArg,
              SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     return ::Evaluate(cx, scopeChain, optionsArg, srcBuf, rval);
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3441,17 +3441,16 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
         filename_(nullptr),
         introducerFilename_(nullptr),
         sourceMapURL_(nullptr),
         version(JSVERSION_UNKNOWN),
         versionSet(false),
         utf8(false),
         lineno(1),
         column(0),
-        hasPollutedGlobalScope(false),
         isRunOnce(false),
         forEval(false),
         noScriptRval(false),
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         werrorOption(false),
@@ -3481,17 +3480,16 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
     virtual JSScript* introductionScript() const = 0;
 
     // POD options.
     JSVersion version;
     bool versionSet;
     bool utf8;
     unsigned lineno;
     unsigned column;
-    bool hasPollutedGlobalScope;
     // isRunOnce only applies to non-function scripts.
     bool isRunOnce;
     bool forEval;
     bool noScriptRval;
     bool selfHostingMode;
     bool canLazilyParse;
     bool strictOption;
     bool extraWarningsOption;
@@ -3574,17 +3572,16 @@ class JS_FRIEND_API(OwningCompileOptions
     }
     OwningCompileOptions& setVersion(JSVersion v) {
         version = v;
         versionSet = true;
         return *this;
     }
     OwningCompileOptions& setUTF8(bool u) { utf8 = u; return *this; }
     OwningCompileOptions& setColumn(unsigned c) { column = c; return *this; }
-    OwningCompileOptions& setHasPollutedScope(bool p) { hasPollutedGlobalScope = p; return *this; }
     OwningCompileOptions& setIsRunOnce(bool once) { isRunOnce = once; return *this; }
     OwningCompileOptions& setForEval(bool eval) { forEval = eval; return *this; }
     OwningCompileOptions& setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions& setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions& setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     OwningCompileOptions& setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
     OwningCompileOptions& setIntroductionType(const char* t) { introductionType = t; return *this; }
     bool setIntroductionInfo(JSContext* cx, const char* introducerFn, const char* intro,
@@ -3658,17 +3655,16 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     }
     CompileOptions& setVersion(JSVersion v) {
         version = v;
         versionSet = true;
         return *this;
     }
     CompileOptions& setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions& setColumn(unsigned c) { column = c; return *this; }
-    CompileOptions& setHasPollutedScope(bool p) { hasPollutedGlobalScope = p; return *this; }
     CompileOptions& setIsRunOnce(bool once) { isRunOnce = once; return *this; }
     CompileOptions& setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions& setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions& setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions& setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     CompileOptions& setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
     CompileOptions& setIntroductionType(const char* t) { introductionType = t; return *this; }
     CompileOptions& setIntroductionInfo(const char* introducerFn, const char* intro,
@@ -3710,16 +3706,20 @@ extern JS_PUBLIC_API(bool)
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file,
         JS::MutableHandleScript script);
 
 extern JS_PUBLIC_API(bool)
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options, const char* filename,
         JS::MutableHandleScript script);
 
 extern JS_PUBLIC_API(bool)
+CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
+                            SourceBufferHolder& srcBuf, JS::MutableHandleScript script);
+
+extern JS_PUBLIC_API(bool)
 CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
 
 /*
  * Off thread compilation control flow.
  *
  * After successfully triggering an off thread compile of a script, the
  * callback will eventually be invoked with the specified data and a token
  * for the compilation. The callback will be invoked while off the main thread,
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2424,21 +2424,26 @@ JSScript::Create(ExclusiveContext* cx, H
 
     PodZero(script.get());
     new (&script->bindings) Bindings;
 
     script->enclosingStaticScope_ = enclosingScope;
     script->savedCallerFun_ = savedCallerFun;
     script->initCompartment(cx);
 
-    script->hasNonSyntacticScope_ = options.hasPollutedGlobalScope;
     script->selfHosted_ = options.selfHostingMode;
     script->noScriptRval_ = options.noScriptRval;
     script->treatAsRunOnce_ = options.isRunOnce;
 
+    // Compute whether this script is under a non-syntactic scope. We don't
+    // need to walk the entire static scope chain if the script is nested in a
+    // function. In that case, we can propagate the cached value from the
+    // outer script.
+    script->hasNonSyntacticScope_ = HasNonSyntacticStaticScopeChain(enclosingScope);
+
     script->version = options.version;
     MOZ_ASSERT(script->getVersion() == options.version);     // assert that no overflow occurred
 
     // This is an unsigned-to-uint16_t conversion, test for too-high values.
     // In practice, recursion in Parser and/or BytecodeEmitter will blow the
     // stack if we nest functions more than a few hundred deep, so this will
     // never trigger.  Oh well.
     if (staticLevel > UINT16_MAX) {
@@ -2663,20 +2668,23 @@ JSScript::fullyInitFromEmitter(Exclusive
             MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
         }
 
         script->funLength_ = funbox->length;
     }
 
     RootedFunction fun(cx, nullptr);
     if (funbox) {
+        // The function should have already been earlier to enable
+        // StaticScopeIter to walk the static scope chain of
+        // currently compiling scripts.
+        MOZ_ASSERT(script->functionNonDelazifying() == funbox->function());
         MOZ_ASSERT(!bce->script->noScriptRval());
         script->isGeneratorExp_ = funbox->inGenexpLambda;
         script->setGeneratorKind(funbox->generatorKind());
-        script->setFunction(funbox->function());
         if (bce->yieldOffsetList.length() != 0)
             bce->yieldOffsetList.finish(script->yieldOffsets(), prologueLength);
     }
 
     // The call to nfixed() depends on the above setFunction() call.
     if (UINT32_MAX - script->nfixed() < bce->maxStackDepth) {
         bce->reportError(nullptr, JSMSG_NEED_DIET, "script");
         return false;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -750,21 +750,16 @@ GeneratorKindFromBits(unsigned val) {
  * subsequent set-up of owning function or script object and then call
  * CallNewScriptHook.
  */
 template<XDRMode mode>
 bool
 XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
           HandleFunction fun, MutableHandleScript scriptp);
 
-enum PollutedGlobalScopeOption {
-    HasPollutedGlobalScope,
-    HasCleanGlobalScope
-};
-
 JSScript*
 CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script,
             PollutedGlobalScopeOption polluted = HasCleanGlobalScope,
             NewObjectKind newKind = GenericObject);
 
 template<XDRMode mode>
 bool
 XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6256,22 +6256,24 @@ EvaluateInEnv(JSContext* cx, Handle<Env*
      * calls and properly compute a static level. In practice, any non-zero
      * static level will suffice.
      *
      * Pass in a StaticEvalObject *not* linked to env for evalStaticScope, as
      * ScopeIter should stop at any non-ScopeObject or non-syntactic With
      * boundaries, and we are putting a DebugScopeProxy or non-syntactic With on
      * the scope chain.
      */
-    Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, nullptr));
+    Rooted<ScopeObject*> enclosingStaticScope(cx);
+    if (!env->is<GlobalObject>())
+        enclosingStaticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
+    Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosingStaticScope));
     if (!staticScope)
         return false;
     CompileOptions options(cx);
-    options.setHasPollutedScope(true)
-           .setIsRunOnce(true)
+    options.setIsRunOnce(true)
            .setForEval(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno)
            .setCanLazilyParse(false)
            .setIntroductionType("debugger eval")
            .maybeMakeStrictMode(frame ? frame.script()->strict() : false);
     RootedScript callerScript(cx, frame ? frame.script() : nullptr);
     SourceBufferHolder srcBuf(chars.start().get(), chars.length(), SourceBufferHolder::NoOwnership);
@@ -6412,24 +6414,18 @@ DebuggerGenericEval(JSContext* cx, const
             }
         }
 
         AutoObjectVector scopeChain(cx);
         if (!scopeChain.append(nenv))
             return false;
 
         RootedObject dynamicScope(cx);
-        // We ignore the static scope here.  See comments about static
-        // scopes in EvaluateInEnv.
-        RootedObject unusedStaticScope(cx);
-        if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope,
-                                             &unusedStaticScope))
-        {
+        if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope))
             return false;
-        }
 
         env = dynamicScope;
     }
 
     /* Run the code and produce the completion value. */
     RootedValue rval(cx);
     AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
     jsbytecode* pc = iter ? iter->pc() : nullptr;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2482,18 +2482,17 @@ js::GetObjectEnvironmentObjectForFunctio
         return &fun->global();
 
     return &env->as<DynamicWithObject>().object();
 }
 
 bool
 js::CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                     HandleObject dynamicTerminatingScope,
-                                    MutableHandleObject dynamicScopeObj,
-                                    MutableHandleObject staticScopeObj)
+                                    MutableHandleObject dynamicScopeObj)
 {
 #ifdef DEBUG
     for (size_t i = 0; i < scopeChain.length(); ++i) {
         assertSameCompartment(cx, scopeChain[i]);
         MOZ_ASSERT(!scopeChain[i]->is<GlobalObject>());
     }
 #endif
 
@@ -2513,20 +2512,33 @@ js::CreateScopeObjectsForScopeChain(JSCo
         dynamicWith = DynamicWithObject::create(cx, scopeChain[--i], dynamicEnclosingScope,
                                                 staticWith, DynamicWithObject::NonSyntacticWith);
         if (!dynamicWith)
             return false;
         dynamicEnclosingScope = dynamicWith;
     }
 
     dynamicScopeObj.set(dynamicEnclosingScope);
-    staticScopeObj.set(staticEnclosingScope);
     return true;
 }
 
+bool
+js::HasNonSyntacticStaticScopeChain(JSObject* staticScope)
+{
+    for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
+        // If we hit a function scope, we can short circuit the logic, as
+        // scripts cache whether they are under a non-syntactic scope.
+        if (ssi.type() == StaticScopeIter<NoGC>::Function)
+            return ssi.funScript()->hasNonSyntacticScope();
+        if (ssi.type() == StaticScopeIter<NoGC>::NonSyntactic)
+            return true;
+    }
+    return false;
+}
+
 #ifdef DEBUG
 
 typedef HashSet<PropertyName*> PropertyNameSet;
 
 static bool
 RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remainingNames)
 {
     // Remove from remainingNames --- the closure variables in some outer
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -1153,18 +1153,19 @@ ScopeIter::enclosingScope() const
     MOZ_ASSERT(done());
     MOZ_ASSERT(!IsSyntacticScope(scope_));
     return *scope_;
 }
 
 extern bool
 CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                 HandleObject dynamicTerminatingScope,
-                                MutableHandleObject dynamicScopeObj,
-                                MutableHandleObject staticScopeObj);
+                                MutableHandleObject dynamicScopeObj);
+
+bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
 
 #ifdef DEBUG
 bool
 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
 #endif
 
 } // namespace js
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -182,17 +182,17 @@ AssertDynamicScopeMatchesStaticScope(JSC
                 MOZ_CRASH("NonSyntactic should not have a syntactic scope");
                 break;
             }
         }
     }
 
     // The scope chain is always ended by one or more non-syntactic
     // ScopeObjects (viz. GlobalObject or an unqualified varobj).
-    MOZ_ASSERT(!scope->is<ScopeObject>());
+    MOZ_ASSERT(!IsSyntacticScope(scope));
 #endif
 }
 
 bool
 InterpreterFrame::initFunctionScopeObjects(JSContext* cx)
 {
     CallObject* callobj = CallObject::createForFunction(cx, this);
     if (!callobj)
@@ -248,18 +248,17 @@ InterpreterFrame::epilogue(JSContext* cx
     probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame());
 
     if (isEvalFrame()) {
         if (isStrictEvalFrame()) {
             MOZ_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval());
             if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
                 DebugScopes::onPopStrictEvalScope(this);
         } else if (isDirectEvalFrame()) {
-            if (isDebuggerEvalFrame())
-                MOZ_ASSERT(!scopeChain()->is<ScopeObject>());
+            MOZ_ASSERT_IF(isDebuggerEvalFrame(), !IsSyntacticScope(scopeChain()));
         } else {
             /*
              * Debugger.Object.prototype.evalInGlobal creates indirect eval
              * frames scoped to the given global;
              * Debugger.Object.prototype.evalInGlobalWithBindings creates
              * indirect eval frames scoped to an object carrying the introduced
              * bindings.
              */