Backed out 13 changesets (bug 1165486) for ASAN jsreftest crashes.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 19 Jun 2015 10:56:48 -0400
changeset 280553 bfc988bd3c77b961248c666a4edd83de2a102490
parent 280552 937562e73564632d3fa7f512ffe0f37791df5f90
child 280554 395099ed230c9c31dd2b7d843965f0c1d858e9dc
push idunknown
push userunknown
push dateunknown
bugs1165486
milestone41.0a1
backs out24966b67d23292e25c3b35186cb2032f759c2534
4a65eeea4d9a9e737757f3cdc6ba2ff2693986cf
c5301aa7a2aeca0ae9ba4bf68e4ef4506d117de3
fea908d8a83651b3359f96513c6d406afbb84397
ca9561cbcd29b9bbd7fb7ddf1f285c5f7f8e1077
91a3217d4cd06f08fd3b5cf38502f0e9bf0cc6ff
e74163801eef3cfafb1f376cca90574d5f09c3ee
ec5df87d2ee505a802d3da463ecb7df4294b07af
17d21020a7864e8612f976737731b98e875a7115
6318eba2d3fe5df3d10eb46658677ddab7ea4121
a103caa361833549bb9a868301fb614863439bfa
18c025c16bed1e10370d629233a6b71ac0782bd6
b32fcdc115b6bdbb64f399760e42ae6d4b81deb5
Backed out 13 changesets (bug 1165486) for ASAN jsreftest crashes. Backed out changeset 24966b67d232 (bug 1165486) Backed out changeset 4a65eeea4d9a (bug 1165486) Backed out changeset c5301aa7a2ae (bug 1165486) Backed out changeset fea908d8a836 (bug 1165486) Backed out changeset ca9561cbcd29 (bug 1165486) Backed out changeset 91a3217d4cd0 (bug 1165486) Backed out changeset e74163801eef (bug 1165486) Backed out changeset ec5df87d2ee5 (bug 1165486) Backed out changeset 17d21020a786 (bug 1165486) Backed out changeset 6318eba2d3fe (bug 1165486) Backed out changeset a103caa36183 (bug 1165486) Backed out changeset 18c025c16bed (bug 1165486) Backed out changeset b32fcdc115b6 (bug 1165486) CLOSED TREE
dom/base/nsFrameMessageManager.cpp
js/src/builtin/Eval.cpp
js/src/builtin/TestingFunctions.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext.h
js/src/jit-test/tests/debug/onNewScript-ExecuteInGlobalAndReturnScope.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BytecodeAnalysis.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/Ion.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/JitFrames.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/ScopeObject-inl.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/Stack.cpp
js/xpconnect/loader/mozJSSubScriptLoader.cpp
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1780,17 +1780,18 @@ nsMessageManagerScriptExecutor::TryCache
     JS::Rooted<JSScript*> script(cx);
 
     if (aRunInGlobalScope) {
       if (!JS::Compile(cx, options, srcBuf, &script)) {
         return;
       }
     } else {
       // We're going to run these against some non-global scope.
-      if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
+      options.setHasPollutedScope(true);
+      if (!JS::Compile(cx, options, srcBuf, &script)) {
         return;
       }
     }
 
     aScriptp.set(script);
 
     nsAutoCString scheme;
     uri->GetScheme(scheme);
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -207,16 +207,28 @@ 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
@@ -315,18 +327,23 @@ 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);
@@ -334,17 +351,17 @@ EvalKernel(JSContext* cx, const CallArgs
             return false;
 
         const char16_t* chars = linearChars.twoByteRange().start().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
         SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
         JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                     scopeobj, staticScope, callerScript,
+                                                     scopeobj, callerScript, staticScope,
                                                      options, srcBuf, linearStr, staticLevel);
         if (!compiled)
             return false;
 
         if (compiled->strict())
             staticScope->setStrict();
 
         esg.setNewScript(compiled);
@@ -388,29 +405,31 @@ 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);
@@ -418,17 +437,17 @@ js::DirectEvalStringFromIon(JSContext* c
             return false;
 
         const char16_t* chars = linearChars.twoByteRange().start().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
         SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
         JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                     scopeobj, staticScope, callerScript,
+                                                     scopeobj, callerScript, staticScope,
                                                      options, srcBuf, linearStr, staticLevel);
         if (!compiled)
             return false;
 
         if (compiled->strict())
             staticScope->setStrict();
 
         esg.setNewScript(compiled);
@@ -489,41 +508,45 @@ js::IsAnyBuiltinEval(JSFunction* fun)
 
 JS_FRIEND_API(bool)
 js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScript scriptArg,
                                   MutableHandleObject scopeArg)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, global);
     MOZ_ASSERT(global->is<GlobalObject>());
-    MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
+    MOZ_RELEASE_ASSERT(scriptArg->hasPollutedGlobalScope());
 
     RootedScript script(cx, scriptArg);
     if (script->compartment() != cx->compartment()) {
-        Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr));
-        if (!staticScope)
-            return false;
-        script = CloneGlobalScript(cx, staticScope, script);
+        script = CloneScript(cx, nullptr, nullptr, script);
         if (!script)
             return false;
 
         Debugger::onNewScript(cx, script);
     }
 
-    Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>());
-    Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalRoot));
+    RootedObject scope(cx, JS_NewPlainObject(cx));
     if (!scope)
         return false;
 
+    if (!scope->setQualifiedVarObj(cx))
+        return false;
+
+    if (!scope->setUnqualifiedVarObj(cx))
+        return false;
+
     JSObject* thisobj = GetThisObject(cx, global);
     if (!thisobj)
         return false;
 
     RootedValue thisv(cx, ObjectValue(*thisobj));
     RootedValue rval(cx);
+    // XXXbz when this is fixed to pass in an actual ScopeObject, fix
+    // up the assert in js::CloneFunctionObject accordingly.
     if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
                        NullFramePtr() /* evalInFrame */, rval.address()))
     {
         return false;
     }
 
     scopeArg.set(scope);
     return true;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2267,20 +2267,21 @@ 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::CompileForNonSyntacticScope(cx, options, srcBuf, &script))
+    if (!JS::Compile(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,21 +139,20 @@ MaybeCheckEvalFreeVariables(ExclusiveCon
             scope = scope->enclosingScope();
         }
     }
 
     return true;
 }
 
 static inline bool
-CanLazilyParse(ExclusiveContext* cx, HandleObject staticScope,
-               const ReadOnlyCompileOptions& options)
+CanLazilyParse(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
 {
     return options.canLazilyParse &&
-           !HasNonSyntacticStaticScopeChain(staticScope) &&
+           !options.hasPollutedGlobalScope &&
            !cx->compartment()->options().disableLazyParsing() &&
            !cx->compartment()->options().discardSource() &&
            !options.sourceIsLazy;
 }
 
 static void
 MarkFunctionsWithinEvalScript(JSScript* script)
 {
@@ -206,18 +205,18 @@ frontend::CreateScriptSourceObject(Exclu
             return nullptr;
     }
 
     return sso;
 }
 
 JSScript*
 frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject scopeChain,
-                        Handle<ScopeObject*> enclosingStaticScope,
                         HandleScript evalCaller,
+                        Handle<StaticEvalObject*> evalStaticScope,
                         const ReadOnlyCompileOptions& options,
                         SourceBufferHolder& srcBuf,
                         JSString* source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionTask* extraSct /* = nullptr */)
 {
     MOZ_ASSERT(srcBuf.get());
 
@@ -256,17 +255,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, enclosingStaticScope, options);
+    bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
         syntaxParser.emplace(cx, alloc, options, srcBuf.get(), srcBuf.length(),
                              /* foldConstants = */ false,
                              (Parser<SyntaxParseHandler>*) nullptr,
                              (LazyScript*) nullptr);
 
@@ -280,32 +279,32 @@ frontend::CompileScript(ExclusiveContext
     parser.sct = sct;
     parser.ss = ss;
 
     if (!parser.checkOptions())
         return nullptr;
 
     bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
     Directives directives(options.strictOption);
-    GlobalSharedContext globalsc(cx, directives, enclosingStaticScope, options.extraWarningsOption);
+    GlobalSharedContext globalsc(cx, directives, evalStaticScope, options.extraWarningsOption);
 
-    Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingStaticScope, savedCallerFun,
+    Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
                                                   options, staticLevel, sourceObject, 0,
                                                   srcBuf.length()));
     if (!script)
         return nullptr;
 
     bool insideNonGlobalEval =
-        enclosingStaticScope && enclosingStaticScope->is<StaticEvalObject>() &&
-        enclosingStaticScope->as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
+        evalStaticScope && evalStaticScope->enclosingScopeForStaticScopeIter();
     BytecodeEmitter::EmitterMode emitterMode =
         options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
     BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script,
                         /* lazyScript = */ nullptr, options.forEval,
-                        evalCaller, insideNonGlobalEval, options.lineno, emitterMode);
+                        evalCaller, insideNonGlobalEval,
+                        options.lineno, emitterMode);
     if (!bce.init())
         return nullptr;
 
     // Syntax parsing may cause us to restart processing of top level
     // statements in the script. Use Maybe<> so that the parse context can be
     // reset when this occurs.
     Maybe<ParseContext<FullParseHandler> > pc;
 
@@ -557,17 +556,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, enclosingStaticScope, options);
+    bool canLazilyParse = CanLazilyParse(cx, 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/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -12,27 +12,28 @@
 class JSLinearString;
 
 namespace js {
 
 class AutoNameVector;
 class LazyScript;
 class LifoAlloc;
 class ScriptSourceObject;
-class ScopeObject;
+class StaticEvalObject;
 struct SourceCompressionTask;
 
 namespace frontend {
 
 JSScript*
 CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
-              HandleObject scopeChain, Handle<ScopeObject*> enclosingStaticScope,
-              HandleScript evalCaller, const ReadOnlyCompileOptions& options,
-              SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
-              unsigned staticLevel = 0, SourceCompressionTask* extraSct = nullptr);
+              HandleObject scopeChain, HandleScript evalCaller,
+              Handle<StaticEvalObject*> evalStaticScope,
+              const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf,
+              JSString* source_ = nullptr, unsigned staticLevel = 0,
+              SourceCompressionTask* extraSct = nullptr);
 
 bool
 CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
 
 /*
  * enclosingStaticScope is a static enclosing scope (e.g. a StaticWithObject).
  * Must be null if the enclosing scope is a global.
  */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -764,17 +764,17 @@ BytecodeEmitter::enclosingStaticScope()
     if (staticScope)
         return staticScope;
 
     if (!sc->isFunctionBox()) {
         MOZ_ASSERT(!parent);
 
         // Top-level eval scripts have a placeholder static scope so that
         // StaticScopeIter may iterate through evals.
-        return sc->asGlobalSharedContext()->topStaticScope();
+        return sc->asGlobalSharedContext()->evalStaticScope();
     }
 
     return sc->asFunctionBox()->function();
 }
 
 #ifdef DEBUG
 static bool
 AllLocalsAliased(StaticBlockObject& obj)
@@ -1543,24 +1543,24 @@ BytecodeEmitter::tryConvertFreeName(Pars
             return false;
         RootedObject outerScope(cx, script->enclosingStaticScope());
         for (StaticScopeIter<CanGC> ssi(cx, outerScope); !ssi.done(); ssi++) {
             if (ssi.type() != StaticScopeIter<CanGC>::Function) {
                 if (ssi.type() == StaticScopeIter<CanGC>::Block) {
                     // Use generic ops if a catch block is encountered.
                     return false;
                 }
-                if (ssi.hasSyntacticDynamicScopeObject())
+                if (ssi.hasDynamicScopeObject())
                     hops++;
                 continue;
             }
             RootedScript script(cx, ssi.funScript());
             if (script->functionNonDelazifying()->atom() == pn->pn_atom)
                 return false;
-            if (ssi.hasSyntacticDynamicScopeObject()) {
+            if (ssi.hasDynamicScopeObject()) {
                 uint32_t slot;
                 if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) {
                     JSOp op;
                     switch (pn->getOp()) {
                       case JSOP_GETNAME: op = JSOP_GETALIASEDVAR; break;
                       case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break;
                       default: return false;
                     }
@@ -1582,19 +1582,19 @@ BytecodeEmitter::tryConvertFreeName(Pars
         }
     }
 
     // Unbound names aren't recognizable global-property references if the
     // script is inside a non-global eval call.
     if (insideNonGlobalEval)
         return false;
 
-    // Skip trying to use GNAME ops if we know our script has a non-syntactic
-    // scope, since they'll just get treated as NAME ops anyway.
-    if (script->hasNonSyntacticScope())
+    // Skip trying to use GNAME ops if we know our script has a polluted
+    // global scope, since they'll just get treated as NAME ops anyway.
+    if (script->hasPollutedGlobalScope())
         return false;
 
     // Deoptimized names also aren't necessarily globals.
     if (pn->isDeoptimized())
         return false;
 
     if (sc->isFunctionBox()) {
         // Unbound names in function code may not be globals if new locals can
@@ -2351,24 +2351,23 @@ bool
 BytecodeEmitter::checkRunOnceContext()
 {
     return checkSingletonContext() || (!isInLoop() && isRunOnceLambda());
 }
 
 bool
 BytecodeEmitter::needsImplicitThis()
 {
-    if (sc->inWith())
+    if (sc->isFunctionBox() && sc->asFunctionBox()->inWith)
         return true;
 
     for (StmtInfoBCE* stmt = topStmt; stmt; stmt = stmt->down) {
         if (stmt->type == STMT_WITH)
             return true;
     }
-
     return false;
 }
 
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext* cx)
 {
     // Note: when parsing off thread the resulting scripts need to be handed to
     // the debugger after rejoining to the main thread.
@@ -3402,29 +3401,16 @@ 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)) {
@@ -3518,16 +3504,25 @@ 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)
 {
@@ -5765,16 +5760,18 @@ 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/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -17,17 +17,17 @@
 
 #include "frontend/ParseMaps.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 
 namespace js {
 
-class ScopeObject;
+class StaticEvalObject;
 
 namespace frontend {
 
 class FullParseHandler;
 class ObjectBox;
 class ParseNode;
 template <typename ParseHandler> class Parser;
 class SharedContext;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -591,63 +591,54 @@ FunctionBox::FunctionBox(ExclusiveContex
                          bool extraWarnings, GeneratorKind generatorKind)
   : ObjectBox(fun, traceListHead),
     SharedContext(cx, directives, extraWarnings),
     bindings(),
     bufStart(0),
     bufEnd(0),
     length(0),
     generatorKindBits_(GeneratorKindAsBits(generatorKind)),
-    inWith_(false),                  // initialized below
+    inWith(false),                  // initialized below
     inGenexpLambda(false),
     hasDestructuringArgs(false),
     useAsm(false),
     insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
     usesArguments(false),
     usesApply(false),
     usesThis(false),
     funCxFlags()
 {
     // Functions created at parse time may be set singleton after parsing and
     // baked into JIT code, so they must be allocated tenured. They are held by
     // the JSScript so cannot be collected during a minor GC anyway.
     MOZ_ASSERT(fun->isTenured());
 
     if (!outerpc) {
-        inWith_ = false;
+        inWith = false;
 
     } else if (outerpc->parsingWith) {
         // This covers cases that don't involve eval().  For example:
         //
         //   with (o) { (function() { g(); })(); }
         //
         // In this case, |outerpc| corresponds to global code, and
         // outerpc->parsingWith is true.
-        inWith_ = true;
+        inWith = true;
 
     } else if (outerpc->sc->isFunctionBox()) {
         // This is like the above case, but for more deeply nested functions.
         // For example:
         //
         //   with (o) { eval("(function() { (function() { g(); })(); })();"); } }
         //
         // In this case, the inner anonymous function needs to inherit the
         // setting of |inWith| from the outer one.
         FunctionBox* parent = outerpc->sc->asFunctionBox();
-        if (parent && parent->inWith())
-            inWith_ = true;
-    } else {
-        // This is like the above case, but when inside eval.
-        //
-        // For example:
-        //
-        //   with(o) { eval("(function() { g(); })();"); }
-        //
-        // In this case, the static scope chain tells us the presence of with.
-        inWith_ = outerpc->sc->inWith();
+        if (parent && parent->inWith)
+            inWith = true;
     }
 }
 
 template <typename ParseHandler>
 FunctionBox*
 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, ParseContext<ParseHandler>* outerpc,
                                      Directives inheritedDirectives, GeneratorKind generatorKind)
 {
@@ -2236,17 +2227,17 @@ template <>
 bool
 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox* funbox,
                                                      Node body)
 {
     // The LazyScript for a lazily parsed function needs to be constructed
     // while its ParseContext and associated lexdeps and inner functions are
     // still available.
 
-    if (funbox->inWith())
+    if (funbox->inWith)
         return abortIfSyntaxParser();
 
     size_t numFreeVariables = pc->lexdeps->count();
     size_t numInnerFunctions = pc->innerFunctions.length();
 
     RootedFunction fun(context, funbox->function());
     LazyScript* lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions,
                                              versionNumber(), funbox->bufStart, funbox->bufEnd,
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -232,17 +232,16 @@ class SharedContext
         return atom == context->names().dotGenerator || atom == context->names().dotGenRVal;
     }
 
     enum class AllowedSyntax {
         NewTarget,
         SuperProperty
     };
     virtual bool allowSyntax(AllowedSyntax allowed) const = 0;
-    virtual bool inWith() const = 0;
 
   protected:
     static bool FunctionAllowsSyntax(JSFunction* func, AllowedSyntax allowed)
     {
         MOZ_ASSERT(!func->isArrow());
 
         switch (allowed) {
           case AllowedSyntax::NewTarget:
@@ -254,80 +253,54 @@ class SharedContext
         }
         MOZ_CRASH("Unknown AllowedSyntax query");
     }
 };
 
 class GlobalSharedContext : public SharedContext
 {
   private:
-    Handle<ScopeObject*> topStaticScope_;
-    bool allowNewTarget_;
-    bool allowSuperProperty_;
-    bool inWith_;
+    Handle<StaticEvalObject*> staticEvalScope_;
 
-    bool computeAllowSyntax(AllowedSyntax allowed) const {
-        StaticScopeIter<CanGC> it(context, topStaticScope_);
+  public:
+    GlobalSharedContext(ExclusiveContext* cx,
+                        Directives directives, Handle<StaticEvalObject*> staticEvalScope,
+                        bool extraWarnings)
+      : SharedContext(cx, directives, extraWarnings),
+        staticEvalScope_(staticEvalScope)
+    {}
+
+    ObjectBox* toObjectBox() { return nullptr; }
+    HandleObject evalStaticScope() const { return staticEvalScope_; }
+
+    bool allowSyntax(AllowedSyntax allowed) const {
+        StaticScopeIter<CanGC> it(context, staticEvalScope_);
         for (; !it.done(); it++) {
             if (it.type() == StaticScopeIter<CanGC>::Function &&
                 !it.fun().isArrow())
             {
                 return FunctionAllowsSyntax(&it.fun(), allowed);
             }
         }
         return false;
     }
-
-    bool computeInWith() const {
-        for (StaticScopeIter<CanGC> it(context, topStaticScope_); !it.done(); it++) {
-            if (it.type() == StaticScopeIter<CanGC>::With)
-                return true;
-        }
-        return false;
-    }
-
-  public:
-    GlobalSharedContext(ExclusiveContext* cx,
-                        Directives directives, Handle<ScopeObject*> topStaticScope,
-                        bool extraWarnings)
-      : SharedContext(cx, directives, extraWarnings),
-        topStaticScope_(topStaticScope),
-        allowNewTarget_(computeAllowSyntax(AllowedSyntax::NewTarget)),
-        allowSuperProperty_(computeAllowSyntax(AllowedSyntax::SuperProperty)),
-        inWith_(computeInWith())
-    {}
-
-    ObjectBox* toObjectBox() override { return nullptr; }
-    HandleObject topStaticScope() const { return topStaticScope_; }
-    bool allowSyntax(AllowedSyntax allowSyntax) const override {
-        switch (allowSyntax) {
-          case AllowedSyntax::NewTarget:
-            // Any function supports new.target
-            return allowNewTarget_;
-          case AllowedSyntax::SuperProperty:
-            return allowSuperProperty_;
-          default:;
-        }
-        MOZ_CRASH("Unknown AllowedSyntax query");
-    }
-    bool inWith() const override { return inWith_; }
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
     uint32_t        bufStart;
     uint32_t        bufEnd;
     uint32_t        startLine;
     uint32_t        startColumn;
     uint16_t        length;
 
     uint8_t         generatorKindBits_;     /* The GeneratorKind of this function. */
-    bool            inWith_:1;              /* some enclosing scope is a with-statement */
+    bool            inWith:1;               /* some enclosing scope is a with-statement */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
     bool            hasDestructuringArgs:1; /* arguments list contains destructuring expression */
     bool            useAsm:1;               /* see useAsmOrInsideUseAsm */
     bool            insideUseAsm:1;         /* see useAsmOrInsideUseAsm */
 
     // Fields for use in heuristics.
     bool            usesArguments:1;  /* contains a free use of 'arguments' */
     bool            usesApply:1;      /* contains an f.apply() call */
@@ -335,17 +308,17 @@ class FunctionBox : public ObjectBox, pu
 
     FunctionContextFlags funCxFlags;
 
     template <typename ParseHandler>
     FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
                 ParseContext<ParseHandler>* pc, Directives directives,
                 bool extraWarnings, GeneratorKind generatorKind);
 
-    ObjectBox* toObjectBox() override { return this; }
+    ObjectBox* toObjectBox() { return this; }
     JSFunction* function() const { return &object->as<JSFunction>(); }
 
     GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
     bool isGenerator() const { return generatorKind() != NotGenerator; }
     bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == StarGenerator; }
 
     void setGeneratorKind(GeneratorKind kind) {
@@ -399,23 +372,19 @@ class FunctionBox : public ObjectBox, pu
         // Note: this should be kept in sync with JSFunction::isHeavyweight().
         return bindings.hasAnyAliasedBindings() ||
                hasExtensibleScope() ||
                needsDeclEnvObject() ||
                needsHomeObject()    ||
                isGenerator();
     }
 
-    bool allowSyntax(AllowedSyntax allowed) const override {
+    bool allowSyntax(AllowedSyntax allowed) const {
         return FunctionAllowsSyntax(function(), allowed);
     }
-
-    bool inWith() const override {
-        return inWith_;
-    }
 };
 
 inline FunctionBox*
 SharedContext::asFunctionBox()
 {
     MOZ_ASSERT(isFunctionBox());
     return static_cast<FunctionBox*>(this);
 }
--- a/js/src/jit-test/tests/debug/onNewScript-ExecuteInGlobalAndReturnScope.js
+++ b/js/src/jit-test/tests/debug/onNewScript-ExecuteInGlobalAndReturnScope.js
@@ -1,15 +1,14 @@
 // Debugger should be notified of scripts created with ExecuteInGlobalAndReturnScope.
 
 var g = newGlobal();
 var g2 = newGlobal();
 var dbg = new Debugger(g, g2);
 var log = '';
-var canary = 42;
 
 dbg.onNewScript = function (evalScript) {
   log += 'e';
 
   dbg.onNewScript = function (clonedScript) {
     log += 'c';
     clonedScript.setBreakpoint(0, {
       hit(frame) {
@@ -20,13 +19,10 @@ dbg.onNewScript = function (evalScript) 
   };
 };
 
 dbg.onDebuggerStatement = function (frame) {
   log += 'd';
 };
 
 assertEq(log, '');
-var evalScope = g.evalReturningScope("canary = 'dead'; debugger; // nee", g2);
+var evalScope = g.evalReturningScope("debugger; // nee", g2);
 assertEq(log, 'ecbd');
-assertEq(canary, 42);
-assertEq(evalScope.canary, 'dead');
-
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -699,24 +699,23 @@ InitFromBailout(JSContext* cx, HandleScr
                 // not resume into baseline code, but instead into
                 // HandleExceptionBaseline, so *do* set the scope chain here.
                 if (iter.pcOffset() != 0 || iter.resumeAfter() ||
                     (excInfo && excInfo->propagatingIonExceptionForDebugMode()))
                 {
                     scopeChain = fun->environment();
                 }
             } else {
-                // For global scripts without a non-syntactic scope the scope
+                // For global scripts without a polluted global scope the scope
                 // chain is the script's global (Ion does not compile scripts
-                // with a non-syntactic global scope). Also note that it's
-                // invalid to resume into the prologue in this case because
-                // the prologue expects the scope chain in R1 for eval and
-                // global scripts.
+                // with a polluted global scope). Also note that it's invalid to
+                // resume into the prologue in this case because the prologue
+                // expects the scope chain in R1 for eval and global scripts.
                 MOZ_ASSERT(!script->isForEval());
-                MOZ_ASSERT(!script->hasNonSyntacticScope());
+                MOZ_ASSERT(!script->hasPollutedGlobalScope());
                 scopeChain = &(script->global());
             }
         }
 
         // Make sure to add HAS_RVAL to flags here because setFlags() below
         // will clobber it.
         returnValue = iter.read();
         flags |= BaselineFrame::HAS_RVAL;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -15,17 +15,16 @@
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/SharedICHelpers.h"
 #include "jit/VMFunctions.h"
-#include "vm/ScopeObject.h"
 #include "vm/TraceLogging.h"
 
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
@@ -124,17 +123,17 @@ BaselineCompiler::compile()
         RootedFunction fun(cx, script->functionNonDelazifying());
         if (fun->isHeavyweight()) {
             RootedScript scriptRoot(cx, script);
             templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap);
             if (!templateScope)
                 return Method_Error;
 
             if (fun->isNamedLambda()) {
-                RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, TenuredObject));
+                RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
                 if (!declEnvObject)
                     return Method_Error;
                 templateScope->as<ScopeObject>().setEnclosingScope(declEnvObject);
             }
         }
     }
 
     // Encode the pc mapping table. See PCMappingIndexEntry for
@@ -2058,17 +2057,17 @@ BaselineCompiler::emit_JSOP_IN()
 
     frame.push(R0);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_GETGNAME()
 {
-    if (script->hasNonSyntacticScope())
+    if (script->hasPollutedGlobalScope())
         return emit_JSOP_GETNAME();
 
     RootedPropertyName name(cx, script->getName(pc));
 
     if (name == cx->names().undefined) {
         frame.push(UndefinedValue());
         return true;
     }
@@ -2093,17 +2092,17 @@ BaselineCompiler::emit_JSOP_GETGNAME()
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_BINDGNAME()
 {
-    if (!script->hasNonSyntacticScope()) {
+    if (!script->hasPollutedGlobalScope()) {
         frame.push(ObjectValue(script->global()));
         return true;
     }
 
     return emit_JSOP_BINDNAME();
 }
 
 bool
@@ -2916,17 +2915,17 @@ BaselineCompiler::emit_JSOP_IMPLICITTHIS
 
     frame.push(R0);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_GIMPLICITTHIS()
 {
-    if (!script->hasNonSyntacticScope()) {
+    if (!script->hasPollutedGlobalScope()) {
         frame.push(UndefinedValue());
         return true;
     }
 
     return emit_JSOP_IMPLICITTHIS();
 }
 
 bool
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -6128,17 +6128,17 @@ DoGetNameFallback(JSContext* cx, Baselin
     bool isTemporarilyUnoptimizable = false;
 
     // Attach new stub.
     if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic stub.
         attached = true;
     }
 
-    if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
+    if (!attached && IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) {
         if (!TryAttachGlobalNameAccessorStub(cx, script, pc, stub, scopeChain.as<GlobalObject>(),
             name, &attached, &isTemporarilyUnoptimizable))
         {
             return false;
         }
     }
 
     static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH,
@@ -6158,17 +6158,17 @@ DoGetNameFallback(JSContext* cx, Baselin
         return true;
 
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, script, res))
         return false;
     if (attached)
         return true;
 
-    if (IsGlobalOp(JSOp(*pc)) && !script->hasNonSyntacticScope()) {
+    if (IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) {
         Handle<GlobalObject*> global = scopeChain.as<GlobalObject>();
         if (!TryAttachGlobalNameValueStub(cx, script, pc, stub, global, name, &attached))
             return false;
     } else {
         if (!TryAttachScopeNameStub(cx, script, stub, scopeChain, name, &attached))
             return false;
     }
 
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -165,17 +165,17 @@ BytecodeAnalysis::init(TempAllocator& al
           case JSOP_DEFCONST:
           case JSOP_SETCONST:
             usesScopeChain_ = true;
             break;
 
           case JSOP_GETGNAME:
           case JSOP_SETGNAME:
           case JSOP_STRICTSETGNAME:
-            if (script_->hasNonSyntacticScope())
+            if (script_->hasPollutedGlobalScope())
                 usesScopeChain_ = true;
             break;
 
           case JSOP_FINALLY:
             hasTryFinally_ = true;
             break;
 
           case JSOP_SETARG:
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4681,31 +4681,31 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox
         break;
       default:
         MOZ_CRASH("The impossible happened!");
     }
 
     bailoutFrom(&bail, lir->snapshot());
 }
 
-typedef js::DeclEnvObject* (*NewDeclEnvObjectFn)(JSContext*, HandleFunction, NewObjectKind);
+typedef js::DeclEnvObject* (*NewDeclEnvObjectFn)(JSContext*, HandleFunction, gc::InitialHeap);
 static const VMFunction NewDeclEnvObjectInfo =
     FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
 
 void
 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject* lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
     DeclEnvObject* templateObj = lir->mir()->templateObj();
     CompileInfo& info = lir->mir()->block()->info();
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode* ool = oolCallVM(NewDeclEnvObjectInfo, lir,
-                                   ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(GenericObject)),
+                                   ArgList(ImmGCPtr(info.funMaybeLazy()), Imm32(gc::DefaultHeap)),
                                    StoreRegisterTo(objReg));
 
     bool initContents = ShouldInitFixedSlots(lir, templateObj);
     masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
                         initContents);
 
     masm.bind(ool->rejoin());
 }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2168,22 +2168,22 @@ CheckScript(JSContext* cx, JSScript* scr
         return false;
     }
 
     if (script->isGenerator()) {
         TrackAndSpewIonAbort(cx, script, "generator script");
         return false;
     }
 
-    if (script->hasNonSyntacticScope() && !script->functionNonDelazifying()) {
-        // Support functions with a non-syntactic global scope but not other
+    if (script->hasPollutedGlobalScope() && !script->functionNonDelazifying()) {
+        // Support functions with a polluted global scope but not other
         // scripts. For global scripts, IonBuilder currently uses the global
         // object as scope chain, this is not valid when the script has a
-        // non-syntactic global scope.
-        TrackAndSpewIonAbort(cx, script, "has non-syntactic global scope");
+        // polluted global scope.
+        TrackAndSpewIonAbort(cx, script, "has polluted global scope");
         return false;
     }
 
     return true;
 }
 
 static MethodStatus
 CheckScriptSize(JSContext* cx, JSScript* script)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -613,17 +613,17 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
         } else {
             MIRType type = MIRType_None;
             switch (*last) {
               case JSOP_VOID:
               case JSOP_UNDEFINED:
                 type = MIRType_Undefined;
                 break;
               case JSOP_GIMPLICITTHIS:
-                if (!script()->hasNonSyntacticScope())
+                if (!script()->hasPollutedGlobalScope())
                     type = MIRType_Undefined;
                 break;
               case JSOP_NULL:
                 type = MIRType_Null;
                 break;
               case JSOP_ZERO:
               case JSOP_ONE:
               case JSOP_INT8:
@@ -1177,20 +1177,20 @@ IonBuilder::initScopeChain(MDefinition* 
                     return false;
             }
 
             scope = createCallObject(callee, scope);
             if (!scope)
                 return false;
         }
     } else {
-        // For global scripts without a non-syntactic global scope, the scope
-        // chain is the global object.
+        // For global scripts without a polluted global scope, the scope chain
+        // is the global object.
         MOZ_ASSERT(!script()->isForEval());
-        MOZ_ASSERT(!script()->hasNonSyntacticScope());
+        MOZ_ASSERT(!script()->hasPollutedGlobalScope());
         scope = constant(ObjectValue(script()->global()));
     }
 
     current->setScopeChain(scope);
     return true;
 }
 
 bool
@@ -1795,26 +1795,26 @@ IonBuilder::inspectOpcode(JSOp op)
         return pushConstant(Int32Value(GET_INT8(pc)));
 
       case JSOP_UINT16:
         return pushConstant(Int32Value(GET_UINT16(pc)));
 
       case JSOP_GETGNAME:
       {
         PropertyName* name = info().getAtom(pc)->asPropertyName();
-        if (!script()->hasNonSyntacticScope())
+        if (!script()->hasPollutedGlobalScope())
             return jsop_getgname(name);
         return jsop_getname(name);
       }
 
       case JSOP_SETGNAME:
       case JSOP_STRICTSETGNAME:
       {
         PropertyName* name = info().getAtom(pc)->asPropertyName();
-        if (script()->hasNonSyntacticScope())
+        if (script()->hasPollutedGlobalScope())
             return jsop_setprop(name);
         JSObject* obj = &script()->global();
         return setStaticName(obj, name);
       }
 
       case JSOP_GETNAME:
       {
         PropertyName* name = info().getAtom(pc)->asPropertyName();
@@ -1823,17 +1823,17 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_GETINTRINSIC:
       {
         PropertyName* name = info().getAtom(pc)->asPropertyName();
         return jsop_intrinsic(name);
       }
 
       case JSOP_BINDGNAME:
-        if (!script()->hasNonSyntacticScope())
+        if (!script()->hasPollutedGlobalScope())
             return pushConstant(ObjectValue(script()->global()));
         // Fall through to JSOP_BINDNAME
       case JSOP_BINDNAME:
         return jsop_bindname(info().getName(pc));
 
       case JSOP_DUP:
         current->pushSlot(current->stackDepth() - 1);
         return true;
@@ -1970,17 +1970,17 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_DEBUGLEAVEBLOCK:
         return true;
 
       case JSOP_DEBUGGER:
         return jsop_debugger();
 
       case JSOP_GIMPLICITTHIS:
-        if (!script()->hasNonSyntacticScope())
+        if (!script()->hasPollutedGlobalScope())
             return pushConstant(UndefinedValue());
 
         // Just fall through to the unsupported bytecode case.
         break;
 
       case JSOP_NEWTARGET:
         return jsop_newtarget();
 
@@ -7608,17 +7608,17 @@ IonBuilder::jsop_getgname(PropertyName* 
 
     return jsop_getname(name);
 }
 
 bool
 IonBuilder::jsop_getname(PropertyName* name)
 {
     MDefinition* object;
-    if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope()) {
+    if (IsGlobalOp(JSOp(*pc)) && !script()->hasPollutedGlobalScope()) {
         MInstruction* global = constant(ObjectValue(script()->global()));
         object = global;
     } else {
         current->push(current->scopeChain());
         object = current->pop();
     }
 
     MGetNameCache* ins;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -2548,17 +2548,17 @@ InlineFrameIterator::computeScopeChain(V
     // are walking the frame during the function prologue, before the scope
     // chain has been initialized.
     if (isFunctionFrame())
         return callee(fallback)->environment();
 
     // Ion does not handle non-function scripts that have anything other than
     // the global on their scope chain.
     MOZ_ASSERT(!script()->isForEval());
-    MOZ_ASSERT(!script()->hasNonSyntacticScope());
+    MOZ_ASSERT(!script()->hasPollutedGlobalScope());
     return &script()->global();
 }
 
 bool
 InlineFrameIterator::isFunctionFrame() const
 {
     return !!calleeTemplate_;
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3294,68 +3294,45 @@ 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
-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;
+CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
+                                MutableHandleObject dynamicScopeObj,
+                                MutableHandleObject staticScopeObj)
+{
+    return js::CreateScopeObjectsForScopeChain(cx, scopeChain, cx->global(),
+                                               dynamicScopeObj, staticScopeObj);
 }
 
 static bool
 IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope)
 {
     if (!fun->isInterpreted())
         return true;
 
     // If a function was compiled to be lexically nested inside some other
     // script, we cannot clone it without breaking the compiler's assumptions.
-    if (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) {
-        // If the script already deals with a non-syntactic scope, we can clone
-        // it.
-        if (scope->is<StaticNonSyntacticScopeObjects>())
-            return true;
-
-        // If the script is an indirect eval that is immediately scoped under
-        // the global, we can clone it.
-        if (scope->is<StaticEvalObject>() &&
-            !scope->as<StaticEvalObject>().isDirect() &&
-            !scope->as<StaticEvalObject>().isStrict())
-        {
-            return true;
-        }
-
-        // Any other enclosing static scope (e.g., function, block) cannot be
-        // cloned.
+    JSObject* scope = fun->nonLazyScript()->enclosingStaticScope();
+    if (scope && (!scope->is<StaticEvalObject>() ||
+                  scope->as<StaticEvalObject>().isDirect() ||
+                  scope->as<StaticEvalObject>().isStrict()))
+    {
         return false;
     }
 
     return true;
 }
 
 static JSObject*
-CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScope,
-                    Handle<ScopeObject*> staticScope)
+CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScope)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, dynamicScope);
     MOZ_ASSERT(dynamicScope);
     // Note that funobj can be in a different compartment.
 
     if (!funobj->is<JSFunction>()) {
@@ -3382,50 +3359,36 @@ CloneFunctionObject(JSContext* cx, Handl
         return nullptr;
     }
 
     if (fun->isNative() && IsAsmJSModuleNative(fun->native())) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
         return nullptr;
     }
 
-    if (CanReuseScriptForClone(cx->compartment(), fun, dynamicScope)) {
-        // If the script is to be reused, either the script can already handle
-        // non-syntactic scopes, or there is no new static scope.
-#ifdef DEBUG
-        // Fail here if we OOM during debug asserting.
-        // CloneFunctionReuseScript will delazify the script anyways, so we
-        // are not creating an extra failure condition for DEBUG builds.
-        if (!fun->getOrCreateScript(cx))
-            return nullptr;
-        MOZ_ASSERT(!staticScope || fun->nonLazyScript()->hasNonSyntacticScope());
-#endif
-        return CloneFunctionReuseScript(cx, fun, dynamicScope, fun->getAllocKind());
-    }
-
-    return CloneFunctionAndScript(cx, fun, dynamicScope, staticScope, fun->getAllocKind());
+    return CloneFunctionObject(cx, fun, dynamicScope, fun->getAllocKind());
 }
 
 namespace JS {
 
 JS_PUBLIC_API(JSObject*)
 CloneFunctionObject(JSContext* cx, JS::Handle<JSObject*> funobj)
 {
-    return CloneFunctionObject(cx, funobj, cx->global(), /* staticScope = */ nullptr);
+    return CloneFunctionObject(cx, funobj, cx->global());
 }
 
 extern JS_PUBLIC_API(JSObject*)
 CloneFunctionObject(JSContext* cx, HandleObject funobj, AutoObjectVector& scopeChain)
 {
     RootedObject dynamicScope(cx);
-    Rooted<ScopeObject*> staticScope(cx);
-    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
+    RootedObject unusedStaticScope(cx);
+    if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
         return nullptr;
 
-    return CloneFunctionObject(cx, funobj, dynamicScope, staticScope);
+    return CloneFunctionObject(cx, funobj, dynamicScope);
 }
 
 } // namespace JS
 
 JS_PUBLIC_API(JSObject*)
 JS_GetFunctionObject(JSFunction* fun)
 {
     return fun;
@@ -3885,154 +3848,74 @@ 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();
 }
 
-enum SyntacticScopeOption { HasSyntacticScope, HasNonSyntacticScope };
-
-static bool
-Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption,
-        SourceBufferHolder& srcBuf, MutableHandleScript script)
+bool
+JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+            SourceBufferHolder& srcBuf, MutableHandleScript script)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     AutoLastFrameCheck lfc(cx);
 
-    Rooted<ScopeObject*> staticScope(cx);
-    if (scopeOption == HasNonSyntacticScope) {
-        staticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
-        if (!staticScope)
-            return false;
-    }
-
     script.set(frontend::CompileScript(cx, &cx->tempLifoAlloc(), cx->global(),
-                                       staticScope, nullptr, options, srcBuf));
+                                       nullptr, nullptr, options, srcBuf));
     return !!script;
 }
 
-static bool
-Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption,
-        const char16_t* chars, size_t length, MutableHandleScript 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, scopeOption, srcBuf, script);
-}
-
-static bool
-Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption,
-        const char* bytes, size_t length, MutableHandleScript script)
+    return Compile(cx, options, srcBuf, script);
+}
+
+bool
+JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
+            const char* bytes, size_t length, MutableHandleScript script)
 {
     mozilla::UniquePtr<char16_t, JS::FreePolicy> chars;
     if (options.utf8)
         chars.reset(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get());
     else
         chars.reset(InflateString(cx, bytes, &length));
     if (!chars)
         return false;
 
-    return ::Compile(cx, options, scopeOption, chars.get(), length, script);
-}
-
-static bool
-Compile(JSContext* cx, const ReadOnlyCompileOptions& options, SyntacticScopeOption scopeOption,
-        FILE* fp, MutableHandleScript script)
+    return Compile(cx, options, chars.get(), length, script);
+}
+
+bool
+JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* fp,
+            MutableHandleScript script)
 {
     FileContents buffer(cx);
     if (!ReadCompleteFile(cx, fp, buffer))
         return false;
 
-    return ::Compile(cx, options, scopeOption, buffer.begin(), buffer.length(), script);
-}
-
-static bool
-Compile(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, SyntacticScopeOption scopeOption,
-        const char* filename, MutableHandleScript script)
+    return Compile(cx, options, buffer.begin(), buffer.length(), script);
+}
+
+bool
+JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& optionsArg, const char* filename,
+            MutableHandleScript script)
 {
     AutoFile file;
     if (!file.open(cx, filename))
         return false;
     CompileOptions options(cx, optionsArg);
     options.setFileAndLine(filename, 1);
-    return ::Compile(cx, options, scopeOption, file.fp(), script);
-}
-
-bool
-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
-            SourceBufferHolder& srcBuf, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasSyntacticScope, srcBuf, script);
-}
-
-bool
-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
-            const char* bytes, size_t length, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasSyntacticScope, bytes, length, script);
-}
-
-bool
-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
-            const char16_t* chars, size_t length, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasSyntacticScope, chars, length, script);
-}
-
-bool
-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
-            FILE* file, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasSyntacticScope, file, script);
-}
-
-bool
-JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
-            const char* filename, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasSyntacticScope, filename, script);
-}
-
-bool
-JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                SourceBufferHolder& srcBuf, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasNonSyntacticScope, srcBuf, script);
-}
-
-bool
-JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                const char* bytes, size_t length, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasNonSyntacticScope, bytes, length, script);
-}
-
-bool
-JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                const char16_t* chars, size_t length,
-                                JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasNonSyntacticScope, chars, length, script);
-}
-
-bool
-JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                FILE* file, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasNonSyntacticScope, file, script);
-}
-
-bool
-JS::CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                const char* filename, JS::MutableHandleScript script)
-{
-    return ::Compile(cx, options, HasNonSyntacticScope, filename, script);
+    return Compile(cx, options, file.fp(), script);
 }
 
 JS_PUBLIC_API(bool)
 JS::CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
 {
     static const size_t TINY_LENGTH = 1000;
     static const size_t HUGE_LENGTH = 100 * 1000;
 
@@ -4205,37 +4088,37 @@ 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 the static scope chain matches up when we have a
-    // non-syntactic scope.
-    MOZ_ASSERT_IF(!enclosingDynamicScope->is<GlobalObject>(),
-                  HasNonSyntacticStaticScopeChain(enclosingStaticScope));
-
+    // Make sure to handle cases when we have a polluted scopechain.
     CompileOptions options(cx, optionsArg);
-    if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingStaticScope))
+    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);
-    Rooted<ScopeObject*> staticScopeObj(cx);
-    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScopeObj, &staticScopeObj))
+    RootedObject staticScopeObj(cx);
+    if (!CreateScopeObjectsForScopeChain(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,
@@ -4299,44 +4182,43 @@ 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 scope, HandleScript script, jsval* rval)
-{
+ExecuteScript(JSContext* cx, HandleObject obj, HandleScript scriptArg, jsval* rval)
+{
+    RootedScript script(cx, scriptArg);
+
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, scope, script);
-    MOZ_ASSERT_IF(!scope->is<GlobalObject>(), script->hasNonSyntacticScope());
+    assertSameCompartment(cx, obj, scriptArg);
+
+    if (!script->hasPollutedGlobalScope() && !obj->is<GlobalObject>()) {
+        script = CloneScript(cx, nullptr, nullptr, script, HasPollutedGlobalScope);
+        if (!script)
+            return false;
+        js::Debugger::onNewScript(cx, script);
+    }
     AutoLastFrameCheck lfc(cx);
-    return Execute(cx, script, *scope, rval);
+    return Execute(cx, script, *obj, rval);
 }
 
 static bool
 ExecuteScript(JSContext* cx, AutoObjectVector& scopeChain, HandleScript scriptArg, jsval* rval)
 {
     RootedObject dynamicScope(cx);
-    Rooted<ScopeObject*> staticScope(cx);
-    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
+    RootedObject unusedStaticScope(cx);
+    if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
         return false;
-
-    RootedScript script(cx, scriptArg);
-    if (!script->hasNonSyntacticScope() && !dynamicScope->is<GlobalObject>()) {
-        script = CloneGlobalScript(cx, staticScope, script);
-        if (!script)
-            return false;
-        js::Debugger::onNewScript(cx, script);
-    }
-
-    return ExecuteScript(cx, dynamicScope, script, rval);
+    return ExecuteScript(cx, dynamicScope, scriptArg, rval);
 }
 
 MOZ_NEVER_INLINE JS_PUBLIC_API(bool)
 JS_ExecuteScript(JSContext* cx, HandleScript scriptArg, MutableHandleValue rval)
 {
     return ExecuteScript(cx, cx->global(), scriptArg, rval.address());
 }
 
@@ -4360,48 +4242,45 @@ JS_ExecuteScript(JSContext* cx, AutoObje
 }
 
 JS_PUBLIC_API(bool)
 JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg)
 {
     CHECK_REQUEST(cx);
     RootedScript script(cx, scriptArg);
     if (script->compartment() != cx->compartment()) {
-        script = CloneGlobalScript(cx, /* enclosingScope = */ nullptr, script);
+        script = CloneScript(cx, nullptr, nullptr, script);
         if (!script)
             return false;
 
         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, Handle<ScopeObject*> staticScope,
-         const ReadOnlyCompileOptions& optionsArg,
+Evaluate(JSContext* cx, HandleObject scope, 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);
 
-    MOZ_ASSERT_IF(!scope->is<GlobalObject>(), HasNonSyntacticStaticScopeChain(staticScope));
-
+    options.setHasPollutedScope(!scope->is<GlobalObject>());
     options.setIsRunOnce(true);
     SourceCompressionTask sct(cx);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(),
-                                                    scope, staticScope,
-                                                    /* evalCaller = */ nullptr, options,
-                                                    srcBuf, /* source = */ nullptr, 0, &sct));
+                                                    scope, nullptr, nullptr, options,
+                                                    srcBuf, 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())
@@ -4421,44 +4300,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);
-    Rooted<ScopeObject*> staticScope(cx);
-    if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
+    RootedObject unusedStaticScope(cx);
+    if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
         return false;
-    return ::Evaluate(cx, dynamicScope, staticScope, optionsArg, srcBuf, rval);
+    return ::Evaluate(cx, dynamicScope, 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(), nullptr, optionsArg, srcBuf, rval);
+  return ::Evaluate(cx, cx->global(), 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(), nullptr, options, srcBuf, rval);
+    bool ok = ::Evaluate(cx, cx->global(), options, srcBuf, rval);
     return ok;
 }
 
 static bool
 Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
          const char* filename, MutableHandleValue rval)
 {
     FileContents buffer(cx);
@@ -4472,17 +4351,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(), nullptr, optionsArg, srcBuf, rval);
+    return ::Evaluate(cx, cx->global(), 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
@@ -3460,16 +3460,17 @@ 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),
@@ -3499,16 +3500,17 @@ 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;
@@ -3591,16 +3593,17 @@ 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,
@@ -3674,16 +3677,17 @@ 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,
@@ -3717,42 +3721,22 @@ extern JS_PUBLIC_API(bool)
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         const char* bytes, size_t length, JS::MutableHandleScript script);
 
 extern JS_PUBLIC_API(bool)
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         const char16_t* chars, size_t length, JS::MutableHandleScript script);
 
 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)
-CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                            const char* bytes, size_t length, JS::MutableHandleScript script);
-
-extern JS_PUBLIC_API(bool)
-CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                            const char16_t* chars, size_t length, JS::MutableHandleScript script);
-
-extern JS_PUBLIC_API(bool)
-CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                            FILE* file, JS::MutableHandleScript script);
-
-extern JS_PUBLIC_API(bool)
-CompileForNonSyntacticScope(JSContext* cx, const ReadOnlyCompileOptions& options,
-                            const char* filename, JS::MutableHandleScript script);
+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)
 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
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -30,17 +30,16 @@
 #include "builtin/Object.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/JitFrameIterator.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/Proxy.h"
-#include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
@@ -520,17 +519,17 @@ js::XDRInterpretedFunction(XDRState<mode
 {
     enum FirstWordFlag {
         HasAtom             = 0x1,
         IsStarGenerator     = 0x2,
         IsLazy              = 0x4,
         HasSingletonType    = 0x8
     };
 
-    /* NB: Keep this in sync with CloneInnerInterpretedFunction. */
+    /* NB: Keep this in sync with CloneFunctionAndScript. */
     RootedAtom atom(xdr->cx());
     uint32_t firstword = 0;        /* bitmask of FirstWordFlag */
     uint32_t flagsword = 0;        /* word for argument count and fun->flags */
 
     JSContext* cx = xdr->cx();
     RootedFunction fun(cx);
     RootedScript script(cx);
     Rooted<LazyScript*> lazy(cx);
@@ -611,19 +610,20 @@ js::XDRInterpretedFunction(XDRState<mode
             return false;
     }
 
     if (mode == XDR_DECODE) {
         fun->setArgCount(flagsword >> 16);
         fun->setFlags(uint16_t(flagsword));
         fun->initAtom(atom);
         if (firstword & IsLazy) {
-            MOZ_ASSERT(fun->lazyScript() == lazy);
+            fun->initLazyScript(lazy);
         } else {
-            MOZ_ASSERT(fun->nonLazyScript() == script);
+            fun->initScript(script);
+            script->setFunction(fun);
             MOZ_ASSERT(fun->nargs() == script->bindings.numArgs());
         }
 
         bool singleton = firstword & HasSingletonType;
         if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
             return false;
         objp.set(fun);
     }
@@ -632,16 +632,54 @@ js::XDRInterpretedFunction(XDRState<mode
 }
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, MutableHandleFunction);
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleObject, HandleScript, MutableHandleFunction);
 
+JSObject*
+js::CloneFunctionAndScript(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun,
+                           PollutedGlobalScopeOption polluted)
+{
+    /* NB: Keep this in sync with XDRInterpretedFunction. */
+    RootedObject cloneProto(cx);
+    if (srcFun->isStarGenerator()) {
+        cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
+        if (!cloneProto)
+            return nullptr;
+    }
+
+    gc::AllocKind allocKind = srcFun->getAllocKind();
+    RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
+                                                  JSFunction::INTERPRETED, nullptr, nullptr,
+                                                  cloneProto, allocKind, TenuredObject));
+    if (!clone)
+        return nullptr;
+
+    JSScript::AutoDelazify srcScript(cx, srcFun);
+    if (!srcScript)
+        return nullptr;
+    RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript, polluted));
+    if (!clonedScript)
+        return nullptr;
+
+    clone->setArgCount(srcFun->nargs());
+    clone->setFlags(srcFun->flags());
+    clone->initAtom(srcFun->displayAtom());
+    clone->initScript(clonedScript);
+    clonedScript->setFunction(clone);
+    if (!JSFunction::setTypeForScriptedFunction(cx, clone))
+        return nullptr;
+
+    RootedScript cloneScript(cx, clone->nonLazyScript());
+    return clone;
+}
+
 /*
  * [[HasInstance]] internal method for Function objects: fetch the .prototype
  * property of its 'this' parameter, and walks the prototype chain of v (only
  * if v is an object) returning true if .prototype is found.
  */
 static bool
 fun_hasInstance(JSContext* cx, HandleObject objArg, MutableHandleValue v, bool* bp)
 {
@@ -785,17 +823,17 @@ CreateFunctionPrototype(JSContext* cx, J
         return nullptr;
 
     protoGroup->setInterpretedFunction(functionProto);
     script->setFunction(functionProto);
 
     /*
      * The default 'new' group of Function.prototype is required by type
      * inference to have unknown properties, to simplify handling of e.g.
-     * NewFunctionClone.
+     * CloneFunctionObject.
      */
     if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto))
         return nullptr;
 
     // Construct the unique [[%ThrowTypeError%]] function object, used only for
     // "callee" and "caller" accessors on strict mode arguments objects.  (The
     // spec also uses this for "arguments" and "caller" on various functions,
     // but we're experimenting with implementing them using accessors on
@@ -1380,23 +1418,26 @@ JSFunction::createScriptForLazilyInterpr
         // has started.
         if (canRelazify && !JS::IsIncrementalGCInProgress(cx->runtime())) {
             LazyScriptCache::Lookup lookup(cx, lazy);
             cx->runtime()->lazyScriptCache.lookup(lookup, script.address());
         }
 
         if (script) {
             RootedObject enclosingScope(cx, lazy->enclosingScope());
-            RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
+            RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script));
             if (!clonedScript)
                 return false;
 
             clonedScript->setSourceObject(lazy->sourceObject());
 
             fun->initAtom(script->functionNonDelazifying()->displayAtom());
+            clonedScript->setFunction(fun);
+
+            fun->setUnlazifiedScript(clonedScript);
 
             if (!lazy->maybeScript())
                 lazy->initScript(clonedScript);
             return true;
         }
 
         MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
 
@@ -1973,47 +2014,43 @@ js::NewScriptedFunction(ExclusiveContext
                         NewObjectKind newKind /* = GenericObject */,
                         HandleObject enclosingDynamicScope /* = nullptr */)
 {
     return NewFunctionWithProto(cx, nullptr, nargs, flags,
                                 enclosingDynamicScope ? enclosingDynamicScope : cx->global(),
                                 atom, nullptr, allocKind, newKind);
 }
 
-#ifdef DEBUG
-static bool
-NewFunctionScopeIsWellFormed(ExclusiveContext* cx, HandleObject parent)
-{
-    // Assert that the parent is null, global, or a debug scope proxy. All
-    // other cases of polluting global scope behavior are handled by
-    // ScopeObjects (viz. non-syntactic DynamicWithObject and
-    // NonSyntacticVariablesObject).
-    RootedObject realParent(cx, SkipScopeParent(parent));
-    return !realParent || realParent == cx->global() ||
-           realParent->is<DebugScopeObject>();
-}
-#endif
-
 JSFunction*
 js::NewFunctionWithProto(ExclusiveContext* cx, Native native,
                          unsigned nargs, JSFunction::Flags flags, HandleObject enclosingDynamicScope,
                          HandleAtom atom, HandleObject proto,
                          gc::AllocKind allocKind /* = AllocKind::FUNCTION */,
                          NewObjectKind newKind /* = GenericObject */)
 {
     MOZ_ASSERT(allocKind == AllocKind::FUNCTION || allocKind == AllocKind::FUNCTION_EXTENDED);
     MOZ_ASSERT_IF(native, !enclosingDynamicScope);
-    MOZ_ASSERT(NewFunctionScopeIsWellFormed(cx, enclosingDynamicScope));
 
     RootedObject funobj(cx);
     // Don't mark asm.js module functions as singleton since they are
     // cloned (via CloneFunctionObjectIfNotSingleton) which assumes that
     // isSingleton implies isInterpreted.
     if (native && !IsAsmJSModuleNative(native))
         newKind = SingletonObject;
+#ifdef DEBUG
+    RootedObject nonScopeParent(cx, SkipScopeParent(enclosingDynamicScope));
+    // We'd like to assert that nonScopeParent is null-or-global, but
+    // js::ExecuteInGlobalAndReturnScope and debugger eval bits mess that up.
+    // Assert that it's one of those or a debug scope proxy or the unqualified
+    // var obj, since it should still be ok to parent to the global in that
+    // case.
+    MOZ_ASSERT(!nonScopeParent || nonScopeParent == cx->global() ||
+               nonScopeParent->is<DebugScopeObject>() ||
+               nonScopeParent->isUnqualifiedVarObj());
+#endif
     funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto, allocKind,
                                      newKind);
     if (!funobj)
         return nullptr;
 
     RootedFunction fun(cx, &funobj->as<JSFunction>());
 
     if (allocKind == AllocKind::FUNCTION_EXTENDED)
@@ -2037,18 +2074,18 @@ js::NewFunctionWithProto(ExclusiveContex
     if (allocKind == AllocKind::FUNCTION_EXTENDED)
         fun->initializeExtended();
     fun->initAtom(atom);
 
     return fun;
 }
 
 bool
-js::CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun,
-                           HandleObject newParent)
+js::CloneFunctionObjectUseSameScript(JSCompartment* compartment, HandleFunction fun,
+                                     HandleObject newParent)
 {
     if (compartment != fun->compartment() ||
         fun->isSingleton() ||
         ObjectGroup::useSingletonForClone(fun))
     {
         return false;
     }
 
@@ -2059,154 +2096,124 @@ js::CanReuseScriptForClone(JSCompartment
     // in that case we have some actual scope objects on our scope chain and
     // whatnot; whoever put them there should be responsible for setting our
     // script's flags appropriately.  We hit this case for JSOP_LAMBDA, for
     // example.
     if (IsSyntacticScope(newParent))
         return true;
 
     // We need to clone the script if we're interpreted and not already marked
-    // as having a non-syntactic scope. If we're lazy, go ahead and clone the
-    // script; see the big comment at the end of CopyScriptInternal for the
-    // explanation of what's going on there.
+    // as having a polluted scope.  If we're lazy, go ahead and clone the
+    // script; see the big comment at the end of CloneScript for the explanation
+    // of what's going on there.
     return !fun->isInterpreted() ||
-           (fun->hasScript() && fun->nonLazyScript()->hasNonSyntacticScope());
+           (fun->hasScript() && fun->nonLazyScript()->hasPollutedGlobalScope());
 }
 
-static inline JSFunction*
-NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind,
-                 gc::AllocKind allocKind, HandleObject proto)
+JSFunction*
+js::CloneFunctionObject(JSContext* cx, HandleFunction fun, HandleObject parent,
+                        gc::AllocKind allocKind,
+                        NewObjectKind newKindArg /* = GenericObject */,
+                        HandleObject proto)
 {
+    MOZ_ASSERT(parent);
+    MOZ_ASSERT(!fun->isBoundFunction());
+
+    bool useSameScript = CloneFunctionObjectUseSameScript(cx->compartment(), fun, parent);
+
+    NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
     RootedObject cloneProto(cx, proto);
-    if (!proto && fun->isStarGenerator()) {
+    if (!cloneProto && fun->isStarGenerator()) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
-
+#ifdef DEBUG
+    RootedObject realParent(cx, SkipScopeParent(parent));
+    // We'd like to assert that realParent is null-or-global, but
+    // js::ExecuteInGlobalAndReturnScope and debugger eval bits mess that up.
+    // Assert that it's one of those or a debug scope proxy or the unqualified
+    // var obj, since it should still be ok to parent to the global in that
+    // case.
+    MOZ_ASSERT(!realParent || realParent == cx->global() ||
+               realParent->is<DebugScopeObject>() ||
+               realParent->isUnqualifiedVarObj());
+#endif
     JSObject* cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto,
                                                  allocKind, newKind);
     if (!cloneobj)
         return nullptr;
     RootedFunction clone(cx, &cloneobj->as<JSFunction>());
 
+    JSScript::AutoDelazify funScript(cx);
+    if (!useSameScript && fun->isInterpretedLazy()) {
+        funScript = fun;
+        if (!funScript)
+            return nullptr;
+    }
+
+    MOZ_ASSERT(useSameScript || !fun->isInterpretedLazy());
+
     uint16_t flags = fun->flags() & ~JSFunction::EXTENDED;
     if (allocKind == AllocKind::FUNCTION_EXTENDED)
         flags |= JSFunction::EXTENDED;
 
     clone->setArgCount(fun->nargs());
     clone->setFlags(flags);
+    if (fun->hasScript()) {
+        clone->initScript(fun->nonLazyScript());
+        clone->initEnvironment(parent);
+    } else if (fun->isInterpretedLazy()) {
+        MOZ_ASSERT(fun->compartment() == clone->compartment());
+        MOZ_ASSERT(useSameScript);
+        LazyScript* lazy = fun->lazyScriptOrNull();
+        clone->initLazyScript(lazy);
+        clone->initEnvironment(parent);
+    } else {
+        clone->initNative(fun->native(), fun->jitInfo());
+    }
     clone->initAtom(fun->displayAtom());
 
     if (allocKind == AllocKind::FUNCTION_EXTENDED) {
         if (fun->isExtended() && fun->compartment() == cx->compartment()) {
             for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++)
                 clone->initExtendedSlot(i, fun->getExtendedSlot(i));
         } else {
             clone->initializeExtended();
         }
     }
 
-    return clone;
-}
-
-JSFunction*
-js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                             gc::AllocKind allocKind /* = FUNCTION */ ,
-                             NewObjectKind newKind /* = GenericObject */,
-                             HandleObject proto /* = nullptr */)
-{
-    MOZ_ASSERT(NewFunctionScopeIsWellFormed(cx, parent));
-    MOZ_ASSERT(!fun->isBoundFunction());
-    MOZ_ASSERT(CanReuseScriptForClone(cx->compartment(), fun, parent));
+    if (useSameScript) {
+        /*
+         * Clone the function, reusing its script. We can use the same group as
+         * the original function provided that its prototype is correct.
+         */
+        if (fun->getProto() == clone->getProto())
+            clone->setGroup(fun->group());
+        return clone;
+    }
 
-    RootedFunction clone(cx, NewFunctionClone(cx, fun, newKind, allocKind, proto));
-    if (!clone)
-        return nullptr;
-
-    if (fun->hasScript()) {
-        clone->initScript(fun->nonLazyScript());
-        clone->initEnvironment(parent);
-    } else if (fun->isInterpretedLazy()) {
-        MOZ_ASSERT(fun->compartment() == clone->compartment());
-        LazyScript* lazy = fun->lazyScriptOrNull();
-        clone->initLazyScript(lazy);
-        clone->initEnvironment(parent);
-    } else {
-        clone->initNative(fun->native(), fun->jitInfo());
-    }
+    RootedFunction cloneRoot(cx, clone);
 
     /*
-     * Clone the function, reusing its script. We can use the same group as
-     * the original function provided that its prototype is correct.
+     * Across compartments or if we have to introduce a polluted scope we have
+     * to clone the script for interpreted functions. Cross-compartment cloning
+     * only happens via JSAPI (JS::CloneFunctionObject) which dynamically
+     * ensures that 'script' has no enclosing lexical scope (only the global
+     * scope or other non-lexical scope).
      */
-    if (fun->getProto() == clone->getProto())
-        clone->setGroup(fun->group());
-    return clone;
-}
-
-JSFunction*
-js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                           HandleObject newStaticScope,
-                           gc::AllocKind allocKind /* = FUNCTION */,
-                           HandleObject proto /* = nullptr */)
-{
-    MOZ_ASSERT(NewFunctionScopeIsWellFormed(cx, parent));
-    MOZ_ASSERT(!fun->isBoundFunction());
-
-    RootedFunction clone(cx, NewFunctionClone(cx, fun, SingletonObject, allocKind, proto));
-    if (!clone)
+    PollutedGlobalScopeOption globalScopeOption = parent->is<GlobalObject>() ?
+        HasCleanGlobalScope : HasPollutedGlobalScope;
+    if (cloneRoot->isInterpreted() &&
+        !CloneFunctionScript(cx, fun, cloneRoot, globalScopeOption, newKindArg))
+    {
         return nullptr;
-
-    if (fun->hasScript()) {
-        clone->initScript(nullptr);
-        clone->initEnvironment(parent);
-    } else {
-        clone->initNative(fun->native(), fun->jitInfo());
     }
 
-    /*
-     * Across compartments or if we have to introduce a non-syntactic scope we
-     * have to clone the script for interpreted functions. Cross-compartment
-     * cloning only happens via JSAPI (JS::CloneFunctionObject) which
-     * dynamically ensures that 'script' has no enclosing lexical scope (only
-     * the global scope or other non-lexical scope).
-     */
-#ifdef DEBUG
-    RootedObject terminatingScope(cx, parent);
-    while (IsSyntacticScope(terminatingScope))
-        terminatingScope = terminatingScope->enclosingScope();
-    MOZ_ASSERT_IF(!terminatingScope->is<GlobalObject>(),
-                  HasNonSyntacticStaticScopeChain(newStaticScope));
-#endif
-
-    if (clone->isInterpreted()) {
-        // The self-hosting compartment is shared across processes, and
-        // AutoDelazify enters fun->compartment(). We would get races if the
-        // self-hosting compartment has lazy interpreted functions.
-        MOZ_ASSERT_IF(fun->compartment()->isSelfHosting, !fun->isInterpretedLazy());
-        JSScript::AutoDelazify funScript(cx);
-        if (fun->isInterpretedLazy()) {
-            funScript = fun;
-            if (!funScript)
-                return nullptr;
-        }
-
-        RootedScript script(cx, fun->nonLazyScript());
-        MOZ_ASSERT(script->compartment() == fun->compartment());
-        MOZ_ASSERT(cx->compartment() == clone->compartment(),
-                   "Otherwise we could relazify clone below!");
-
-        RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newStaticScope, clone, script));
-        if (!clonedScript)
-            return nullptr;
-        Debugger::onNewScript(cx, clonedScript);
-    }
-
-    return clone;
+    return cloneRoot;
 }
 
 /*
  * Return an atom for use as the name of a builtin method with the given
  * property id.
  *
  * Function names are always strings. If id is the well-known @@iterator
  * symbol, this returns "[Symbol.iterator]".
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -628,30 +628,24 @@ class FunctionExtended : public JSFuncti
   private:
     friend class JSFunction;
 
     /* Reserved slots available for storage by particular native functions. */
     HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
 };
 
 extern bool
-CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun, HandleObject newParent);
+CloneFunctionObjectUseSameScript(JSCompartment* compartment, HandleFunction fun,
+                                 HandleObject newParent);
 
 extern JSFunction*
-CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                         gc::AllocKind kind = gc::AllocKind::FUNCTION,
-                         NewObjectKind newKindArg = GenericObject,
-                         HandleObject proto = nullptr);
-
-// Functions whose scripts are cloned are always given singleton types.
-extern JSFunction*
-CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                       HandleObject newStaticScope,
-                       gc::AllocKind kind = gc::AllocKind::FUNCTION,
-                       HandleObject proto = nullptr);
+CloneFunctionObject(JSContext* cx, HandleFunction fun, HandleObject parent,
+                    gc::AllocKind kind = gc::AllocKind::FUNCTION,
+                    NewObjectKind newKindArg = GenericObject,
+                    HandleObject proto = nullptr);
 
 extern bool
 FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
          size_t* bodyEnd);
 
 } // namespace js
 
 inline js::FunctionExtended*
@@ -703,16 +697,20 @@ namespace js {
 
 JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool bodyOnly, bool lambdaParen);
 
 template<XDRMode mode>
 bool
 XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope,
                        HandleScript enclosingScript, MutableHandleFunction objp);
 
+extern JSObject*
+CloneFunctionAndScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
+                       PollutedGlobalScopeOption polluted);
+
 /*
  * Report an error that call.thisv is not compatible with the specified class,
  * assuming that the method (clasp->name).prototype.<name of callee function>
  * is what was called.
  */
 extern void
 ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clasp);
 
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -80,19 +80,14 @@ CloneFunctionObjectIfNotSingleton(JSCont
 
     // These intermediate variables are needed to avoid link errors on some
     // platforms.  Sigh.
     gc::AllocKind finalizeKind = gc::AllocKind::FUNCTION;
     gc::AllocKind extendedFinalizeKind = gc::AllocKind::FUNCTION_EXTENDED;
     gc::AllocKind kind = fun->isExtended()
                          ? extendedFinalizeKind
                          : finalizeKind;
-
-    if (CanReuseScriptForClone(cx->compartment(), fun, parent))
-        return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
-
-    RootedObject staticScope(cx, fun->getOrCreateScript(cx)->enclosingStaticScope());
-    return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto);
+    return CloneFunctionObject(cx, fun, parent, kind, newKind, proto);
 }
 
 } /* namespace js */
 
 #endif /* jsfuninlines_h */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1071,33 +1071,32 @@ NewObjectGCKind(const js::Class* clasp)
         return gc::AllocKind::OBJECT8;
     if (clasp == &JSFunction::class_)
         return gc::AllocKind::OBJECT2;
     return gc::AllocKind::OBJECT4;
 }
 
 static inline JSObject*
 NewObject(ExclusiveContext* cx, HandleObjectGroup group, gc::AllocKind kind,
-          NewObjectKind newKind, uint32_t initialShapeFlags = 0)
+          NewObjectKind newKind)
 {
     const Class* clasp = group->clasp();
 
     MOZ_ASSERT(clasp != &ArrayObject::class_);
     MOZ_ASSERT_IF(clasp == &JSFunction::class_,
                   kind == AllocKind::FUNCTION || kind == AllocKind::FUNCTION_EXTENDED);
 
     // For objects which can have fixed data following the object, only use
     // enough fixed slots to cover the number of reserved slots in the object,
     // regardless of the allocation kind specified.
     size_t nfixed = ClassCanHaveFixedData(clasp)
                     ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
                     : GetGCKindSlots(kind, clasp);
 
-    RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed,
-                                                      initialShapeFlags));
+    RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed));
     if (!shape)
         return nullptr;
 
     gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
     JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
     if (!obj)
         return nullptr;
 
@@ -1135,18 +1134,17 @@ NewObjectWithTaggedProtoIsCachable(Exclu
            newKind == GenericObject &&
            clasp->isNative() &&
            !proto.toObject()->is<GlobalObject>();
 }
 
 JSObject*
 js::NewObjectWithGivenTaggedProto(ExclusiveContext* cxArg, const Class* clasp,
                                   Handle<TaggedProto> proto,
-                                  gc::AllocKind allocKind, NewObjectKind newKind,
-                                  uint32_t initialShapeFlags)
+                                  gc::AllocKind allocKind, NewObjectKind newKind)
 {
     if (CanBeFinalizedInBackground(allocKind, clasp))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, proto, newKind, clasp);
     if (isCachable) {
         JSContext* cx = cxArg->asJSContext();
         JSRuntime* rt = cx->runtime();
@@ -1158,17 +1156,17 @@ js::NewObjectWithGivenTaggedProto(Exclus
                 return obj;
         }
     }
 
     RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, clasp, proto, nullptr));
     if (!group)
         return nullptr;
 
-    RootedObject obj(cxArg, NewObject(cxArg, group, allocKind, newKind, initialShapeFlags));
+    RootedObject obj(cxArg, NewObject(cxArg, group, allocKind, newKind));
     if (!obj)
         return nullptr;
 
     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
         NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         cache.lookupProto(clasp, proto.toObject(), allocKind, &entry);
         cache.fillProto(entry, clasp, proto, allocKind, &obj->as<NativeObject>());
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -417,20 +417,20 @@ class JSObject : public js::gc::Cell
      * Scope chains.
      *
      * The scope chain of an object is the link in the search path when a script
      * does a name lookup on a scope object. For JS internal scope objects ---
      * Call, DeclEnv, Block, and With --- the chain is stored in the first fixed
      * slot of the object.  For other scope objects, the chain goes directly to
      * the global.
      *
-     * In code which is not marked hasNonSyntacticScope, scope chains can
+     * In code which is not marked hasPollutedGlobalScope, scope chains can
      * contain only syntactic scope objects (see IsSyntacticScope) with a global
      * object at the root as the scope of the outermost non-function script. In
-     * hasNonSyntacticScope code, the scope of the outermost non-function
+     * hasPollutedGlobalScope code, the scope of the outermost non-function
      * script might not be a global object, and can have a mix of other objects
      * above it before the global object is reached.
      */
 
     /*
      * Get the enclosing scope of an object. When called on non-scope object,
      * this will just be the global (the name "enclosing scope" still applies
      * in this situation because non-scope objects can be on the scope chain).
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -219,30 +219,25 @@ js::DeleteElement(JSContext* cx, HandleO
 
 /* * */
 
 inline bool
 JSObject::isQualifiedVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isQualifiedVarObj();
-    // TODO: We would like to assert that only GlobalObject or
-    // NonSyntacticVariables object is a qualified varobj, but users of
-    // js::Execute still need to be vetted. See bug 1171177.
     return hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
 }
 
 inline bool
 JSObject::isUnqualifiedVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isUnqualifiedVarObj();
-    bool rv = hasAllFlags(js::BaseShape::UNQUALIFIED_VAROBJ);
-    MOZ_ASSERT_IF(rv, is<js::GlobalObject>() || is<js::NonSyntacticVariablesObject>());
-    return rv;
+    return hasAllFlags(js::BaseShape::UNQUALIFIED_VAROBJ);
 }
 
 namespace js {
 
 inline bool
 ClassCanHaveFixedData(const Class* clasp)
 {
     // Normally, the number of fixed slots given an object is the maximum
@@ -603,48 +598,35 @@ IsInternalFunctionObject(JSObject* funob
 typedef AutoVectorRooter<PropertyDescriptor> AutoPropertyDescriptorVector;
 
 /*
  * Make an object with the specified prototype. If parent is null, it will
  * default to the prototype's global if the prototype is non-null.
  */
 JSObject*
 NewObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp, Handle<TaggedProto> proto,
-                              gc::AllocKind allocKind, NewObjectKind newKind,
-                              uint32_t initialShapeFlags = 0);
+                              gc::AllocKind allocKind, NewObjectKind newKind);
 
 inline JSObject*
 NewObjectWithGivenTaggedProto(ExclusiveContext* cx, const Class* clasp, Handle<TaggedProto> proto,
-                              NewObjectKind newKind = GenericObject,
-                              uint32_t initialShapeFlags = 0)
+                              NewObjectKind newKind = GenericObject)
 {
     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
-    return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind, initialShapeFlags);
+    return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind);
 }
 
 template <typename T>
 inline T*
 NewObjectWithGivenTaggedProto(ExclusiveContext* cx, Handle<TaggedProto> proto,
-                              NewObjectKind newKind = GenericObject,
-                              uint32_t initialShapeFlags = 0)
+                              NewObjectKind newKind = GenericObject)
 {
-    JSObject* obj = NewObjectWithGivenTaggedProto(cx, &T::class_, proto, newKind,
-                                                  initialShapeFlags);
+    JSObject* obj = NewObjectWithGivenTaggedProto(cx, &T::class_, proto, newKind);
     return obj ? &obj->as<T>() : nullptr;
 }
 
-template <typename T>
-inline T*
-NewObjectWithNullTaggedProto(ExclusiveContext* cx, NewObjectKind newKind = GenericObject,
-                             uint32_t initialShapeFlags = 0)
-{
-    Rooted<TaggedProto> nullProto(cx, TaggedProto(nullptr));
-    return NewObjectWithGivenTaggedProto<T>(cx, nullProto, newKind, initialShapeFlags);
-}
-
 inline JSObject*
 NewObjectWithGivenProto(ExclusiveContext* cx, const Class* clasp, HandleObject proto,
                         gc::AllocKind allocKind, NewObjectKind newKind)
 {
     return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto), allocKind,
                                          newKind);
 }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -571,20 +571,20 @@ enum XDRClassKind {
     CK_BlockObject = 0,
     CK_WithObject  = 1,
     CK_JSFunction  = 2,
     CK_JSObject    = 3
 };
 
 template<XDRMode mode>
 bool
-js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript,
+js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
               HandleFunction fun, MutableHandleScript scriptp)
 {
-    /* NB: Keep this in sync with CopyScript. */
+    /* NB: Keep this in sync with CloneScript. */
 
     enum ScriptBits {
         NoScriptRval,
         SavedCallerFun,
         Strict,
         ContainsDynamicNameAccess,
         FunHasExtensibleScope,
         FunNeedsDeclEnvObject,
@@ -595,30 +595,29 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         IsLegacyGenerator,
         IsStarGenerator,
         OwnSource,
         ExplicitUseStrict,
         SelfHosted,
         HasSingleton,
         TreatAsRunOnce,
         HasLazyScript,
-        HasNonSyntacticScope,
+        HasPollutedGlobalScope,
     };
 
     uint32_t length, lineno, column, nslots, staticLevel;
     uint32_t natoms, nsrcnotes, i;
     uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes, nyieldoffsets;
     uint32_t prologueLength, version;
     uint32_t funLength = 0;
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
 
     JSContext* cx = xdr->cx();
     RootedScript script(cx);
-    RootedObject enclosingScope(cx, enclosingScopeArg);
     natoms = nsrcnotes = 0;
     nconsts = nobjects = nregexps = ntrynotes = nblockscopes = nyieldoffsets = 0;
 
     /* XDR arguments and vars. */
     uint16_t nargs = 0;
     uint16_t nblocklocals = 0;
     uint16_t nbodylevellexicals = 0;
     uint32_t nvars = 0;
@@ -728,18 +727,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (script->isStarGenerator())
             scriptBits |= (1 << IsStarGenerator);
         if (script->hasSingletons())
             scriptBits |= (1 << HasSingleton);
         if (script->treatAsRunOnce())
             scriptBits |= (1 << TreatAsRunOnce);
         if (script->isRelazifiable())
             scriptBits |= (1 << HasLazyScript);
-        if (script->hasNonSyntacticScope())
-            scriptBits |= (1 << HasNonSyntacticScope);
+        if (script->hasPollutedGlobalScope())
+            scriptBits |= (1 << HasPollutedGlobalScope);
     }
 
     if (!xdr->codeUint32(&prologueLength))
         return false;
     if (!xdr->codeUint32(&version))
         return false;
 
     // To fuse allocations, we need lengths of all embedded arrays early.
@@ -797,36 +796,20 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         } else {
             MOZ_ASSERT(enclosingScript);
             // When decoding, all the scripts and the script source object
             // are in the same compartment, so the script's source object
             // should never be a cross-compartment wrapper.
             MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
             sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
         }
-
-        // If the outermost script has a non-syntactic scope, reflect that on
-        // the static scope chain.
-        if (scriptBits & (1 << HasNonSyntacticScope) && !enclosingScope) {
-            enclosingScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
-            if (!enclosingScope)
-                return false;
-        }
-
         script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
                                   options, /* staticLevel = */ 0, sourceObject, 0, 0);
         if (!script)
             return false;
-
-        // Set the script in its function now so that inner scripts to be
-        // decoded may iterate the static scope chain.
-        if (fun) {
-            fun->initScript(script);
-            script->setFunction(fun);
-        }
     }
 
     /* JSScript::partiallyInit assumes script->bindings is fully initialized. */
     LifoAllocScope las(&cx->tempLifoAlloc());
     if (!XDRScriptBindings(xdr, las, nargs, nvars, nbodylevellexicals, nblocklocals,
                            nunaliasedvars, nunaliasedbodylevellexicals, script))
         return false;
 
@@ -861,18 +844,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (scriptBits & (1 << NeedsArgsObj))
             script->setNeedsArgsObj(true);
         if (scriptBits & (1 << IsGeneratorExp))
             script->isGeneratorExp_ = true;
         if (scriptBits & (1 << HasSingleton))
             script->hasSingletons_ = true;
         if (scriptBits & (1 << TreatAsRunOnce))
             script->treatAsRunOnce_ = true;
-        if (scriptBits & (1 << HasNonSyntacticScope))
-            script->hasNonSyntacticScope_ = true;
+        if (scriptBits & (1 << HasPollutedGlobalScope))
+            script->hasPollutedGlobalScope_ = true;
 
         if (scriptBits & (1 << IsLegacyGenerator)) {
             MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
             script->setGeneratorKind(LegacyGenerator);
         } else if (scriptBits & (1 << IsStarGenerator))
             script->setGeneratorKind(StarGenerator);
     }
 
@@ -994,22 +977,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             if (!xdr->codeUint32(&enclosingStaticScopeIndex))
                 return false;
             Rooted<JSObject*> enclosingStaticScope(cx);
             if (mode == XDR_DECODE) {
                 if (enclosingStaticScopeIndex != UINT32_MAX) {
                     MOZ_ASSERT(enclosingStaticScopeIndex < i);
                     enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
                 } else {
-                    // This is not ternary because MSVC can't typecheck the
-                    // ternary.
-                    if (fun)
-                        enclosingStaticScope = fun;
-                    else
-                        enclosingStaticScope = enclosingScope;
+                    enclosingStaticScope = fun;
                 }
             }
 
             if (classk == CK_BlockObject) {
                 Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject*>(objp->get()));
                 if (!XDRStaticBlockObject(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
@@ -1036,46 +1014,34 @@ js::XDRScript(XDRState<mode>* xdr, Handl
                 else {
                     MOZ_ASSERT(function->isAsmJSNative());
                     JS_ReportError(cx, "AsmJS modules are not yet supported in XDR serialization.");
                     return false;
                 }
 
                 StaticScopeIter<NoGC> ssi(funEnclosingScope);
 
-                // Starting from a nested function, hitting a non-syntactic
-                // scope on the static scope chain means that its enclosing
-                // function has a non-syntactic scope. Nested functions
-                // themselves never have non-syntactic scope chains.
-                if (ssi.done() ||
-                    ssi.type() == StaticScopeIter<NoGC>::NonSyntactic ||
-                    ssi.type() == StaticScopeIter<NoGC>::Function)
-                {
-                    MOZ_ASSERT_IF(ssi.done() || ssi.type() != StaticScopeIter<NoGC>::Function, !fun);
+                if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::Function) {
+                    MOZ_ASSERT(ssi.done() == !fun);
                     funEnclosingScopeIndex = UINT32_MAX;
                 } else if (ssi.type() == StaticScopeIter<NoGC>::Block) {
                     funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block());
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
                 } else {
                     funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.staticWith());
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
                 }
             }
 
             if (!xdr->codeUint32(&funEnclosingScopeIndex))
                 return false;
 
             if (mode == XDR_DECODE) {
                 if (funEnclosingScopeIndex == UINT32_MAX) {
-                    // This is not ternary because MSVC can't typecheck the
-                    // ternary.
-                    if (fun)
-                        funEnclosingScope = fun;
-                    else
-                        funEnclosingScope = enclosingScope;
+                    funEnclosingScope = fun;
                 } else {
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
                     funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
                 }
             }
 
             // Code nested function and script.
             RootedFunction tmp(cx);
@@ -1206,23 +1172,19 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
 
         if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
             !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
             !xdr->codeUint64(&packedFields))
         {
             return false;
         }
 
-        if (mode == XDR_DECODE) {
+        if (mode == XDR_DECODE)
             lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
                                         packedFields, begin, end, lineno, column));
-            if (!lazy)
-                return false;
-            fun->initLazyScript(lazy);
-        }
     }
 
     // Code free variables.
     if (!XDRLazyFreeVariables(xdr, lazy))
         return false;
 
     // Code inner functions.
     {
@@ -2462,26 +2424,21 @@ JSScript::Create(ExclusiveContext* cx, H
 
     PodZero(script.get());
     new (&script->bindings) Bindings;
 
     script->enclosingStaticScope_ = enclosingScope;
     script->savedCallerFun_ = savedCallerFun;
     script->initCompartment(cx);
 
+    script->hasPollutedGlobalScope_ = 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) {
@@ -2707,23 +2664,20 @@ 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;
@@ -3046,157 +3000,158 @@ js::DescribeScriptedCallerForCompilation
 template <class T>
 static inline T*
 Rebase(JSScript* dst, JSScript* src, T* srcp)
 {
     size_t off = reinterpret_cast<uint8_t*>(srcp) - src->data;
     return reinterpret_cast<T*>(dst->data + off);
 }
 
-static JSObject*
-CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun)
-{
-    /* NB: Keep this in sync with XDRInterpretedFunction. */
-    RootedObject cloneProto(cx);
-    if (srcFun->isStarGenerator()) {
-        cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
-        if (!cloneProto)
-            return nullptr;
-    }
-
-    gc::AllocKind allocKind = srcFun->getAllocKind();
-    RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
-                                                  JSFunction::INTERPRETED, nullptr, nullptr,
-                                                  cloneProto, allocKind, TenuredObject));
-    if (!clone)
-        return nullptr;
-
-    JSScript::AutoDelazify srcScript(cx, srcFun);
-    if (!srcScript)
-        return nullptr;
-    JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript);
-    if (!cloneScript)
-        return nullptr;
-
-    clone->setArgCount(srcFun->nargs());
-    clone->setFlags(srcFun->flags());
-    clone->initAtom(srcFun->displayAtom());
-    if (!JSFunction::setTypeForScriptedFunction(cx, clone))
-        return nullptr;
-
-    return clone;
-}
-
-bool
-js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
-                       HandleScript dst)
+JSScript*
+js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src,
+                PollutedGlobalScopeOption polluted /* = HasCleanGlobalScope */,
+                NewObjectKind newKind /* = GenericObject */)
 {
     if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
         // Toplevel run-once scripts may not be cloned.
         JS_ReportError(cx, "No cloning toplevel run-once scripts");
-        return false;
+        return nullptr;
     }
 
     /* NB: Keep this in sync with XDRScript. */
 
     /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */
     MOZ_ASSERT(!src->sourceObject()->asTenured().isMarked(gc::GRAY));
 
     uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
     uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
     uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0;
-    uint32_t nyieldoffsets = src->hasYieldOffsets() ? src->yieldOffsets().length() : 0;
 
     /* Script data */
 
     size_t size = src->dataSize();
     uint8_t* data = AllocScriptData(cx->zone(), size);
     if (size && !data)
-        return false;
+        return nullptr;
 
     /* Bindings */
 
     Rooted<Bindings> bindings(cx);
     InternalHandle<Bindings*> bindingsHandle =
         InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
     if (!Bindings::clone(cx, bindingsHandle, data, src))
-        return false;
+        return nullptr;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject* vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
             RootedObject obj(cx, vector[i]);
             RootedObject clone(cx);
             if (obj->is<NestedScopeObject>()) {
                 Rooted<NestedScopeObject*> innerBlock(cx, &obj->as<NestedScopeObject>());
 
                 RootedObject enclosingScope(cx);
                 if (NestedScopeObject* enclosingBlock = innerBlock->enclosingNestedScope())
                     enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)];
                 else
-                    enclosingScope = scriptStaticScope;
+                    enclosingScope = fun;
 
                 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
             } else if (obj->is<JSFunction>()) {
                 RootedFunction innerFun(cx, &obj->as<JSFunction>());
                 if (innerFun->isNative()) {
                     if (cx->compartment() != innerFun->compartment()) {
                         MOZ_ASSERT(innerFun->isAsmJSNative());
                         JS_ReportError(cx, "AsmJS modules do not yet support cloning.");
-                        return false;
+                        return nullptr;
                     }
                     clone = innerFun;
                 } else {
                     if (innerFun->isInterpretedLazy()) {
                         AutoCompartment ac(cx, innerFun);
                         if (!innerFun->getOrCreateScript(cx))
-                            return false;
+                            return nullptr;
                     }
                     RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
                     StaticScopeIter<CanGC> ssi(cx, staticScope);
                     RootedObject enclosingScope(cx);
-                    if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::NonSyntactic) {
-                        enclosingScope = scriptStaticScope;
-                    } else if (ssi.type() == StaticScopeIter<CanGC>::Function) {
-                        MOZ_ASSERT(scriptStaticScope->is<JSFunction>());
-                        enclosingScope = scriptStaticScope;
-                    } else if (ssi.type() == StaticScopeIter<CanGC>::Block) {
+                    if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::Function)
+                        enclosingScope = fun;
+                    else if (ssi.type() == StaticScopeIter<CanGC>::Block)
                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
-                    } else {
+                    else
                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
-                    }
-
-                    clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun);
+
+                    clone = CloneFunctionAndScript(cx, enclosingScope, innerFun, polluted);
                 }
             } else {
                 clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
             }
             if (!clone || !objects.append(clone))
-                return false;
+                return nullptr;
         }
     }
 
     /* RegExps */
 
     AutoObjectVector regexps(cx);
     for (unsigned i = 0; i < nregexps; i++) {
         HeapPtrObject* vector = src->regexps()->vector;
         for (unsigned i = 0; i < nregexps; i++) {
             JSObject* clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>());
             if (!clone || !regexps.append(clone))
-                return false;
+                return nullptr;
         }
     }
 
-    /* Now that all fallible allocation is complete, do the copying. */
+    /*
+     * Wrap the script source object as needed. Self-hosted scripts may be
+     * in another runtime, so lazily create a new script source object to
+     * use for them.
+     */
+    RootedObject sourceObject(cx);
+    if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
+        if (!cx->compartment()->selfHostingScriptSource) {
+            CompileOptions options(cx);
+            FillSelfHostingCompileOptions(options);
+
+            ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
+            if (!obj)
+                return nullptr;
+            cx->compartment()->selfHostingScriptSource.set(obj);
+        }
+        sourceObject = cx->compartment()->selfHostingScriptSource;
+    } else {
+        sourceObject = src->sourceObject();
+        if (!cx->compartment()->wrap(cx, &sourceObject))
+            return nullptr;
+    }
+
+    /* Now that all fallible allocation is complete, create the GC thing. */
+
+    CompileOptions options(cx);
+    options.setMutedErrors(src->mutedErrors())
+           .setHasPollutedScope(src->hasPollutedGlobalScope() ||
+                                polluted == HasPollutedGlobalScope)
+           .setSelfHostingMode(src->selfHosted())
+           .setNoScriptRval(src->noScriptRval())
+           .setVersion(src->getVersion());
+
+    RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun(),
+                                          options, src->staticLevel(),
+                                          sourceObject, src->sourceStart(), src->sourceEnd()));
+    if (!dst) {
+        js_free(data);
+        return nullptr;
+    }
 
     dst->bindings = bindings;
 
     /* This assignment must occur before all the Rebase calls. */
     dst->data = data;
     dst->dataSize_ = size;
     memcpy(data, src->data, size);
 
@@ -3245,120 +3200,72 @@ js::detail::CopyScript(JSContext* cx, Ha
         dst->regexps()->vector = vector;
         for (unsigned i = 0; i < nregexps; ++i)
             vector[i].init(&regexps[i]->as<NativeObject>());
     }
     if (ntrynotes != 0)
         dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
     if (nblockscopes != 0)
         dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector);
-    if (nyieldoffsets != 0)
-        dst->yieldOffsets().vector_ = Rebase<uint32_t>(dst, src, src->yieldOffsets().vector_);
 
     /*
      * Function delazification assumes that their script does not have a
-     * non-syntactic global scope.  We ensure that as follows:
+     * polluted global scope.  We ensure that as follows:
      *
      * 1) Initial parsing only creates lazy functions if
-     *    !hasNonSyntacticScope.
+     *    !hasPollutedGlobalScope.
      * 2) Cloning a lazy function into a non-global scope will always require
      *    that its script be cloned.  See comments in
      *    CloneFunctionObjectUseSameScript.
      * 3) Cloning a script never sets a lazyScript on the clone, so the function
      *    cannot be relazified.
      *
-     * If you decide that lazy functions should be supported with a
-     * non-syntactic global scope, make sure delazification can deal.
-     */
-    MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->maybeLazyScript());
-    MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->isRelazifiable());
-    return true;
-}
-
-static JSScript*
-CreateEmptyScriptForClone(JSContext* cx, HandleObject enclosingScope, HandleScript src)
-{
-    /*
-     * Wrap the script source object as needed. Self-hosted scripts may be
-     * in another runtime, so lazily create a new script source object to
-     * use for them.
+     * If you decide that lazy functions should be supported with a polluted
+     * global scope, make sure delazification can deal.
      */
-    RootedObject sourceObject(cx);
-    if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
-        if (!cx->compartment()->selfHostingScriptSource) {
-            CompileOptions options(cx);
-            FillSelfHostingCompileOptions(options);
-
-            ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
-            if (!obj)
-                return nullptr;
-            cx->compartment()->selfHostingScriptSource.set(obj);
-        }
-        sourceObject = cx->compartment()->selfHostingScriptSource;
-    } else {
-        sourceObject = src->sourceObject();
-        if (!cx->compartment()->wrap(cx, &sourceObject))
-            return nullptr;
-    }
-
-    CompileOptions options(cx);
-    options.setMutedErrors(src->mutedErrors())
-           .setSelfHostingMode(src->selfHosted())
-           .setNoScriptRval(src->noScriptRval())
-           .setVersion(src->getVersion());
-
-    return JSScript::Create(cx, enclosingScope, src->savedCallerFun(),
-                            options, src->staticLevel(),
-                            sourceObject, src->sourceStart(), src->sourceEnd());
-}
-
-JSScript*
-js::CloneGlobalScript(JSContext* cx, Handle<ScopeObject*> enclosingScope, HandleScript src)
-{
-    // No enclosingScope means clean global.
-    MOZ_ASSERT(!enclosingScope || enclosingScope->is<StaticNonSyntacticScopeObjects>());
-
-    RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
-    if (!dst)
-        return nullptr;
-
-    if (!detail::CopyScript(cx, enclosingScope, src, dst))
-        return nullptr;
-
+    MOZ_ASSERT_IF(dst->hasPollutedGlobalScope(), !dst->maybeLazyScript());
+    MOZ_ASSERT_IF(dst->hasPollutedGlobalScope(), !dst->isRelazifiable());
     return dst;
 }
 
-JSScript*
-js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
-                            HandleScript src)
+bool
+js::CloneFunctionScript(JSContext* cx, HandleFunction original, HandleFunction clone,
+                        PollutedGlobalScopeOption polluted, NewObjectKind newKind)
 {
-    MOZ_ASSERT(fun->isInterpreted());
-
-    // Allocate the destination script up front and set it as the script of
-    // |fun|, which is to be its container.
-    //
-    // This is so that when cloning nested functions, they can walk the static
-    // scope chain via fun and correctly compute the presence of a
-    // non-syntactic global.
-    RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
-    if (!dst)
-        return nullptr;
-
-    dst->setFunction(fun);
-    if (fun->isInterpretedLazy())
-        fun->setUnlazifiedScript(dst);
-    else
-        fun->initScript(dst);
-
-    if (!detail::CopyScript(cx, fun, src, dst)) {
-        fun->setScript(nullptr);
-        return nullptr;
+    RootedScript script(cx, clone->nonLazyScript());
+    MOZ_ASSERT(script);
+    MOZ_ASSERT(script->compartment() == original->compartment());
+    MOZ_ASSERT(cx->compartment() == clone->compartment(),
+               "Otherwise we could relazify clone below!");
+
+    // The only scripts with enclosing static scopes that may be cloned across
+    // compartments are non-strict, indirect eval scripts, as their dynamic
+    // scope chains terminate in the global scope immediately.
+    RootedObject scope(cx, script->enclosingStaticScope());
+    if (script->compartment() != cx->compartment() && scope) {
+        MOZ_ASSERT(!scope->as<StaticEvalObject>().isDirect() &&
+                   !scope->as<StaticEvalObject>().isStrict());
+        scope = StaticEvalObject::create(cx, nullptr);
+        if (!scope)
+            return false;
     }
 
-    return dst;
+    clone->initScript(nullptr);
+
+    JSScript* cscript = CloneScript(cx, scope, clone, script, polluted, newKind);
+    if (!cscript)
+        return false;
+
+    clone->setScript(cscript);
+    cscript->setFunction(clone);
+
+    script = clone->nonLazyScript();
+    Debugger::onNewScript(cx, script);
+
+    return true;
 }
 
 DebugScript*
 JSScript::debugScript()
 {
     MOZ_ASSERT(hasDebugScript_);
     DebugScriptMap* map = compartment()->debugScriptMap;
     MOZ_ASSERT(map);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -50,25 +50,16 @@ struct SourceCompressionTask;
 class Shape;
 class NestedScopeObject;
 
 namespace frontend {
     struct BytecodeEmitter;
     class UpvarCookie;
 }
 
-namespace detail {
-
-// Do not call this directly! It is exposed for the friend declarations in
-// this file.
-bool
-CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst);
-
-} // namespace detail
-
 }
 
 /*
  * Type of try note associated with each catch or finally block, and also with
  * for-in and other kinds of loops. Non-for-in loops do not need these notes
  * for exception unwinding, but storing their boundaries here is helpful for
  * heuristics that need to know whether a given op is inside a loop.
  */
@@ -134,20 +125,16 @@ struct TryNoteArray {
 };
 
 struct BlockScopeArray {
     BlockScopeNote* vector;     // Array of indexed BlockScopeNote records.
     uint32_t        length;     // Count of indexed try notes.
 };
 
 class YieldOffsetArray {
-    friend bool
-    detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
-                       HandleScript dst);
-
     uint32_t*       vector_;   // Array of bytecode offsets.
     uint32_t        length_;    // Count of bytecode offsets.
 
   public:
     void init(uint32_t* vector, uint32_t length) {
         vector_ = vector;
         length_ = length;
     }
@@ -763,16 +750,26 @@ 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,
               HandleFunction fun, MutableHandle<LazyScript*> lazy);
 
 /*
  * Code any constant value.
  */
@@ -782,23 +779,23 @@ XDRScriptConst(XDRState<mode>* xdr, Muta
 
 } /* namespace js */
 
 class JSScript : public js::gc::TenuredCell
 {
     template <js::XDRMode mode>
     friend
     bool
-    js::XDRScript(js::XDRState<mode>* xdr, js::HandleObject enclosingScope,
-                  js::HandleScript enclosingScript,
+    js::XDRScript(js::XDRState<mode>* xdr, js::HandleObject enclosingScope, js::HandleScript enclosingScript,
                   js::HandleFunction fun, js::MutableHandleScript scriptp);
 
-    friend bool
-    js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src,
-                           js::HandleScript dst);
+    friend JSScript*
+    js::CloneScript(JSContext* cx, js::HandleObject enclosingScope, js::HandleFunction fun,
+                    js::HandleScript src, js::PollutedGlobalScopeOption polluted,
+                    js::NewObjectKind newKind);
 
   public:
     //
     // We order fields according to their size in order to avoid wasting space
     // for alignment.
     //
 
     // Larger-than-word-sized fields.
@@ -934,17 +931,17 @@ class JSScript : public js::gc::TenuredC
     bool strict_:1;
 
     // Code has "use strict"; explicitly.
     bool explicitUseStrict_:1;
 
     // True if the script has a non-syntactic scope on its dynamic scope chain.
     // That is, there are objects about which we know nothing between the
     // outermost syntactic scope and the global.
-    bool hasNonSyntacticScope_:1;
+    bool hasPollutedGlobalScope_:1;
 
     // see Parser::selfHostingMode.
     bool selfHosted_:1;
 
     // See FunctionContextFlags.
     bool bindingsAccessedDynamically_:1;
     bool funHasExtensibleScope_:1;
     bool funNeedsDeclEnvObject_:1;
@@ -1176,18 +1173,18 @@ class JSScript : public js::gc::TenuredC
     bool savedCallerFun() const { return savedCallerFun_; }
 
     bool strict() const {
         return strict_;
     }
 
     bool explicitUseStrict() const { return explicitUseStrict_; }
 
-    bool hasNonSyntacticScope() const {
-        return hasNonSyntacticScope_;
+    bool hasPollutedGlobalScope() const {
+        return hasPollutedGlobalScope_;
     }
 
     bool selfHosted() const { return selfHosted_; }
     bool bindingsAccessedDynamically() const { return bindingsAccessedDynamically_; }
     bool funHasExtensibleScope() const {
         return funHasExtensibleScope_;
     }
     bool funNeedsDeclEnvObject() const {
@@ -2291,22 +2288,19 @@ enum LineOption {
 };
 
 extern void
 DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScript,
                                      const char** file, unsigned* linenop,
                                      uint32_t* pcOffset, bool* mutedErrors,
                                      LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
 
-JSScript*
-CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
-                        HandleScript src);
-
-JSScript*
-CloneGlobalScript(JSContext* cx, Handle<ScopeObject*> enclosingScope, HandleScript src);
+bool
+CloneFunctionScript(JSContext* cx, HandleFunction original, HandleFunction clone,
+                    PollutedGlobalScopeOption polluted, NewObjectKind newKind);
 
 } /* namespace js */
 
 // JS::ubi::Nodes can point to js::LazyScripts; they're js::gc::Cell instances
 // with no associated compartment.
 namespace JS {
 namespace ubi {
 template<> struct Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> { };
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4313,46 +4313,16 @@ ReflectTrackedOptimizations(JSContext* c
     RootedValue jsonVal(cx);
     if (!JS_ParseJSON(cx, str, &jsonVal))
         return false;
 
     args.rval().set(jsonVal);
     return true;
 }
 
-#ifdef DEBUG
-static bool
-DumpStaticScopeChain(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject callee(cx, &args.callee());
-
-    if (args.length() != 1) {
-        ReportUsageError(cx, callee, "Wrong number of arguments");
-        return false;
-    }
-
-    if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
-        ReportUsageError(cx, callee, "Argument must be an interpreted function");
-        return false;
-    }
-
-    RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
-    if (!fun->isInterpreted()) {
-        ReportUsageError(cx, callee, "Argument must be an interpreted function");
-        return false;
-    }
-
-    js::DumpStaticScopeChain(fun->getOrCreateScript(cx));
-
-    args.rval().setUndefined();
-    return true;
-}
-#endif
-
 namespace js {
 namespace shell {
 
 class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
     Vector<UniqueChars, 1, js::SystemAllocPolicy> log;
     bool oom;
     bool enteredWithoutExit;
 
@@ -4991,22 +4961,16 @@ static const JSFunctionSpecWithHelp fuzz
 "        deeply nested wrapper chains that cannot exist in the wild."),
 
     JS_FN_HELP("trackedOpts", ReflectTrackedOptimizations, 1, 0,
 "trackedOpts(fun)",
 "  Returns an object describing the tracked optimizations of |fun|, if\n"
 "  any. If |fun| is not a scripted function or has not been compiled by\n"
 "  Ion, null is returned."),
 
-#ifdef DEBUG
-    JS_FN_HELP("dumpStaticScopeChain", DumpStaticScopeChain, 1, 0,
-"dumpStaticScopeChain(fun)",
-"  Prints the static scope chain of an interpreted function fun."),
-#endif
-
     JS_FS_HELP_END
 };
 
 static const JSFunctionSpecWithHelp console_functions[] = {
     JS_FN_HELP("log", Print, 0, 0,
 "log([exp ...])",
 "  Evaluate and print expressions to stdout.\n"
 "  This function is an alias of the print() function."),
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6256,34 +6256,32 @@ 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<ScopeObject*> enclosingStaticScope(cx);
-    if (!env->is<GlobalObject>())
-        enclosingStaticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
-    Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosingStaticScope));
+    Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, nullptr));
     if (!staticScope)
         return false;
     CompileOptions options(cx);
-    options.setIsRunOnce(true)
+    options.setHasPollutedScope(true)
+           .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);
-    RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, staticScope,
-                                                    callerScript, options, srcBuf,
+    RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, callerScript,
+                                                    staticScope, options, srcBuf,
                                                     /* source = */ nullptr,
                                                     /* staticLevel = */ frame ? 1 : 0));
     if (!script)
         return false;
 
     if (script->strict())
         staticScope->setStrict();
 
@@ -6421,18 +6419,24 @@ DebuggerGenericEval(JSContext* cx, const
             }
         }
 
         AutoObjectVector scopeChain(cx);
         if (!scopeChain.append(nenv))
             return false;
 
         RootedObject dynamicScope(cx);
-        if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope))
+        // We ignore the static scope here.  See comments about static
+        // scopes in EvaluateInEnv.
+        RootedObject unusedStaticScope(cx);
+        if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope,
+                                             &unusedStaticScope))
+        {
             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/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -299,19 +299,19 @@ SetAliasedVarOperation(JSContext* cx, JS
 inline bool
 SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject scope,
                  HandleValue val)
 {
     MOZ_ASSERT(*pc == JSOP_SETNAME ||
                *pc == JSOP_STRICTSETNAME ||
                *pc == JSOP_SETGNAME ||
                *pc == JSOP_STRICTSETGNAME);
-    MOZ_ASSERT_IF(*pc == JSOP_SETGNAME && !script->hasNonSyntacticScope(),
+    MOZ_ASSERT_IF(*pc == JSOP_SETGNAME && !script->hasPollutedGlobalScope(),
                   scope == cx->global());
-    MOZ_ASSERT_IF(*pc == JSOP_STRICTSETGNAME && !script->hasNonSyntacticScope(),
+    MOZ_ASSERT_IF(*pc == JSOP_STRICTSETGNAME && !script->hasPollutedGlobalScope(),
                   scope == cx->global());
 
     bool strict = *pc == JSOP_STRICTSETNAME || *pc == JSOP_STRICTSETGNAME;
     RootedPropertyName name(cx, script->getName(pc));
 
     // In strict mode, assigning to an undeclared global variable is an
     // error. To detect this, we call NativeSetProperty directly and pass
     // Unqualified. It stores the error, if any, in |result|.
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -269,17 +269,17 @@ GetNameOperation(JSContext* cx, Interpre
      * Skip along the scope chain to the enclosing global object. This is
      * used for GNAME opcodes where the bytecode emitter has determined a
      * name access must be on the global. It also insulates us from bugs
      * in the emitter: type inference will assume that GNAME opcodes are
      * accessing the global object, and the inferred behavior should match
      * the actual behavior even if the id could be found on the scope chain
      * before the global object.
      */
-    if (IsGlobalOp(JSOp(*pc)) && !fp->script()->hasNonSyntacticScope())
+    if (IsGlobalOp(JSOp(*pc)) && !fp->script()->hasPollutedGlobalScope())
         obj = &obj->global();
 
     Shape* shape = nullptr;
     JSObject* scope = nullptr;
     JSObject* pobj = nullptr;
     if (LookupNameNoGC(cx, name, obj, &scope, &pobj, &shape)) {
         if (FetchNameNoGC(pobj, shape, vp))
             return CheckUninitializedLexical(cx, name, vp);
@@ -863,17 +863,17 @@ js::ExecuteKernel(JSContext* cx, HandleS
         RootedObject thisObj(cx, &thisv.toObject());
         AutoSuppressGC nogc(cx);
         MOZ_ASSERT(GetOuterObject(cx, thisObj) == thisObj);
     }
     RootedObject terminatingScope(cx, &scopeChainArg);
     while (IsSyntacticScope(terminatingScope))
         terminatingScope = terminatingScope->enclosingScope();
     MOZ_ASSERT(terminatingScope->is<GlobalObject>() ||
-               script->hasNonSyntacticScope());
+               script->hasPollutedGlobalScope());
 #endif
 
     if (script->treatAsRunOnce()) {
         if (script->hasRunOnce()) {
             JS_ReportError(cx, "Trying to execute a run-once script multiple times");
             return false;
         }
 
@@ -899,18 +899,18 @@ js::ExecuteKernel(JSContext* cx, HandleS
 bool
 js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value* rval)
 {
     /* The scope chain is something we control, so we know it can't
        have any outer objects on it. */
     RootedObject scopeChain(cx, &scopeChainArg);
     MOZ_ASSERT(scopeChain == GetInnerObject(scopeChain));
 
-    MOZ_RELEASE_ASSERT(scopeChain->is<GlobalObject>() || script->hasNonSyntacticScope(),
-                       "Only scripts with non-syntactic scopes can be executed with "
+    MOZ_RELEASE_ASSERT(scopeChain->is<GlobalObject>() || script->hasPollutedGlobalScope(),
+                       "Only scripts with polluted scopes can be executed with "
                        "interesting scopechains");
 
     /* Ensure the scope chain is all same-compartment and terminates in a global. */
 #ifdef DEBUG
     JSObject* s = scopeChain;
     do {
         assertSameCompartment(cx, s);
         MOZ_ASSERT_IF(!s->enclosingScope(), s->is<GlobalObject>());
@@ -1177,17 +1177,16 @@ PopScope(JSContext* cx, ScopeIter& si)
         if (si.staticBlock().needsClone())
             si.initialFrame().popBlock(cx);
         break;
       case ScopeIter::With:
         si.initialFrame().popWith(cx);
         break;
       case ScopeIter::Call:
       case ScopeIter::Eval:
-      case ScopeIter::NonSyntactic:
         break;
     }
 }
 
 // Unwind scope chain and iterator to match the static scope corresponding to
 // the given bytecode position.
 void
 js::UnwindScope(JSContext* cx, ScopeIter& si, jsbytecode* pc)
@@ -2316,17 +2315,17 @@ END_CASE(JSOP_SETCONST)
 CASE(JSOP_BINDINTRINSIC)
     PUSH_OBJECT(*cx->global()->intrinsicsHolder());
 END_CASE(JSOP_BINDINTRINSIC)
 
 CASE(JSOP_BINDGNAME)
 CASE(JSOP_BINDNAME)
 {
     JSOp op = JSOp(*REGS.pc);
-    if (op == JSOP_BINDNAME || script->hasNonSyntacticScope()) {
+    if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) {
         ReservedRooted<JSObject*> scopeChain(&rootObject0, REGS.fp()->scopeChain());
         ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
 
         /* Assigning to an undeclared name adds a property to the global object. */
         ReservedRooted<JSObject*> scope(&rootObject1);
         if (!LookupNameUnqualified(cx, name, scopeChain, &scope))
             goto error;
 
@@ -3058,17 +3057,17 @@ CASE(JSOP_THROWMSG)
     goto error;
 }
 END_CASE(JSOP_THROWMSG)
 
 CASE(JSOP_IMPLICITTHIS)
 CASE(JSOP_GIMPLICITTHIS)
 {
     JSOp op = JSOp(*REGS.pc);
-    if (op == JSOP_IMPLICITTHIS || script->hasNonSyntacticScope()) {
+    if (op == JSOP_IMPLICITTHIS || script->hasPollutedGlobalScope()) {
         ReservedRooted<PropertyName*> name(&rootName0, script->getName(REGS.pc));
         ReservedRooted<JSObject*> scopeObj(&rootObject0, REGS.fp()->scopeChain());
         ReservedRooted<JSObject*> scope(&rootObject1);
         if (!LookupNameWithGlobalDefault(cx, name, scopeObj, &scope))
             goto error;
 
         ReservedRooted<Value> v(&rootValue0);
         if (!ComputeImplicitThis(cx, scope, &v))
@@ -3937,17 +3936,17 @@ CASE(JSOP_INITHOMEOBJECT)
     func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj));
 }
 END_CASE(JSOP_INITHOMEOBJECT)
 
 CASE(JSOP_SUPERBASE)
 {
     ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc));
     for (; !si.done(); ++si) {
-        if (si.hasSyntacticScopeObject() && si.type() == ScopeIter::Call) {
+        if (si.hasScopeObject() && si.type() == ScopeIter::Call) {
             JSFunction& callee = si.scope().as<CallObject>().callee();
 
             // Arrow functions don't have the information we're looking for,
             // their enclosing scopes do. Nevertheless, they might have call
             // objects. Skip them to find what we came for.
             if (callee.isArrow())
                 continue;
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1565,47 +1565,47 @@ 1234567890123456789012345678901234567890
      *   Category: Statements
      *   Type: Function
      *   Operands:
      *   Stack: =>
      */ \
     macro(JSOP_RETRVAL,       153,"retrval",    NULL,       1,  0,  0,  JOF_BYTE) \
     \
     /*
-     * Looks up name on global scope and pushes its value onto the stack,
-     * unless the script has a non-syntactic global scope, in which case it
-     * acts just like JSOP_NAME.
+     * Looks up name on global scope and pushes its value onto the stack, unless
+     * the script has a polluted global, in which case it acts just like
+     * JSOP_NAME.
      *
      * Free variable references that must either be found on the global or a
      * ReferenceError.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => val
      */ \
     macro(JSOP_GETGNAME,      154,"getgname",  NULL,       5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME) \
     /*
      * Pops the top two values on the stack as 'val' and 'scope', sets property
      * of 'scope' as 'val' and pushes 'val' back on the stack.
      *
-     * 'scope' should be the global scope unless the script has a non-syntactic
+     * 'scope' should be the global scope unless the script has a polluted
      * global scope, in which case acts like JSOP_SETNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
     macro(JSOP_SETGNAME,      155,"setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSLOPPY) \
     \
     /*
      * Pops the top two values on the stack as 'val' and 'scope', sets property
      * of 'scope' as 'val' and pushes 'val' back on the stack. Throws a
      * TypeError if the set fails, per strict mode semantics.
      *
-     * 'scope' should be the global scope unless the script has a non-syntactic
+     * 'scope' should be the global scope unless the script has a polluted
      * global scope, in which case acts like JSOP_STRICTSETNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
     macro(JSOP_STRICTSETGNAME, 156, "strict-setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSTRICT) \
     /*
@@ -1866,17 +1866,17 @@ 1234567890123456789012345678901234567890
     macro(JSOP_DEBUGAFTERYIELD,  208, "debugafteryield",  NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED209,     209, "unused209",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED210,     210, "unused210",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED211,     211, "unused211",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED212,     212, "unused212",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED213,     213, "unused213",    NULL,  1,  0,  0,  JOF_BYTE) \
     /*
      * Pushes the global scope onto the stack if the script doesn't have a
-     * non-syntactic global scope.  Otherwise will act like JSOP_BINDNAME.
+     * polluted global scope.  Otherwise will act like JSOP_BINDNAME.
      *
      * 'nameIndex' is only used when acting like JSOP_BINDNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => global
      */ \
     macro(JSOP_BINDGNAME,     214, "bindgname",    NULL,  5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -79,52 +79,45 @@ StaticScopeIter<allowGC>::done() const
 template <AllowGC allowGC>
 inline void
 StaticScopeIter<allowGC>::operator++(int)
 {
     if (obj->template is<NestedScopeObject>()) {
         obj = obj->template as<NestedScopeObject>().enclosingScopeForStaticScopeIter();
     } else if (obj->template is<StaticEvalObject>()) {
         obj = obj->template as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
-    } else if (obj->template is<StaticNonSyntacticScopeObjects>()) {
-        obj = obj->template as<StaticNonSyntacticScopeObjects>().enclosingScopeForStaticScopeIter();
     } else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
         onNamedLambda = false;
         obj = obj->template as<JSFunction>().nonLazyScript()->enclosingStaticScope();
     } else {
         onNamedLambda = true;
     }
     MOZ_ASSERT_IF(obj, obj->template is<NestedScopeObject>() ||
                        obj->template is<StaticEvalObject>() ||
-                       obj->template is<StaticNonSyntacticScopeObjects>() ||
                        obj->template is<JSFunction>());
     MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>());
 }
 
 template <AllowGC allowGC>
 inline bool
-StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
+StaticScopeIter<allowGC>::hasDynamicScopeObject() const
 {
-    if (obj->template is<JSFunction>())
-        return obj->template as<JSFunction>().isHeavyweight();
-    if (obj->template is<StaticBlockObject>())
-        return obj->template as<StaticBlockObject>().needsClone();
-    if (obj->template is<StaticWithObject>())
-        return true;
-    if (obj->template is<StaticEvalObject>())
-        return obj->template as<StaticEvalObject>().isStrict();
-    MOZ_ASSERT(obj->template is<StaticNonSyntacticScopeObjects>());
-    return false;
+    return obj->template is<StaticBlockObject>()
+           ? obj->template as<StaticBlockObject>().needsClone()
+           : (obj->template is<StaticEvalObject>()
+              ? obj->template as<StaticEvalObject>().isStrict()
+              : (obj->template is<StaticWithObject>() ||
+                 obj->template as<JSFunction>().isHeavyweight()));
 }
 
 template <AllowGC allowGC>
 inline Shape*
 StaticScopeIter<allowGC>::scopeShape() const
 {
-    MOZ_ASSERT(hasSyntacticDynamicScopeObject());
+    MOZ_ASSERT(hasDynamicScopeObject());
     MOZ_ASSERT(type() != NamedLambda && type() != Eval);
     if (type() == Block)
         return block().lastProperty();
     return funScript()->callObjShape();
 }
 
 template <AllowGC allowGC>
 inline typename StaticScopeIter<allowGC>::Type
@@ -133,18 +126,16 @@ StaticScopeIter<allowGC>::type() const
     if (onNamedLambda)
         return NamedLambda;
     return obj->template is<StaticBlockObject>()
            ? Block
            : (obj->template is<StaticWithObject>()
               ? With
               : (obj->template is<StaticEvalObject>()
                  ? Eval
-                 : (obj->template is<StaticNonSyntacticScopeObjects>())
-                 ? NonSyntactic
                  : Function));
 }
 
 template <AllowGC allowGC>
 inline StaticBlockObject&
 StaticScopeIter<allowGC>::block() const
 {
     MOZ_ASSERT(type() == Block);
@@ -163,24 +154,16 @@ template <AllowGC allowGC>
 inline StaticEvalObject&
 StaticScopeIter<allowGC>::eval() const
 {
     MOZ_ASSERT(type() == Eval);
     return obj->template as<StaticEvalObject>();
 }
 
 template <AllowGC allowGC>
-inline StaticNonSyntacticScopeObjects&
-StaticScopeIter<allowGC>::nonSyntactic() const
-{
-    MOZ_ASSERT(type() == NonSyntactic);
-    return obj->template as<StaticNonSyntacticScopeObjects>();
-}
-
-template <AllowGC allowGC>
 inline JSScript*
 StaticScopeIter<allowGC>::funScript() const
 {
     MOZ_ASSERT(type() == Function);
     return obj->template as<JSFunction>().nonLazyScript();
 }
 
 template <AllowGC allowGC>
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -38,17 +38,17 @@ typedef MutableHandle<ArgumentsObject*> 
 Shape*
 js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc)
 {
     MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
     StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
     uint32_t hops = ScopeCoordinate(pc).hops();
     while (true) {
         MOZ_ASSERT(!ssi.done());
-        if (ssi.hasSyntacticDynamicScopeObject()) {
+        if (ssi.hasDynamicScopeObject()) {
             if (!hops)
                 break;
             hops--;
         }
         ssi++;
     }
     return ssi.scopeShape();
 }
@@ -102,17 +102,17 @@ js::ScopeCoordinateName(ScopeCoordinateN
 
 JSScript*
 js::ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc)
 {
     MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
     StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
     uint32_t hops = ScopeCoordinate(pc).hops();
     while (true) {
-        if (ssi.hasSyntacticDynamicScopeObject()) {
+        if (ssi.hasDynamicScopeObject()) {
             if (!hops)
                 break;
             hops--;
         }
         ssi++;
     }
     if (ssi.type() != StaticScopeIter<NoGC>::Function)
         return nullptr;
@@ -207,17 +207,17 @@ CallObject::createTemplateObject(JSConte
 CallObject*
 CallObject::create(JSContext* cx, HandleScript script, HandleObject enclosing, HandleFunction callee)
 {
     gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
     CallObject* callobj = CallObject::createTemplateObject(cx, script, heap);
     if (!callobj)
         return nullptr;
 
-    callobj->setEnclosingScope(enclosing);
+    callobj->as<ScopeObject>().setEnclosingScope(enclosing);
     callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
 
     if (script->treatAsRunOnce()) {
         Rooted<CallObject*> ncallobj(cx, callobj);
         if (!JSObject::setSingleton(cx, ncallobj))
             return nullptr;
         return ncallobj;
     }
@@ -318,20 +318,32 @@ const Class DeclEnvObject::class_ = {
 };
 
 /*
  * Create a DeclEnvObject for a JSScript that is not initialized to any
  * particular callsite. This object can either be initialized (with an enclosing
  * scope and callee) or used as a template for jit compilation.
  */
 DeclEnvObject*
-DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, NewObjectKind newKind)
+DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, gc::InitialHeap heap)
 {
-    Rooted<DeclEnvObject*> obj(cx);
-    obj = NewObjectWithNullTaggedProto<DeclEnvObject>(cx, newKind, BaseShape::DELEGATE);
+    MOZ_ASSERT(IsNurseryAllocable(FINALIZE_KIND));
+
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
+    if (!group)
+        return nullptr;
+
+    RootedShape emptyDeclEnvShape(cx);
+    emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
+                                                    FINALIZE_KIND, BaseShape::DELEGATE);
+    if (!emptyDeclEnvShape)
+        return nullptr;
+
+    RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, heap,
+                                                                  emptyDeclEnvShape, group)));
     if (!obj)
         return nullptr;
 
     // Assign a fixed slot to a property with the same name as the lambda.
     Rooted<jsid> id(cx, AtomToId(fun->atom()));
     const Class* clasp = obj->getClass();
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
 
@@ -339,23 +351,23 @@ DeclEnvObject::createTemplateObject(JSCo
     JSSetterOp setter = clasp->setProperty;
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
 
     if (!NativeObject::putProperty(cx, obj, id, getter, setter, lambdaSlot(), attrs, 0))
         return nullptr;
 
     MOZ_ASSERT(!obj->hasDynamicSlots());
-    return obj;
+    return &obj->as<DeclEnvObject>();
 }
 
 DeclEnvObject*
 DeclEnvObject::create(JSContext* cx, HandleObject enclosing, HandleFunction callee)
 {
-    Rooted<DeclEnvObject*> obj(cx, createTemplateObject(cx, callee, GenericObject));
+    Rooted<DeclEnvObject*> obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap));
     if (!obj)
         return nullptr;
 
     obj->setEnclosingScope(enclosing);
     obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
     return obj;
 }
 
@@ -383,17 +395,30 @@ template bool
 js::XDRStaticWithObject(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticWithObject*>);
 
 template bool
 js::XDRStaticWithObject(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticWithObject*>);
 
 StaticWithObject*
 StaticWithObject::create(ExclusiveContext* cx)
 {
-    return NewObjectWithNullTaggedProto<StaticWithObject>(cx, TenuredObject, BaseShape::DELEGATE);
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
+    if (!group)
+        return nullptr;
+
+    RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
+                                                      FINALIZE_KIND));
+    if (!shape)
+        return nullptr;
+
+    RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group));
+    if (!obj)
+        return nullptr;
+
+    return &obj->as<StaticWithObject>();
 }
 
 static JSObject*
 CloneStaticWithObject(JSContext* cx, HandleObject enclosingScope, Handle<StaticWithObject*> srcWith)
 {
     Rooted<StaticWithObject*> clone(cx, StaticWithObject::create(cx));
     if (!clone)
         return nullptr;
@@ -403,34 +428,41 @@ CloneStaticWithObject(JSContext* cx, Han
     return clone;
 }
 
 DynamicWithObject*
 DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
                           HandleObject staticWith, WithKind kind)
 {
     MOZ_ASSERT(staticWith->is<StaticWithObject>());
-
-    Rooted<TaggedProto> proto(cx, TaggedProto(staticWith));
-    Rooted<DynamicWithObject*> obj(cx);
-    obj = NewObjectWithGivenTaggedProto<DynamicWithObject>(cx, proto, GenericObject,
-                                                           BaseShape::DELEGATE);
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_,
+                                                             TaggedProto(staticWith.get())));
+    if (!group)
+        return nullptr;
+
+    RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith),
+                                                      FINALIZE_KIND));
+    if (!shape)
+        return nullptr;
+
+    RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
+                                                                  gc::DefaultHeap, shape, group)));
     if (!obj)
         return nullptr;
 
     JSObject* thisp = GetThisObject(cx, object);
     if (!thisp)
         return nullptr;
 
-    obj->setEnclosingScope(enclosing);
+    obj->as<ScopeObject>().setEnclosingScope(enclosing);
     obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
     obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
     obj->setFixedSlot(KIND_SLOT, Int32Value(kind));
 
-    return obj;
+    return &obj->as<DynamicWithObject>();
 }
 
 static bool
 with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
                     MutableHandleObject objp, MutableHandleShape propp)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return LookupProperty(cx, actual, id, objp, propp);
@@ -530,94 +562,56 @@ const Class DynamicWithObject::class_ = 
         nullptr,             /* enumerate (native enumeration of target doesn't work) */
         with_ThisObject,
     }
 };
 
 /* static */ StaticEvalObject*
 StaticEvalObject::create(JSContext* cx, HandleObject enclosing)
 {
-    StaticEvalObject* obj =
-        NewObjectWithNullTaggedProto<StaticEvalObject>(cx, TenuredObject, BaseShape::DELEGATE);
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
+    if (!group)
+        return nullptr;
+
+    RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
+                                                      FINALIZE_KIND, BaseShape::DELEGATE));
+    if (!shape)
+        return nullptr;
+
+    RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
+                                                                  gc::TenuredHeap, shape, group)));
     if (!obj)
         return nullptr;
 
     obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing));
     obj->setReservedSlot(STRICT_SLOT, BooleanValue(false));
-    return obj;
+    return &obj->as<StaticEvalObject>();
 }
 
 const Class StaticEvalObject::class_ = {
     "StaticEval",
     JSCLASS_HAS_RESERVED_SLOTS(StaticEvalObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS
 };
 
-/* static */ StaticNonSyntacticScopeObjects*
-StaticNonSyntacticScopeObjects::create(JSContext*cx, HandleObject enclosing)
-{
-    StaticNonSyntacticScopeObjects* obj =
-        NewObjectWithNullTaggedProto<StaticNonSyntacticScopeObjects>(cx, TenuredObject,
-                                                                     BaseShape::DELEGATE);
-    if (!obj)
-        return nullptr;
-
-    obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing));
-    return obj;
-}
-
-const Class StaticNonSyntacticScopeObjects::class_ = {
-    "StaticNonSyntacticScopeObjects",
-    JSCLASS_HAS_RESERVED_SLOTS(StaticNonSyntacticScopeObjects::RESERVED_SLOTS) |
-    JSCLASS_IS_ANONYMOUS
-};
-
-/* static */ NonSyntacticVariablesObject*
-NonSyntacticVariablesObject::create(JSContext* cx, Handle<GlobalObject*> global)
-{
-    Rooted<NonSyntacticVariablesObject*> obj(cx,
-        NewObjectWithNullTaggedProto<NonSyntacticVariablesObject>(cx, TenuredObject,
-                                                                  BaseShape::DELEGATE));
-    if (!obj)
-        return nullptr;
-
-    if (!obj->setQualifiedVarObj(cx))
-        return nullptr;
-
-    if (!obj->setUnqualifiedVarObj(cx))
-        return nullptr;
-
-    obj->setEnclosingScope(global);
-    return obj;
-}
-
-const Class NonSyntacticVariablesObject::class_ = {
-    "NonSyntacticVariablesObject",
-    JSCLASS_HAS_RESERVED_SLOTS(NonSyntacticVariablesObject::RESERVED_SLOTS) |
-    JSCLASS_IS_ANONYMOUS
-};
-
 /*****************************************************************************/
 
 /* static */ ClonedBlockObject*
 ClonedBlockObject::create(JSContext* cx, Handle<StaticBlockObject*> block, HandleObject enclosing)
 {
     MOZ_ASSERT(block->getClass() == &BlockObject::class_);
 
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_,
                                                              TaggedProto(block.get())));
     if (!group)
         return nullptr;
 
     RootedShape shape(cx, block->lastProperty());
 
-    gc::AllocKind allocKind = gc::GetGCObjectKind(&BlockObject::class_);
-    if (CanBeFinalizedInBackground(allocKind, &BlockObject::class_))
-        allocKind = GetBackgroundAllocKind(allocKind);
-    RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, allocKind,
+    RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
                                                                   gc::TenuredHeap, shape, group)));
     if (!obj)
         return nullptr;
 
     MOZ_ASSERT(!obj->inDictionaryMode());
     MOZ_ASSERT(obj->slotSpan() >= block->numVariables() + RESERVED_SLOTS);
 
     obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*enclosing));
@@ -681,17 +675,32 @@ ClonedBlockObject::clone(JSContext* cx, 
         copy->setVar(i, clonedBlock->var(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING);
 
     return copy;
 }
 
 StaticBlockObject*
 StaticBlockObject::create(ExclusiveContext* cx)
 {
-    return NewObjectWithNullTaggedProto<StaticBlockObject>(cx, TenuredObject, BaseShape::DELEGATE);
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_,
+                                                             TaggedProto(nullptr)));
+    if (!group)
+        return nullptr;
+
+    RootedShape emptyBlockShape(cx);
+    emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, TaggedProto(nullptr),
+                                                  FINALIZE_KIND, BaseShape::DELEGATE);
+    if (!emptyBlockShape)
+        return nullptr;
+
+    JSObject* obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, group);
+    if (!obj)
+        return nullptr;
+
+    return &obj->as<StaticBlockObject>();
 }
 
 /* static */ Shape*
 StaticBlockObject::addVar(ExclusiveContext* cx, Handle<StaticBlockObject*> block, HandleId id,
                           bool constant, unsigned index, bool* redeclared)
 {
     MOZ_ASSERT(JSID_IS_ATOM(id));
     MOZ_ASSERT(index < LOCAL_INDEX_LIMIT);
@@ -879,23 +888,32 @@ js::CloneNestedScopeObject(JSContext* cx
         Rooted<StaticWithObject*> withObj(cx, &srcBlock->as<StaticWithObject>());
         return CloneStaticWithObject(cx, enclosingScope, withObj);
     }
 }
 
 /* static */ UninitializedLexicalObject*
 UninitializedLexicalObject::create(JSContext* cx, HandleObject enclosing)
 {
-    UninitializedLexicalObject* obj =
-        NewObjectWithNullTaggedProto<UninitializedLexicalObject>(cx, GenericObject,
-                                                                 BaseShape::DELEGATE);
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
+    if (!group)
+        return nullptr;
+
+    RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
+                                                      FINALIZE_KIND));
+    if (!shape)
+        return nullptr;
+
+    RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group));
     if (!obj)
         return nullptr;
-    obj->setEnclosingScope(enclosing);
-    return obj;
+
+    obj->as<ScopeObject>().setEnclosingScope(enclosing);
+
+    return &obj->as<UninitializedLexicalObject>();
 }
 
 static void
 ReportUninitializedLexicalId(JSContext* cx, HandleId id)
 {
     if (JSID_IS_ATOM(id)) {
         RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
         ReportUninitializedLexical(cx, name);
@@ -1022,24 +1040,17 @@ ScopeIter::ScopeIter(JSContext* cx, Abst
     assertSameCompartment(cx, frame);
     settle();
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 void
 ScopeIter::incrementStaticScopeIter()
 {
-    // If settled on a non-syntactic static scope, only increment ssi_ once
-    // we've iterated through all the non-syntactic dynamic ScopeObjects.
-    if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
-        if (!hasNonSyntacticScopeObject())
-            ssi_++;
-    } else {
-        ssi_++;
-    }
+    ssi_++;
 
     // For named lambdas, DeclEnvObject scopes are always attached to their
     // CallObjects. Skip it here, as they are special cased in users of
     // ScopeIter.
     if (!ssi_.done() && ssi_.type() == StaticScopeIter<CanGC>::NamedLambda)
         ssi_++;
 }
 
@@ -1056,44 +1067,41 @@ ScopeIter::settle()
     }
 
     // Check if we have left the extent of the initial frame after we've
     // settled on a static scope.
     if (frame_ && (ssi_.done() || maybeStaticScope() == frame_.script()->enclosingStaticScope()))
         frame_ = NullFramePtr();
 
 #ifdef DEBUG
-    if (!ssi_.done() && hasAnyScopeObject()) {
+    if (!ssi_.done() && hasScopeObject()) {
         switch (ssi_.type()) {
           case StaticScopeIter<CanGC>::Function:
             MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript());
             break;
           case StaticScopeIter<CanGC>::Block:
             MOZ_ASSERT(scope_->as<ClonedBlockObject>().staticBlock() == staticBlock());
             break;
           case StaticScopeIter<CanGC>::With:
             MOZ_ASSERT(scope_->as<DynamicWithObject>().staticScope() == &staticWith());
             break;
           case StaticScopeIter<CanGC>::Eval:
             MOZ_ASSERT(scope_->as<CallObject>().isForEval());
             break;
-          case StaticScopeIter<CanGC>::NonSyntactic:
-            MOZ_ASSERT(!IsSyntacticScope(scope_));
-            break;
           case StaticScopeIter<CanGC>::NamedLambda:
             MOZ_CRASH("named lambda static scopes should have been skipped");
         }
     }
 #endif
 }
 
 ScopeIter&
 ScopeIter::operator++()
 {
-    if (hasAnyScopeObject()) {
+    if (hasScopeObject()) {
         scope_ = &scope_->as<ScopeObject>().enclosingScope();
         if (scope_->is<DeclEnvObject>())
             scope_ = &scope_->as<DeclEnvObject>().enclosingScope();
     }
 
     incrementStaticScopeIter();
     settle();
 
@@ -1109,29 +1117,27 @@ ScopeIter::type() const
       case StaticScopeIter<CanGC>::Function:
         return Call;
       case StaticScopeIter<CanGC>::Block:
         return Block;
       case StaticScopeIter<CanGC>::With:
         return With;
       case StaticScopeIter<CanGC>::Eval:
         return Eval;
-      case StaticScopeIter<CanGC>::NonSyntactic:
-        return NonSyntactic;
       case StaticScopeIter<CanGC>::NamedLambda:
         MOZ_CRASH("named lambda static scopes should have been skipped");
       default:
         MOZ_CRASH("bad SSI type");
     }
 }
 
 ScopeObject&
 ScopeIter::scope() const
 {
-    MOZ_ASSERT(hasAnyScopeObject());
+    MOZ_ASSERT(hasScopeObject());
     return scope_->as<ScopeObject>();
 }
 
 JSObject*
 ScopeIter::maybeStaticScope() const
 {
     if (ssi_.done())
         return nullptr;
@@ -1140,18 +1146,16 @@ ScopeIter::maybeStaticScope() const
       case StaticScopeIter<CanGC>::Function:
         return &fun();
       case StaticScopeIter<CanGC>::Block:
         return &staticBlock();
       case StaticScopeIter<CanGC>::With:
         return &staticWith();
       case StaticScopeIter<CanGC>::Eval:
         return &staticEval();
-      case StaticScopeIter<CanGC>::NonSyntactic:
-        return &staticNonSyntactic();
       case StaticScopeIter<CanGC>::NamedLambda:
         MOZ_CRASH("named lambda static scopes should have been skipped");
       default:
         MOZ_CRASH("bad SSI type");
     }
 }
 
 /* static */ HashNumber
@@ -1741,17 +1745,17 @@ class DebugScopeProxy : public BaseProxy
 
 const char DebugScopeProxy::family = 0;
 const DebugScopeProxy DebugScopeProxy::singleton;
 
 /* static */ DebugScopeObject*
 DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing)
 {
     MOZ_ASSERT(scope.compartment() == cx->compartment());
-    MOZ_ASSERT(!enclosing->is<ScopeObject>());
+    MOZ_ASSERT(!IsSyntacticScope(enclosing));
 
     RootedValue priv(cx, ObjectValue(scope));
     JSObject* obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
                                    nullptr /* proto */);
     if (!obj)
         return nullptr;
 
     DebugScopeObject* debugScope = &obj->as<DebugScopeObject>();
@@ -2003,33 +2007,33 @@ DebugScopes::addDebugScope(JSContext* cx
         return false;
 
     return scopes->proxiedScopes.add(cx, &scope, &debugScope);
 }
 
 DebugScopeObject*
 DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si)
 {
-    MOZ_ASSERT(!si.hasSyntacticScopeObject());
+    MOZ_ASSERT(!si.hasScopeObject());
 
     DebugScopes* scopes = cx->compartment()->debugScopes;
     if (!scopes)
         return nullptr;
 
     if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) {
         MOZ_ASSERT(CanUseDebugScopeMaps(cx));
         return p->value();
     }
     return nullptr;
 }
 
 bool
 DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope)
 {
-    MOZ_ASSERT(!si.hasSyntacticScopeObject());
+    MOZ_ASSERT(!si.hasScopeObject());
     MOZ_ASSERT(cx->compartment() == debugScope.compartment());
     MOZ_ASSERT_IF(si.withinInitialFrame() && si.initialFrame().isFunctionFrame(),
                   !si.initialFrame().callee()->isGenerator());
     // Generators should always reify their scopes.
     MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator());
 
     if (!CanUseDebugScopeMaps(cx))
         return true;
@@ -2237,17 +2241,17 @@ DebugScopes::updateLiveScopes(JSContext*
 
         if (frame.isFunctionFrame() && frame.callee()->isGenerator())
             continue;
 
         if (!frame.isDebuggee())
             continue;
 
         for (ScopeIter si(cx, frame, i.pc()); si.withinInitialFrame(); ++si) {
-            if (si.hasSyntacticScopeObject()) {
+            if (si.hasScopeObject()) {
                 MOZ_ASSERT(si.scope().compartment() == cx->compartment());
                 DebugScopes* scopes = ensureCompartmentData(cx);
                 if (!scopes)
                     return false;
                 if (!scopes->liveScopes.put(&si.scope(), LiveScopeVal(si)))
                     return false;
                 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope());
             }
@@ -2355,17 +2359,17 @@ GetDebugScopeForScope(JSContext* cx, con
         return nullptr;
 
     return debugScope;
 }
 
 static DebugScopeObject*
 GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si)
 {
-    MOZ_ASSERT(!si.hasSyntacticScopeObject() && si.canHaveSyntacticScopeObject());
+    MOZ_ASSERT(!si.hasScopeObject() && si.canHaveScopeObject());
 
     if (DebugScopeObject* debugScope = DebugScopes::hasDebugScope(cx, si))
         return debugScope;
 
     ScopeIter copy(cx, si);
     RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy));
     if (!enclosingDebug)
         return nullptr;
@@ -2422,53 +2426,51 @@ GetDebugScopeForMissing(JSContext* cx, c
             return nullptr;
 
         debugScope = DebugScopeObject::create(cx, *block, enclosingDebug);
         break;
       }
       case ScopeIter::With:
       case ScopeIter::Eval:
         MOZ_CRASH("should already have a scope");
-      case ScopeIter::NonSyntactic:
-        MOZ_CRASH("non-syntactic scopes cannot be synthesized");
     }
     if (!debugScope)
         return nullptr;
 
     if (!DebugScopes::addDebugScope(cx, si, *debugScope))
         return nullptr;
 
     return debugScope;
 }
 
 static JSObject*
 GetDebugScopeForNonScopeObject(const ScopeIter& si)
 {
     JSObject& enclosing = si.enclosingScope();
-    MOZ_ASSERT(!enclosing.is<ScopeObject>());
+    MOZ_ASSERT(!IsSyntacticScope(&enclosing));
 #ifdef DEBUG
     JSObject* o = &enclosing;
     while ((o = o->enclosingScope()))
-        MOZ_ASSERT(!o->is<ScopeObject>());
+        MOZ_ASSERT(!IsSyntacticScope(o));
 #endif
     return &enclosing;
 }
 
 static JSObject*
 GetDebugScope(JSContext* cx, const ScopeIter& si)
 {
     JS_CHECK_RECURSION(cx, return nullptr);
 
     if (si.done())
         return GetDebugScopeForNonScopeObject(si);
 
-    if (si.hasAnyScopeObject())
+    if (si.hasScopeObject())
         return GetDebugScopeForScope(cx, si);
 
-    if (si.canHaveSyntacticScopeObject())
+    if (si.canHaveScopeObject())
         return GetDebugScopeForMissing(cx, si);
 
     ScopeIter copy(cx, si);
     return GetDebugScope(cx, ++copy);
 }
 
 JSObject*
 js::GetDebugScopeForFunction(JSContext* cx, HandleFunction fun)
@@ -2507,17 +2509,18 @@ js::GetObjectEnvironmentObjectForFunctio
         return &fun->global();
 
     return &env->as<DynamicWithObject>().object();
 }
 
 bool
 js::CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                     HandleObject dynamicTerminatingScope,
-                                    MutableHandleObject dynamicScopeObj)
+                                    MutableHandleObject dynamicScopeObj,
+                                    MutableHandleObject staticScopeObj)
 {
 #ifdef DEBUG
     for (size_t i = 0; i < scopeChain.length(); ++i) {
         assertSameCompartment(cx, scopeChain[i]);
         MOZ_ASSERT(!scopeChain[i]->is<GlobalObject>());
     }
 #endif
 
@@ -2537,65 +2540,22 @@ 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
 
-void
-js::DumpStaticScopeChain(JSScript* script)
-{
-    JSObject* enclosingScope = script->enclosingStaticScope();
-    for (StaticScopeIter<NoGC> ssi(enclosingScope); !ssi.done(); ssi++) {
-        switch (ssi.type()) {
-          case StaticScopeIter<NoGC>::Function:
-            fprintf(stdout, "function");
-            break;
-          case StaticScopeIter<NoGC>::Block:
-            fprintf(stdout, "block");
-            break;
-          case StaticScopeIter<NoGC>::With:
-            fprintf(stdout, "with");
-            break;
-          case StaticScopeIter<NoGC>::NamedLambda:
-            fprintf(stdout, "named lambda");
-            break;
-          case StaticScopeIter<NoGC>::Eval:
-            fprintf(stdout, "eval");
-            break;
-          case StaticScopeIter<NoGC>::NonSyntactic:
-            fprintf(stdout, "non-syntactic");
-            break;
-        }
-        fprintf(stdout, " -> ");
-    }
-    fprintf(stdout, "global\n");
-}
-
 typedef HashSet<PropertyName*> PropertyNameSet;
 
 static bool
 RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remainingNames)
 {
     // Remove from remainingNames --- the closure variables in some outer
     // script --- any free variables in this script. This analysis isn't perfect:
     //
@@ -2618,17 +2578,17 @@ RemoveReferencedNames(JSContext* cx, Han
           case JSOP_SETNAME:
           case JSOP_STRICTSETNAME:
             name = script->getName(pc);
             break;
 
           case JSOP_GETGNAME:
           case JSOP_SETGNAME:
           case JSOP_STRICTSETGNAME:
-            if (script->hasNonSyntacticScope())
+            if (script->hasPollutedGlobalScope())
                 name = script->getName(pc);
             else
                 name = nullptr;
             break;
 
           case JSOP_GETALIASEDVAR:
           case JSOP_SETALIASEDVAR:
             name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -17,17 +17,16 @@
 #include "vm/WeakMapObject.h"
 
 namespace js {
 
 namespace frontend { struct Definition; }
 
 class StaticWithObject;
 class StaticEvalObject;
-class StaticNonSyntacticScopeObjects;
 
 /*****************************************************************************/
 
 /*
  * All function scripts have an "enclosing static scope" that refers to the
  * innermost enclosing let or function in the program text. This allows full
  * reconstruction of the lexical scope for debugging or compiling efficient
  * access to variables in enclosing scopes. The static scope is represented at
@@ -58,17 +57,16 @@ class StaticScopeIter
     {
         static_assert(allowGC == CanGC,
                       "the context-accepting constructor should only be used "
                       "in CanGC code");
         MOZ_ASSERT_IF(obj,
                       obj->is<StaticBlockObject>() ||
                       obj->is<StaticWithObject>() ||
                       obj->is<StaticEvalObject>() ||
-                      obj->is<StaticNonSyntacticScopeObjects>() ||
                       obj->is<JSFunction>());
     }
 
     StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter<CanGC>& ssi)
       : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda)
     {
         JS_STATIC_ASSERT(allowGC == CanGC);
     }
@@ -78,44 +76,40 @@ class StaticScopeIter
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
         MOZ_ASSERT_IF(obj,
                       obj->is<StaticBlockObject>() ||
                       obj->is<StaticWithObject>() ||
                       obj->is<StaticEvalObject>() ||
-                      obj->is<StaticNonSyntacticScopeObjects>() ||
                       obj->is<JSFunction>());
     }
 
     explicit StaticScopeIter(const StaticScopeIter<NoGC>& ssi)
       : obj((ExclusiveContext*) nullptr, ssi.obj), onNamedLambda(ssi.onNamedLambda)
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
     }
 
     bool done() const;
     void operator++(int);
 
-    // Return whether this static scope will have a syntactic scope (i.e. a
-    // ScopeObject that isn't a non-syntactic With or
-    // NonSyntacticVariablesObject) on the dynamic scope chain.
-    bool hasSyntacticDynamicScopeObject() const;
+    /* Return whether this static scope will be on the dynamic scope chain. */
+    bool hasDynamicScopeObject() const;
     Shape* scopeShape() const;
 
-    enum Type { Function, Block, With, NamedLambda, Eval, NonSyntactic };
+    enum Type { Function, Block, With, NamedLambda, Eval };
     Type type() const;
 
     StaticBlockObject& block() const;
     StaticWithObject& staticWith() const;
     StaticEvalObject& eval() const;
-    StaticNonSyntacticScopeObjects& nonSyntactic() const;
     JSScript* funScript() const;
     JSFunction& fun() const;
 };
 
 /*****************************************************************************/
 
 /*
  * A "scope coordinate" describes how to get from head of the scope chain to a
@@ -176,52 +170,47 @@ ScopeCoordinateFunctionScript(JSScript* 
 /*
  * Scope objects
  *
  * Scope objects are technically real JSObjects but only belong on the scope
  * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of
  * scope objects is:
  *
  *   JSObject                      Generic object
- *     |
- *   ScopeObject---+---+           Engine-internal scope
- *     |   |   |   |   |
- *     |   |   |   |  StaticNonSyntacticScopeObjects  See NB2
- *     |   |   |   |
- *     |   |   |  StaticEvalObject  Placeholder so eval scopes may be iterated through
- *     |   |   |
- *     |   |  DeclEnvObject         Holds name of recursive/heavyweight named lambda
- *     |   |
- *     |  CallObject                Scope of entire function or strict eval
- *     |
- *   NestedScopeObject              Scope created for a statement
- *     |   |   |
- *     |   |  StaticWithObject      Template for "with" object in static scope chain
- *     |   |
- *     |  DynamicWithObject         Run-time "with" object on scope chain
- *     |
- *   BlockObject                    Shared interface of cloned/static block objects
- *     |   |
- *     |  ClonedBlockObject         let, switch, catch, for
- *     |
- *   StaticBlockObject              See NB
+ *     \
+ *   ScopeObject                   Engine-internal scope
+ *   \   \   \   \
+ *    \   \   \  StaticEvalObject  Placeholder so eval scopes may be iterated through
+ *     \   \   \
+ *      \   \  DeclEnvObject       Holds name of recursive/heavyweight named lambda
+ *       \   \
+ *        \  CallObject            Scope of entire function or strict eval
+ *         \
+ *   NestedScopeObject             Scope created for a statement
+ *     \   \  \
+ *      \   \ StaticWithObject     Template for "with" object in static scope chain
+ *       \   \
+ *        \  DynamicWithObject     Run-time "with" object on scope chain
+ *         \
+ *   BlockObject                   Shared interface of cloned/static block objects
+ *     \   \
+ *      \  ClonedBlockObject       let, switch, catch, for
+ *       \
+ *       StaticBlockObject         See NB
  *
  * This hierarchy represents more than just the interface hierarchy: reserved
  * slots in base classes are fixed for all derived classes. Thus, for example,
  * ScopeObject::enclosingScope() can simply access a fixed slot without further
  * dynamic type information.
  *
  * NB: Static block objects are a special case: these objects are created at
  * compile time to hold the shape/binding information from which block objects
  * are cloned at runtime. These objects should never escape into the wild and
  * support a restricted set of ScopeObject operations.
  *
- * NB2: StaticNonSyntacticScopeObjects notify either of 0+ non-syntactic
- * DynamicWithObjects on the dynamic scope chain or a NonSyntacticScopeObject.
- *
  * See also "Debug scope objects" below.
  */
 
 class ScopeObject : public NativeObject
 {
   protected:
     static const uint32_t SCOPE_CHAIN_SLOT = 0;
 
@@ -346,37 +335,40 @@ class CallObject : public ScopeObject
 
 class DeclEnvObject : public ScopeObject
 {
     // Pre-allocated slot for the named lambda.
     static const uint32_t LAMBDA_SLOT = 1;
 
   public:
     static const uint32_t RESERVED_SLOTS = 2;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
+
     static const Class class_;
 
     static DeclEnvObject*
-    createTemplateObject(JSContext* cx, HandleFunction fun, NewObjectKind newKind);
+    createTemplateObject(JSContext* cx, HandleFunction fun, gc::InitialHeap heap);
 
     static DeclEnvObject* create(JSContext* cx, HandleObject enclosing, HandleFunction callee);
 
     static inline size_t lambdaSlot() {
         return LAMBDA_SLOT;
     }
 };
 
-// Static eval scope placeholder objects on the static scope chain. Created at
-// the time of compiling the eval script, and set as its static enclosing
-// scope.
+// Static eval scope template objects on the static scope. Created at the
+// time of compiling the eval script, and set as its static enclosing scope.
 class StaticEvalObject : public ScopeObject
 {
     static const uint32_t STRICT_SLOT = 1;
 
   public:
     static const unsigned RESERVED_SLOTS = 2;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
+
     static const Class class_;
 
     static StaticEvalObject* create(JSContext* cx, HandleObject enclosing);
 
     JSObject* enclosingScopeForStaticScopeIter() {
         return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
     }
 
@@ -390,52 +382,16 @@ class StaticEvalObject : public ScopeObj
 
     // Indirect evals terminate in the global at run time, and has no static
     // enclosing scope.
     bool isDirect() const {
         return getReservedSlot(SCOPE_CHAIN_SLOT).isObject();
     }
 };
 
-// Static scope objects that stand in for one or more "polluting global"
-// scopes on the dynamic scope chain.
-//
-// There are two flavors of polluting global scopes on the dynamic scope
-// chain: either 0+ non-syntactic DynamicWithObjects, or 1
-// NonSyntacticVariablesObject, created exclusively in
-// js::ExecuteInGlobalAndReturnScope.
-class StaticNonSyntacticScopeObjects : public ScopeObject
-{
-  public:
-    static const unsigned RESERVED_SLOTS = 1;
-    static const Class class_;
-
-    static StaticNonSyntacticScopeObjects* create(JSContext* cx, HandleObject enclosing);
-
-    JSObject* enclosingScopeForStaticScopeIter() {
-        return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
-    }
-};
-
-// A non-syntactic dynamic scope object that captures non-lexical
-// bindings. That is, a scope object that captures both qualified var
-// assignments and unqualified bareword assignments. Its parent is always the
-// real global.
-//
-// This is used in ExecuteInGlobalAndReturnScope and sits in front of the
-// global scope to capture 'var' and bareword asignments.
-class NonSyntacticVariablesObject : public ScopeObject
-{
-  public:
-    static const unsigned RESERVED_SLOTS = 1;
-    static const Class class_;
-
-    static NonSyntacticVariablesObject* create(JSContext* cx, Handle<GlobalObject*> global);
-};
-
 class NestedScopeObject : public ScopeObject
 {
   public:
     /*
      * A refinement of enclosingScope that returns nullptr if the enclosing
      * scope is not a NestedScopeObject.
      */
     inline NestedScopeObject* enclosingNestedScope() const;
@@ -475,30 +431,34 @@ class NestedScopeObject : public ScopeOb
     }
 };
 
 // With scope template objects on the static scope chain.
 class StaticWithObject : public NestedScopeObject
 {
   public:
     static const unsigned RESERVED_SLOTS = 1;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
+
     static const Class class_;
 
     static StaticWithObject* create(ExclusiveContext* cx);
 };
 
 // With scope objects on the run-time scope chain.
 class DynamicWithObject : public NestedScopeObject
 {
     static const unsigned OBJECT_SLOT = 1;
     static const unsigned THIS_SLOT = 2;
     static const unsigned KIND_SLOT = 3;
 
   public:
     static const unsigned RESERVED_SLOTS = 4;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
+
     static const Class class_;
 
     enum WithKind {
         SyntacticWith,
         NonSyntacticWith
     };
 
     static DynamicWithObject*
@@ -540,16 +500,18 @@ class DynamicWithObject : public NestedS
 
 class BlockObject : public NestedScopeObject
 {
   protected:
     static const unsigned DEPTH_SLOT = 1;
 
   public:
     static const unsigned RESERVED_SLOTS = 2;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT4_BACKGROUND;
+
     static const Class class_;
 
     /* Return the abstract stack depth right before entering this nested scope. */
     uint32_t stackDepth() const {
         return getReservedSlot(DEPTH_SLOT).toPrivateUint32();
     }
 
     /* Return the number of variables associated with this block. */
@@ -626,17 +588,17 @@ class StaticBlockObject : public BlockOb
         return slotValue(i).isTrue();
     }
 
     /*
      * A static block object is cloned (when entering the block) iff some
      * variable of the block isAliased.
      */
     bool needsClone() {
-        return numVariables() > 0 && !getSlot(RESERVED_SLOTS).isFalse();
+        return !getFixedSlot(RESERVED_SLOTS).isFalse();
     }
 
     /* Frontend-only functions ***********************************************/
 
     /* Initialization functions for above fields. */
     void setAliased(unsigned i, bool aliased) {
         MOZ_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
         setSlotValue(i, BooleanValue(aliased));
@@ -730,16 +692,18 @@ class ClonedBlockObject : public BlockOb
 // demands that the error be thrown after evaluating the RHS of
 // assignments. Instead, this sentinel scope object is pushed on the stack.
 // Attempting to access anything on this scope throws the appropriate
 // ReferenceError.
 class UninitializedLexicalObject : public ScopeObject
 {
   public:
     static const unsigned RESERVED_SLOTS = 1;
+    static const gc::AllocKind FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
+
     static const Class class_;
 
     static UninitializedLexicalObject* create(JSContext* cx, HandleObject enclosing);
 };
 
 template<XDRMode mode>
 bool
 XDRStaticBlockObject(XDRState<mode>* xdr, HandleObject enclosingScope,
@@ -790,30 +754,27 @@ class ScopeIter
 
     inline bool done() const;
     ScopeIter& operator++();
 
     // If done():
     inline JSObject& enclosingScope() const;
 
     // If !done():
-    enum Type { Call, Block, With, Eval, NonSyntactic };
+    enum Type { Call, Block, With, Eval };
     Type type() const;
 
-    inline bool hasNonSyntacticScopeObject() const;
-    inline bool hasSyntacticScopeObject() const;
-    inline bool hasAnyScopeObject() const;
-    inline bool canHaveSyntacticScopeObject() const;
+    inline bool hasScopeObject() const;
+    inline bool canHaveScopeObject() const;
     ScopeObject& scope() const;
 
     JSObject* maybeStaticScope() const;
     StaticBlockObject& staticBlock() const { return ssi_.block(); }
     StaticWithObject& staticWith() const { return ssi_.staticWith(); }
     StaticEvalObject& staticEval() const { return ssi_.eval(); }
-    StaticNonSyntacticScopeObjects& staticNonSyntactic() const { return ssi_.nonSyntactic(); }
     JSFunction& fun() const { return ssi_.fun(); }
 
     bool withinInitialFrame() const { return !!frame_; }
     AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; }
     AbstractFramePtr maybeInitialFrame() const { return frame_; }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
@@ -1043,18 +1004,17 @@ JSObject::is<js::NestedScopeObject>() co
 
 template<>
 inline bool
 JSObject::is<js::ScopeObject>() const
 {
     return is<js::CallObject>() ||
            is<js::DeclEnvObject>() ||
            is<js::NestedScopeObject>() ||
-           is<js::UninitializedLexicalObject>() ||
-           is<js::NonSyntacticVariablesObject>();
+           is<js::UninitializedLexicalObject>();
 }
 
 template<>
 inline bool
 JSObject::is<js::DebugScopeObject>() const
 {
     // Note: don't use is<ProxyObject>() here -- it also matches subclasses!
     return hasClass(&js::ProxyObject::class_) &&
@@ -1076,18 +1036,18 @@ JSObject::is<js::StaticBlockObject>() co
 }
 
 namespace js {
 
 inline bool
 IsSyntacticScope(JSObject* scope)
 {
     return scope->is<ScopeObject>() &&
-           (!scope->is<DynamicWithObject>() || scope->as<DynamicWithObject>().isSyntactic()) &&
-           !scope->is<NonSyntacticVariablesObject>();
+           (!scope->is<DynamicWithObject>() ||
+            scope->as<DynamicWithObject>().isSyntactic());
 }
 
 inline const Value&
 ScopeObject::aliasedVar(ScopeCoordinate sc)
 {
     MOZ_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
     return getSlot(sc.slot());
 }
@@ -1101,64 +1061,26 @@ NestedScopeObject::enclosingNestedScope(
 
 inline bool
 ScopeIter::done() const
 {
     return ssi_.done();
 }
 
 inline bool
-ScopeIter::hasSyntacticScopeObject() const
-{
-    return ssi_.hasSyntacticDynamicScopeObject();
-}
-
-inline bool
-ScopeIter::hasNonSyntacticScopeObject() const
+ScopeIter::hasScopeObject() const
 {
-    // The case we're worrying about here is a NonSyntactic static scope which
-    // has 0+ corresponding non-syntactic DynamicWithObject scopes or a
-    // NonSyntacticVariablesObject.
-    if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
-        MOZ_ASSERT_IF(scope_->is<DynamicWithObject>(),
-                      !scope_->as<DynamicWithObject>().isSyntactic());
-        return scope_->is<DynamicWithObject>() ||
-               scope_->is<NonSyntacticVariablesObject>();
-    }
-    return false;
+    return ssi_.hasDynamicScopeObject();
 }
 
 inline bool
-ScopeIter::hasAnyScopeObject() const
-{
-    return hasSyntacticScopeObject() || hasNonSyntacticScopeObject();
-}
-
-inline bool
-ScopeIter::canHaveSyntacticScopeObject() const
+ScopeIter::canHaveScopeObject() const
 {
-    if (ssi_.done())
-        return false;
-
-    switch (type()) {
-      case Call:
-        return true;
-      case Block:
-        return true;
-      case With:
-        return true;
-      case Eval:
-        // Only strict eval scopes can have dynamic scope objects.
-        return staticEval().isStrict();
-      case NonSyntactic:
-        return false;
-    }
-
-    // Silence warnings.
-    return false;
+    // Non-strict eval scopes cannot have dynamic scope objects.
+    return !ssi_.done() && (type() != Eval || staticEval().isStrict());
 }
 
 inline JSObject&
 ScopeIter::enclosingScope() const
 {
     // As an engine invariant (maintained internally and asserted by Execute),
     // ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
     // chain; every scope chain must start with zero or more ScopeObjects and
@@ -1166,21 +1088,19 @@ ScopeIter::enclosingScope() const
     MOZ_ASSERT(done());
     MOZ_ASSERT(!IsSyntacticScope(scope_));
     return *scope_;
 }
 
 extern bool
 CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                 HandleObject dynamicTerminatingScope,
-                                MutableHandleObject dynamicScopeObj);
-
-bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
+                                MutableHandleObject dynamicScopeObj,
+                                MutableHandleObject staticScopeObj);
 
 #ifdef DEBUG
-void DumpStaticScopeChain(JSScript* script);
 bool
 AnalyzeEntrainedVariables(JSContext* cx, HandleScript script);
 #endif
 
 } // namespace js
 
 #endif /* vm_ScopeObject_h */
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1717,19 +1717,17 @@ CloneObject(JSContext* cx, HandleNativeO
     if (selfHostedObject->is<JSFunction>()) {
         RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
         bool hasName = selfHostedFunction->atom() != nullptr;
         // Arrow functions use the first extended slot for their lexical |this| value.
         MOZ_ASSERT(!selfHostedFunction->isArrow());
         js::gc::AllocKind kind = hasName
                                  ? gc::AllocKind::FUNCTION_EXTENDED
                                  : selfHostedFunction->getAllocKind();
-        MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global()));
-        clone = CloneFunctionAndScript(cx, selfHostedFunction, cx->global(),
-                                       /* newStaticScope = */ nullptr, kind);
+        clone = CloneFunctionObject(cx, selfHostedFunction, cx->global(), kind, TenuredObject);
         // To be able to re-lazify the cloned function, its name in the
         // self-hosting compartment has to be stored on the clone.
         if (clone && hasName)
             clone->as<JSFunction>().setExtendedSlot(0, StringValue(selfHostedFunction->atom()));
     } else if (selfHostedObject->is<RegExpObject>()) {
         RegExpObject& reobj = selfHostedObject->as<RegExpObject>();
         RootedAtom source(cx, reobj.getSource());
         MOZ_ASSERT(source->isPermanentAtom());
@@ -1803,27 +1801,32 @@ JSRuntime::cloneSelfHostedFunctionScript
     RootedValue funVal(cx);
     if (!GetUnclonedValue(cx, HandleNativeObject::fromMarkedLocation(&selfHostingGlobal_), id, &funVal))
         return false;
 
     RootedFunction sourceFun(cx, &funVal.toObject().as<JSFunction>());
     // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
     // aren't any.
     MOZ_ASSERT(!sourceFun->isGenerator());
+    RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
+    if (!sourceScript)
+        return false;
+    MOZ_ASSERT(!sourceScript->enclosingStaticScope());
+    JSScript* cscript = CloneScript(cx, nullptr, targetFun, sourceScript);
+    if (!cscript)
+        return false;
+    cscript->setFunction(targetFun);
+
     MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
     // The target function might have been relazified after it's flags changed.
     targetFun->setFlags((targetFun->flags() & ~JSFunction::INTERPRETED_LAZY) |
                         sourceFun->flags() | JSFunction::EXTENDED);
+    targetFun->setScript(cscript);
     MOZ_ASSERT(targetFun->isExtended());
-
-    RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
-    if (!sourceScript)
-        return false;
-    MOZ_ASSERT(!sourceScript->enclosingStaticScope());
-    return !!CloneScriptIntoFunction(cx, /* enclosingScope = */ nullptr, targetFun, sourceScript);
+    return true;
 }
 
 bool
 JSRuntime::cloneSelfHostedValue(JSContext* cx, HandlePropertyName name, MutableHandleValue vp)
 {
     RootedId id(cx, NameToId(name));
     RootedValue selfHostedValue(cx);
     if (!GetUnclonedValue(cx, HandleNativeObject::fromMarkedLocation(&selfHostingGlobal_), id, &selfHostedValue))
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -148,22 +148,17 @@ InterpreterFrame::createRestParameter(JS
 }
 
 static inline void
 AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* scope)
 {
 #ifdef DEBUG
     RootedObject enclosingScope(cx, script->enclosingStaticScope());
     for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) {
-        if (i.type() == StaticScopeIter<NoGC>::NonSyntactic) {
-            while (scope->is<DynamicWithObject>() || scope->is<NonSyntacticVariablesObject>()) {
-                MOZ_ASSERT(!IsSyntacticScope(scope));
-                scope = &scope->as<ScopeObject>().enclosingScope();
-            }
-        } else if (i.hasSyntacticDynamicScopeObject()) {
+        if (i.hasDynamicScopeObject()) {
             switch (i.type()) {
               case StaticScopeIter<NoGC>::Function:
                 MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
                 scope = &scope->as<CallObject>().enclosingScope();
                 break;
               case StaticScopeIter<NoGC>::Block:
                 MOZ_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope());
                 scope = &scope->as<ClonedBlockObject>().enclosingScope();
@@ -173,24 +168,23 @@ AssertDynamicScopeMatchesStaticScope(JSC
                 scope = &scope->as<DynamicWithObject>().enclosingScope();
                 break;
               case StaticScopeIter<NoGC>::NamedLambda:
                 scope = &scope->as<DeclEnvObject>().enclosingScope();
                 break;
               case StaticScopeIter<NoGC>::Eval:
                 scope = &scope->as<CallObject>().enclosingScope();
                 break;
-              case StaticScopeIter<NoGC>::NonSyntactic:
-                MOZ_CRASH("NonSyntactic should not have a syntactic scope");
-                break;
             }
         }
     }
 
-    MOZ_ASSERT(scope->is<GlobalObject>() || scope->is<DebugScopeObject>());
+    // The scope chain is always ended by one or more non-syntactic
+    // ScopeObjects (viz. GlobalObject or a non-syntactic WithObject).
+    MOZ_ASSERT(!IsSyntacticScope(scope));
 #endif
 }
 
 bool
 InterpreterFrame::initFunctionScopeObjects(JSContext* cx)
 {
     CallObject* callobj = CallObject::createForFunction(cx, this);
     if (!callobj)
@@ -246,17 +240,18 @@ 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()) {
-            MOZ_ASSERT_IF(isDebuggerEvalFrame(), !IsSyntacticScope(scopeChain()));
+            if (isDebuggerEvalFrame())
+                MOZ_ASSERT(!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.
              */
@@ -1138,17 +1133,17 @@ FrameIter::matchCallee(JSContext* cx, Ha
     {
         return false;
     }
 
     // Use the same condition as |js::CloneFunctionObject|, to know if we should
     // expect both functions to have the same JSScript. If so, and if they are
     // different, then they cannot be equal.
     RootedObject global(cx, &fun->global());
-    bool useSameScript = CanReuseScriptForClone(fun->compartment(), currentCallee, global);
+    bool useSameScript = CloneFunctionObjectUseSameScript(fun->compartment(), currentCallee, global);
     if (useSameScript &&
         (currentCallee->hasScript() != fun->hasScript() ||
          currentCallee->nonLazyScript() != fun->nonLazyScript()))
     {
         return false;
     }
 
     // If none of the previous filters worked, then take the risk of
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -141,39 +141,35 @@ PrepareScript(nsIURI* uri,
         JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
                                       JS::SourceBufferHolder::GiveOwnership);
 
         if (NS_FAILED(rv)) {
             return ReportError(cx, LOAD_ERROR_BADCHARSET, uri);
         }
 
         if (!reuseGlobal) {
-            if (JS_IsGlobalObject(targetObj))
-                JS::Compile(cx, options, srcBuf, script);
-            else
-                JS::CompileForNonSyntacticScope(cx, options, srcBuf, script);
+            options.setHasPollutedScope(!JS_IsGlobalObject(targetObj));
+            JS::Compile(cx, options, srcBuf, script);
         } else {
             AutoObjectVector scopeChain(cx);
             if (!JS_IsGlobalObject(targetObj) &&
                 !scopeChain.append(targetObj)) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
             // XXXbz do we really not care if the compile fails???
             JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr,
                                 srcBuf, function);
         }
     } else {
         // We only use lazy source when no special encoding is specified because
         // the lazy source loader doesn't know the encoding.
         if (!reuseGlobal) {
-            options.setSourceIsLazy(true);
-            if (JS_IsGlobalObject(targetObj))
-                JS::Compile(cx, options, buf, len, script);
-            else
-                JS::CompileForNonSyntacticScope(cx, options, buf, len, script);
+            options.setSourceIsLazy(true)
+                   .setHasPollutedScope(!JS_IsGlobalObject(targetObj));
+            JS::Compile(cx, options, buf, len, script);
         } else {
             AutoObjectVector scopeChain(cx);
             if (!JS_IsGlobalObject(targetObj) &&
                 !scopeChain.append(targetObj)) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
             // XXXbz do we really not care if the compile fails???
             JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr,