backout Bug 1221144 for perf regression
authorJoel Maher <jmaher@mozilla.com>
Fri, 12 Feb 2016 04:11:10 -0800
changeset 284145 966f47ed2f25eb54fb1f967d4443b3c2b8b63220
parent 284144 c1e09305f827211d32ff6a7d54be608d52710232
child 284146 4c9a6c4c5ba72bbe67db2f18cf77cde85a84500e
push id29998
push userphilringnalda@gmail.com
push dateSun, 14 Feb 2016 03:19:08 +0000
treeherderautoland@e355cacefc88 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1221144
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
backout Bug 1221144 for perf regression MozReview-Commit-ID: Aete3iN6i3r
js/src/builtin/Eval.cpp
js/src/builtin/ModuleObject.cpp
js/src/builtin/ModuleObject.h
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/ParseNode.cpp
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
js/src/gc/Marking.cpp
js/src/gc/Policy.h
js/src/jit-test/tests/gc/withStatementOffThread.js
js/src/jit-test/tests/xdr/scope.js
js/src/jsapi.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsgc.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/shell/js.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.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/src/vm/Xdr.cpp
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -282,17 +282,17 @@ EvalKernel(JSContext* cx, const CallArgs
                                              evalType == DIRECT_EVAL
                                              ? CALLED_FROM_JSOP_EVAL
                                              : NOT_CALLED_FROM_JSOP_EVAL);
 
         const char* introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
-        Rooted<StaticScope*> enclosing(cx);
+        RootedObject enclosing(cx);
         if (evalType == DIRECT_EVAL)
             enclosing = callerScript->innermostStaticScope(pc);
         else
             enclosing = &cx->global()->lexicalScope().staticBlock();
         Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing));
         if (!staticScope)
             return false;
 
@@ -368,17 +368,17 @@ js::DirectEvalStringFromIon(JSContext* c
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                              &mutedErrors, CALLED_FROM_JSOP_EVAL);
 
         const char* introducerFilename = filename;
         if (maybeScript && maybeScript->scriptSource()->introducerFilename())
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
-        Rooted<StaticScope*> enclosing(cx, callerScript->innermostStaticScope(pc));
+        RootedObject enclosing(cx, callerScript->innermostStaticScope(pc));
         Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing));
         if (!staticScope)
             return false;
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setIsRunOnce(true)
                .setForEval(true)
@@ -470,18 +470,17 @@ js::ExecuteInGlobalAndReturnScope(JSCont
 
     Rooted<ClonedBlockObject*> globalLexical(cx, &globalRoot->lexicalScope());
     Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalLexical));
     if (!scope)
         return false;
 
     // Unlike the non-syntactic scope chain API used by the subscript loader,
     // this API creates a fresh block scope each time.
-    Rooted<StaticNonSyntacticScope*> enclosingStaticScope(cx,
-        &script->enclosingStaticScope()->as<StaticNonSyntacticScope>());
+    RootedObject enclosingStaticScope(cx, script->enclosingStaticScope());
     scope = ClonedBlockObject::createNonSyntactic(cx, enclosingStaticScope, scope);
     if (!scope)
         return false;
 
     RootedValue rval(cx);
     if (!ExecuteKernel(cx, script, *scope, UndefinedValue(),
                        NullFramePtr() /* evalInFrame */, rval.address()))
     {
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -561,29 +561,25 @@ DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject,
 
 /* static */ bool
 ModuleObject::isInstance(HandleValue value)
 {
     return value.isObject() && value.toObject().is<ModuleObject>();
 }
 
 /* static */ ModuleObject*
-ModuleObject::create(ExclusiveContext* cx, Handle<StaticScope*> enclosingStaticScope)
+ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope)
 {
     RootedObject proto(cx, cx->global()->getModulePrototype());
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
     if (!obj)
         return nullptr;
 
     RootedModuleObject self(cx, &obj->as<ModuleObject>());
-    Rooted<StaticModuleScope*> scope(cx, StaticModuleScope::create(cx, self,
-                                                                   enclosingStaticScope));
-    if (!scope)
-        return nullptr;
-    self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(scope));
+    self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(enclosingStaticScope));
 
     Zone* zone = cx->zone();
     IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
     if (!bindings || !bindings->init()) {
         ReportOutOfMemory(cx);
         js_delete<IndirectBindingMap>(bindings);
         return nullptr;
     }
@@ -722,20 +718,20 @@ ModuleObject::evaluated() const
 }
 
 ModuleEnvironmentObject&
 ModuleObject::initialEnvironment() const
 {
     return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
 }
 
-StaticModuleScope*
-ModuleObject::staticScope() const
+JSObject*
+ModuleObject::enclosingStaticScope() const
 {
-    return &getReservedSlot(StaticScopeSlot).toObject().as<StaticModuleScope>();
+    return getReservedSlot(StaticScopeSlot).toObjectOrNull();
 }
 
 /* static */ void
 ModuleObject::trace(JSTracer* trc, JSObject* obj)
 {
     ModuleObject& module = obj->as<ModuleObject>();
     if (module.hasScript()) {
         JSScript* script = module.script();
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -16,18 +16,16 @@
 
 #include "vm/NativeObject.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 class ModuleEnvironmentObject;
 class ModuleObject;
-class StaticScope;
-class StaticModuleScope;
 
 namespace frontend {
 class ParseNode;
 } /* namespace frontend */
 
 typedef Rooted<ModuleObject*> RootedModuleObject;
 typedef Handle<ModuleObject*> HandleModuleObject;
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
@@ -221,27 +219,27 @@ class ModuleObject : public NativeObject
         FunctionDeclarationsSlot,
         SlotCount
     };
 
     static const Class class_;
 
     static bool isInstance(HandleValue value);
 
-    static ModuleObject* create(ExclusiveContext* cx, Handle<StaticScope*> enclosingStaticScope);
+    static ModuleObject* create(ExclusiveContext* cx, HandleObject enclosingStaticScope);
     void init(HandleScript script);
     void setInitialEnvironment(Handle<ModuleEnvironmentObject*> initialEnvironment);
     void initImportExportData(HandleArrayObject requestedModules,
                               HandleArrayObject importEntries,
                               HandleArrayObject localExportEntries,
                               HandleArrayObject indiretExportEntries,
                               HandleArrayObject starExportEntries);
 
     JSScript* script() const;
-    StaticModuleScope* staticScope() const;
+    JSObject* enclosingStaticScope() const;
     ModuleEnvironmentObject& initialEnvironment() const;
     ModuleEnvironmentObject* environment() const;
     ModuleNamespaceObject* namespace_();
     bool evaluated() const;
     ArrayObject& requestedModules() const;
     ArrayObject& importEntries() const;
     ArrayObject& localExportEntries() const;
     ArrayObject& indirectExportEntries() const;
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -67,17 +67,17 @@ class MOZ_STACK_CLASS BytecodeCompiler
 
   private:
     bool checkLength();
     bool createScriptSource();
     bool maybeCompressSource();
     bool canLazilyParse();
     bool createParser();
     bool createSourceAndParser();
-    bool createScript(Handle<StaticScope*> staticScope, bool savedCallerFun = false);
+    bool createScript(HandleObject staticScope, bool savedCallerFun = false);
     bool createEmitter(SharedContext* sharedContext, HandleScript evalCaller = nullptr,
                        bool insideNonGlobalEval = false);
     bool isEvalCompilationUnit();
     bool isNonGlobalEvalCompilationUnit();
     bool isNonSyntacticCompilationUnit();
     bool saveCallerFun(HandleScript evalCaller);
     bool handleParseFailure(const Directives& newDirectives);
     bool prepareAndEmitTree(ParseNode** pn);
@@ -250,17 +250,17 @@ bool
 BytecodeCompiler::createSourceAndParser()
 {
     return createScriptSource() &&
            maybeCompressSource() &&
            createParser();
 }
 
 bool
-BytecodeCompiler::createScript(Handle<StaticScope*> staticScope, bool savedCallerFun)
+BytecodeCompiler::createScript(HandleObject staticScope, bool savedCallerFun)
 {
     script = JSScript::Create(cx, staticScope, savedCallerFun, options,
                               sourceObject, /* sourceStart = */ 0, sourceBuffer.length());
 
     return script != nullptr;
 }
 
 bool
@@ -279,18 +279,21 @@ bool
 BytecodeCompiler::isEvalCompilationUnit()
 {
     return enclosingStaticScope->is<StaticEvalScope>();
 }
 
 bool
 BytecodeCompiler::isNonGlobalEvalCompilationUnit()
 {
-    return isEvalCompilationUnit() &&
-           !IsStaticGlobalLexicalScope(enclosingStaticScope->enclosingScope());
+    if (!isEvalCompilationUnit())
+        return false;
+    StaticEvalScope& eval = enclosingStaticScope->as<StaticEvalScope>();
+    JSObject* enclosing = eval.enclosingScopeForStaticScopeIter();
+    return !IsStaticGlobalLexicalScope(enclosing);
 }
 
 bool
 BytecodeCompiler::isNonSyntacticCompilationUnit()
 {
     return enclosingStaticScope->is<StaticNonSyntacticScope>();
 }
 
@@ -554,28 +557,26 @@ BytecodeCompiler::compileScript(HandleOb
 
     if (!maybeCompleteCompressSource())
         return nullptr;
 
     MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
     return script;
 }
 
-ModuleObject*
-BytecodeCompiler::compileModule()
+ModuleObject* BytecodeCompiler::compileModule()
 {
     if (!createSourceAndParser())
         return nullptr;
 
     Rooted<ModuleObject*> module(cx, ModuleObject::create(cx, enclosingStaticScope));
     if (!module)
         return nullptr;
 
-    Rooted<StaticModuleScope*> moduleScope(cx, module->staticScope());
-    if (!createScript(moduleScope))
+    if (!createScript(module))
         return nullptr;
 
     module->init(script);
 
     ModuleBuilder builder(cx->asJSContext(), module);
     ParseNode* pn = parser->standaloneModule(module, builder);
     if (!pn)
         return nullptr;
@@ -645,18 +646,17 @@ BytecodeCompiler::compileFunctionBody(Mu
         !maybeSetSourceMap(parser->tokenStream))
     {
         return false;
     }
 
     if (fn->pn_funbox->function()->isInterpreted()) {
         MOZ_ASSERT(fun == fn->pn_funbox->function());
 
-        Rooted<StaticScope*> scope(cx, fn->pn_funbox->staticScope());
-        if (!createScript(scope))
+        if (!createScript(enclosingStaticScope))
             return false;
 
         script->bindings = fn->pn_funbox->bindings;
 
         if (!createEmitter(fn->pn_funbox) ||
             !emitter->emitFunctionScript(fn->pn_body))
         {
             return false;
@@ -798,22 +798,21 @@ frontend::CompileLazyFunction(JSContext*
     MOZ_ASSERT(!lazy->isLegacyGenerator());
     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind());
     if (!pn)
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
 
-    Rooted<StaticScope*> staticScope(cx, pn->pn_funbox->staticScope());
-    MOZ_ASSERT(staticScope);
+    RootedObject enclosingScope(cx, lazy->enclosingScope());
     RootedScriptSource sourceObject(cx, lazy->sourceObject());
     MOZ_ASSERT(sourceObject);
 
-    Rooted<JSScript*> script(cx, JSScript::Create(cx, staticScope, false, options,
+    Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
                                                   sourceObject, lazy->begin(), lazy->end()));
     if (!script)
         return false;
 
     script->bindings = pn->pn_funbox->bindings;
 
     if (lazy->usesArgumentsApplyAndThis())
         script->setUsesArgumentsApplyAndThis();
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -728,17 +728,17 @@ BytecodeEmitter::pushLoopStatement(LoopS
     if (enclosingLoop) {
         stmt->canIonOsr = (enclosingLoop->canIonOsr &&
                            stmt->stackDepth == enclosingLoop->stackDepth + loopSlots);
     } else {
         stmt->canIonOsr = stmt->stackDepth == loopSlots;
     }
 }
 
-StaticScope*
+JSObject*
 BytecodeEmitter::innermostStaticScope() const
 {
     if (StmtInfoBCE* stmt = innermostScopeStmt())
         return stmt->staticScope;
     return sc->staticScope();
 }
 
 #ifdef DEBUG
@@ -1357,17 +1357,19 @@ bool
 BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const
 {
     // 'eval' and non-syntactic scripts are always under an invisible lexical
     // scope, but since it is not syntactic, it should still be considered at
     // body level.
     if (sc->staticScope()->is<StaticEvalScope>()) {
         bool bl = !stmt->enclosing;
         MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
-        MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope());
+        MOZ_ASSERT_IF(bl, stmt->staticScope
+                              ->as<StaticBlockScope>()
+                              .enclosingStaticScope() == sc->staticScope());
         return bl;
     }
     return !stmt;
 }
 
 uint32_t
 BytecodeEmitter::computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut)
 {
@@ -1460,18 +1462,18 @@ BytecodeEmitter::computeDefinitionIsAlia
 {
     if (dn->isKnownAliased()) {
         *op = UnaliasedVarOpToAliasedVarOp(*op);
     } else if (isAliasedName(bceOfDef, dn)) {
         // Translate the frame slot to a slot on the dynamic scope
         // object. Aliased block bindings do not need adjusting; see
         // computeAliasedSlots.
         uint32_t slot = dn->pn_scopecoord.slot();
-        if (blockScopeOfDef(dn)->is<StaticFunctionScope>() ||
-            blockScopeOfDef(dn)->is<StaticModuleScope>())
+        if (blockScopeOfDef(dn)->is<JSFunction>() ||
+            blockScopeOfDef(dn)->is<ModuleObject>())
         {
             MOZ_ASSERT(IsArgOp(*op) || slot < bceOfDef->script->bindings.numBodyLevelLocals());
             MOZ_ALWAYS_TRUE(bceOfDef->lookupAliasedName(bceOfDef->script, dn->name(), &slot));
         }
         if (!dn->pn_scopecoord.setSlot(parser->tokenStream, slot))
             return false;
 
         *op = UnaliasedVarOpToAliasedVarOp(*op);
@@ -1562,42 +1564,38 @@ BytecodeEmitter::tryConvertFreeName(Pars
             }
 
             if (!ssi.hasSyntacticDynamicScopeObject())
                 continue;
 
             // Look up for name in function and block scopes.
             if (ssi.type() == StaticScopeIter<NoGC>::Function) {
                 RootedScript funScript(cx, ssi.funScript());
-                if (funScript->funHasExtensibleScope() ||
-                    ssi.fun().function().atom() == pn->pn_atom)
-                {
+                if (funScript->funHasExtensibleScope() || ssi.fun().atom() == pn->pn_atom)
                     return false;
-                }
 
                 // Skip the current function, since we're trying to convert a
                 // free name.
                 if (script != funScript) {
                     uint32_t slot_;
                     if (lookupAliasedName(funScript, name, &slot_, pn)) {
                         slot = Some(slot_);
                         break;
                     }
                 }
             } else if (ssi.type() == StaticScopeIter<NoGC>::Module) {
-                RootedScript moduleScript(cx, ssi.module().script());
+                RootedScript moduleScript(cx, ssi.moduleScript());
                 uint32_t slot_;
                 if (lookupAliasedName(moduleScript, name, &slot_, pn)) {
                     slot = Some(slot_);
                     break;
                 }
 
                 // Convert module import accesses to use JSOP_GETIMPORT.
-                RootedModuleEnvironmentObject env(cx, &ssi.module().moduleObject()
-                                                       .initialEnvironment());
+                RootedModuleEnvironmentObject env(cx, &ssi.module().initialEnvironment());
                 RootedPropertyName propName(cx, name);
                 MOZ_ASSERT(env);
                 if (env->hasImportBinding(propName)) {
                     if (pn->getOp() == JSOP_GETNAME) {
                         pn->setOp(JSOP_GETIMPORT);
                         return true;
                     }
                     return false;
@@ -1830,21 +1828,22 @@ BytecodeEmitter::bindNameToSlotHelper(Pa
       case Definition::NAMED_LAMBDA: {
         MOZ_ASSERT(dn->isOp(JSOP_CALLEE));
         MOZ_ASSERT(op != JSOP_CALLEE);
 
         /*
          * Currently, the ALIASEDVAR ops do not support accessing the
          * callee of a DeclEnvObject, so use NAME.
          */
-        if (blockScopeOfDef(dn) != sc->asFunctionBox()->staticScope())
+        JSFunction* fun = sc->asFunctionBox()->function();
+        if (blockScopeOfDef(dn) != fun)
             return true;
 
-        MOZ_ASSERT(sc->asFunctionBox()->function()->isLambda());
-        MOZ_ASSERT(pn->pn_atom == sc->asFunctionBox()->function()->atom());
+        MOZ_ASSERT(fun->isLambda());
+        MOZ_ASSERT(pn->pn_atom == fun->atom());
 
         /*
          * Leave pn->isOp(JSOP_GETNAME) if this->fun needs a CallObject to
          * address two cases: a new binding introduced by eval, and
          * assignment to the name in strict mode.
          *
          *   var fun = (function f(s) { eval(s); return f; });
          *   assertEq(fun("var f = 42"), 42);
@@ -3534,19 +3533,19 @@ BytecodeEmitter::emitSetThis(ParseNode* 
     if (!emit1(JSOP_POP))
         return false;
 
     // Emit the set.
     return emitVarOp(name, setOp);
 }
 
 static bool
-IsModuleOnScopeChain(StaticScope* scope)
-{
-    for (StaticScopeIter<NoGC> ssi(scope); !ssi.done(); ssi++) {
+IsModuleOnScopeChain(JSObject* obj)
+{
+    for (StaticScopeIter<NoGC> ssi(obj); !ssi.done(); ssi++) {
         if (ssi.type() == StaticScopeIter<NoGC>::Module)
             return true;
     }
     return false;
 }
 
 bool
 BytecodeEmitter::emitFunctionScript(ParseNode* body)
@@ -6386,63 +6385,54 @@ BytecodeEmitter::emitFunction(ParseNode*
     if (fun->isInterpreted()) {
         bool singleton = checkRunOnceContext();
         if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
             return false;
 
         SharedContext* outersc = sc;
         if (fun->isInterpretedLazy()) {
             if (!fun->lazyScript()->sourceObject()) {
-                // Two cases that can arise during parsing can cause the static
-                // scope chain to be incorrectly linked up: (1) the
-                // transformation of blocks from non-scopeful to scopeful when
-                // the first block-scoped declaration is found; (2) legacy
-                // comprehension expression transplantation. The
-                // setEnclosingScope call below fixes these cases.
-                Rooted<StaticScope*> enclosingScope(cx, innermostStaticScope());
-                fun->lazyScript()->staticScope()->setEnclosingScope(enclosingScope);
-
+                JSObject* scope = innermostStaticScope();
                 JSObject* source = script->sourceObject();
-                fun->lazyScript()->initSource(&source->as<ScriptSourceObject>());
+                fun->lazyScript()->setParent(scope, &source->as<ScriptSourceObject>());
             }
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
+
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
             MOZ_ASSERT(parent->getVersion() == parser->options().version);
             MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
             const TransitiveCompileOptions& transitiveOptions = parser->options();
             CompileOptions options(cx, transitiveOptions);
 
-            // See comment above regarding funScope->setEnclosingScope().
-            Rooted<StaticScope*> funScope(cx, funbox->staticScope());
-            Rooted<StaticScope*> enclosingScope(cx, innermostStaticScope());
-            funScope->setEnclosingScope(enclosingScope);
-
+            Rooted<JSObject*> enclosingScope(cx, innermostStaticScope());
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
-            Rooted<JSScript*> script(cx, JSScript::Create(cx, funScope, false, options,
+            Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
                                                           sourceObject,
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
                 return false;
 
             script->bindings = funbox->bindings;
 
             uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
             BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
                                  insideEval, evalCaller,
                                  insideNonGlobalEval, lineNum, emitterMode);
             if (!bce2.init())
                 return false;
+
+            /* We measured the max scope depth when we parsed the function. */
             if (!bce2.emitFunctionScript(pn->pn_body))
                 return false;
 
             if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
                 script->setUsesArgumentsApplyAndThis();
         }
         if (outersc->isFunctionBox())
             outersc->asFunctionBox()->function()->nonLazyScript()->setHasInnerFunctions(true);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -242,19 +242,19 @@ struct BytecodeEmitter
                     HandleScript script, Handle<LazyScript*> lazyScript,
                     bool insideEval, HandleScript evalCaller,
                     bool insideNonGlobalEval, uint32_t lineNum, EmitterMode emitterMode = Normal);
     bool init();
     bool updateLocalsToFrameSlots();
 
     StmtInfoBCE* innermostStmt() const { return stmtStack.innermost(); }
     StmtInfoBCE* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
-    StaticScope* innermostStaticScope() const;
-    StaticScope* blockScopeOfDef(Definition* dn) const {
-        return &parser->blockScopes[dn->pn_blockid].get()->as<StaticScope>();
+    JSObject* innermostStaticScope() const;
+    JSObject* blockScopeOfDef(Definition* dn) const {
+        return parser->blockScopes[dn->pn_blockid];
     }
 
     bool atBodyLevel(StmtInfoBCE* stmt) const;
     bool atBodyLevel() const {
         return atBodyLevel(innermostStmt());
     }
     uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
     bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -1188,18 +1188,18 @@ ObjectBox::trace(JSTracer* trc)
     TraceRoot(trc, &object, "parser.object");
 }
 
 void
 FunctionBox::trace(JSTracer* trc)
 {
     ObjectBox::trace(trc);
     bindings.trace(trc);
-    if (staticScope_)
-        TraceRoot(trc, &staticScope_, "funbox-staticScope");
+    if (enclosingStaticScope_)
+        TraceRoot(trc, &enclosingStaticScope_, "funbox-enclosingStaticScope");
 }
 
 void
 ModuleBox::trace(JSTracer* trc)
 {
     ObjectBox::trace(trc);
     bindings.trace(trc);
 }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -52,19 +52,19 @@ using JS::AutoGCRooter;
 
 JSFunction::AutoParseUsingFunctionBox::AutoParseUsingFunctionBox(ExclusiveContext* cx,
                                                                  frontend::FunctionBox* funbox)
   : fun_(cx, funbox->function()),
     oldEnv_(cx, fun_->environment())
 {
     fun_->unsetEnvironment();
     fun_->setFunctionBox(funbox);
-    funbox->computeAllowSyntax(funbox->staticScope_);
-    funbox->computeInWith(funbox->staticScope_);
-    funbox->computeThisBinding(funbox->staticScope_);
+    funbox->computeAllowSyntax(fun_);
+    funbox->computeInWith(fun_);
+    funbox->computeThisBinding(fun_);
 }
 
 JSFunction::AutoParseUsingFunctionBox::~AutoParseUsingFunctionBox()
 {
     fun_->unsetFunctionBox();
     fun_->initEnvironment(oldEnv_);
 }
 
@@ -114,75 +114,74 @@ MarkUsesAsHoistedLexical(ParseNode* pn)
     while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
         MOZ_ASSERT(pnu->isUsed());
         pnu->pn_dflags |= PND_LEXICAL;
         pnup = &pnu->pn_link;
     }
 }
 
 void
-SharedContext::computeAllowSyntax(StaticScope* staticScope)
+SharedContext::computeAllowSyntax(JSObject* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
-        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().function().isArrow()) {
+        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().isArrow()) {
             // Any function supports new.target.
             allowNewTarget_ = true;
-            allowSuperProperty_ = it.fun().function().allowSuperProperty();
+            allowSuperProperty_ = it.fun().allowSuperProperty();
             if (it.maybeFunctionBox()) {
                 superScopeAlreadyNeedsHomeObject_ = it.maybeFunctionBox()->needsHomeObject();
                 allowSuperCall_ = it.maybeFunctionBox()->isDerivedClassConstructor();
             } else {
-                allowSuperCall_ = it.fun().function().isDerivedClassConstructor();
+                allowSuperCall_ = it.fun().isDerivedClassConstructor();
             }
             break;
         }
     }
 }
 
 void
-SharedContext::computeThisBinding(StaticScope* staticScope)
+SharedContext::computeThisBinding(JSObject* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
         if (it.type() == StaticScopeIter<CanGC>::Module) {
             thisBinding_ = ThisBinding::Module;
             return;
         }
 
         if (it.type() == StaticScopeIter<CanGC>::Function) {
-            RootedFunction fun(context, &it.fun().function());
             // Arrow functions and generator expression lambdas don't have
             // their own `this` binding.
-            if (fun->isArrow())
+            if (it.fun().isArrow())
                 continue;
             bool isDerived;
             if (it.maybeFunctionBox()) {
                 if (it.maybeFunctionBox()->inGenexpLambda)
                     continue;
                 isDerived = it.maybeFunctionBox()->isDerivedClassConstructor();
             } else {
-                if (fun->nonLazyScript()->isGeneratorExp())
+                if (it.fun().nonLazyScript()->isGeneratorExp())
                     continue;
-                isDerived = fun->isDerivedClassConstructor();
+                isDerived = it.fun().isDerivedClassConstructor();
             }
 
             // Derived class constructors (including nested arrow functions and
             // eval) need TDZ checks when accessing |this|.
             if (isDerived)
                 needsThisTDZChecks_ = true;
 
             thisBinding_ = ThisBinding::Function;
             return;
         }
     }
 
     thisBinding_ = ThisBinding::Global;
 }
 
 void
-SharedContext::computeInWith(StaticScope* staticScope)
+SharedContext::computeInWith(JSObject* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
         if (it.type() == StaticScopeIter<CanGC>::With) {
             inWith_ = true;
             break;
         }
     }
 }
@@ -191,26 +190,26 @@ void
 SharedContext::markSuperScopeNeedsHomeObject()
 {
     MOZ_ASSERT(allowSuperProperty());
 
     if (superScopeAlreadyNeedsHomeObject_)
         return;
 
     for (StaticScopeIter<CanGC> it(context, staticScope()); !it.done(); it++) {
-        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().function().isArrow()) {
-            MOZ_ASSERT(it.fun().function().allowSuperProperty());
+        if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().isArrow()) {
+            MOZ_ASSERT(it.fun().allowSuperProperty());
             // If we are still emitting the outer function that needs a home
             // object, mark it as needing one. Otherwise, we must be emitting
             // an eval script, and the outer function must already be marked
             // as needing a home object since it contains an eval.
             if (it.maybeFunctionBox())
                 it.maybeFunctionBox()->setNeedsHomeObject();
             else
-                MOZ_ASSERT(it.funScript()->needsHomeObject());
+                MOZ_ASSERT(it.fun().nonLazyScript()->needsHomeObject());
             superScopeAlreadyNeedsHomeObject_ = true;
             return;
         }
     }
     MOZ_CRASH("Must have found an enclosing function box scope that allows super.property");
 }
 
 // See comment on member function declaration.
@@ -765,22 +764,22 @@ Parser<ParseHandler>::newObjectBox(JSObj
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 template <typename ParseHandler>
 FunctionBox::FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
-                         ParseContext<ParseHandler>* outerpc,
+                         JSObject* enclosingStaticScope, ParseContext<ParseHandler>* outerpc,
                          Directives directives, bool extraWarnings, GeneratorKind generatorKind)
   : ObjectBox(fun, traceListHead),
     SharedContext(cx, directives, extraWarnings),
     bindings(),
-    staticScope_(nullptr),
+    enclosingStaticScope_(enclosingStaticScope),
     bufStart(0),
     bufEnd(0),
     startLine(1),
     startColumn(0),
     length(0),
     generatorKindBits_(GeneratorKindAsBits(generatorKind)),
     inGenexpLambda(false),
     hasDestructuringArgs(false),
@@ -793,54 +792,44 @@ FunctionBox::FunctionBox(ExclusiveContex
     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());
 }
 
-bool
-FunctionBox::initStaticScope(Handle<StaticScope*> enclosingScope)
-{
-    RootedFunction fun(context, function());
-    staticScope_ = StaticFunctionScope::create(context, fun, enclosingScope);
-    return staticScope_ != nullptr;
-}
-
 template <typename ParseHandler>
 FunctionBox*
 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
                                      ParseContext<ParseHandler>* outerpc,
                                      Directives inheritedDirectives,
                                      GeneratorKind generatorKind,
-                                     Handle<StaticScope*> enclosingStaticScope)
+                                     JSObject* enclosingStaticScope)
 {
     MOZ_ASSERT_IF(outerpc, enclosingStaticScope == outerpc->innermostStaticScope());
     MOZ_ASSERT(fun);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     FunctionBox* funbox =
-        alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc, inheritedDirectives,
-                                options().extraWarningsOption, generatorKind);
+        alloc.new_<FunctionBox>(context, traceListHead, fun, enclosingStaticScope, outerpc,
+                                inheritedDirectives, options().extraWarningsOption,
+                                generatorKind);
     if (!funbox) {
         ReportOutOfMemory(context);
         return nullptr;
     }
+
     traceListHead = funbox;
-
-    if (!funbox->initStaticScope(enclosingStaticScope))
-        return nullptr;
-
     if (fn)
         handler.setFunctionBox(fn, funbox);
 
     return funbox;
 }
 
 template <typename ParseHandler>
 ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
@@ -1179,17 +1168,17 @@ Parser<SyntaxParseHandler>::defineFuncti
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
                                                  Handle<PropertyNameVector> formals,
                                                  GeneratorKind generatorKind,
                                                  Directives inheritedDirectives,
                                                  Directives* newDirectives,
-                                                 Handle<StaticScope*> enclosingStaticScope)
+                                                 HandleObject enclosingStaticScope)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node fn = handler.newFunctionDefinition();
     if (!fn)
         return null();
 
     ParseNode* argsbody = handler.newList(PNK_ARGSBODY);
@@ -2872,19 +2861,17 @@ Parser<SyntaxParseHandler>::finishFuncti
 
     if (funbox->inWith())
         return abortIfSyntaxParser();
 
     size_t numFreeVariables = pc->lexdeps->count();
     size_t numInnerFunctions = pc->innerFunctions.length();
 
     RootedFunction fun(context, funbox->function());
-    Rooted<StaticFunctionScope*> funScope(context, funbox->staticScope());
-    LazyScript* lazy = LazyScript::CreateRaw(context, fun, funScope,
-                                             numFreeVariables, numInnerFunctions,
+    LazyScript* lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions,
                                              versionNumber(), funbox->bufStart, funbox->bufEnd,
                                              funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
     size_t i = 0;
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
@@ -3077,17 +3064,17 @@ Parser<FullParseHandler>::standaloneLazy
     if (!pn)
         return null();
 
     // Our tokenStream has no current token, so pn's position is garbage.
     // Substitute the position of the first token in our source.
     if (!tokenStream.peekTokenPos(&pn->pn_pos))
         return null();
 
-    Rooted<StaticScope*> enclosing(context, fun->lazyScript()->enclosingScope());
+    RootedObject enclosing(context, fun->lazyScript()->enclosingScope());
     Directives directives(/* strict = */ strict);
     FunctionBox* funbox = newFunctionBox(pn, fun, directives, generatorKind, enclosing);
     if (!funbox)
         return null();
     funbox->length = fun->nargs() - fun->hasRest();
 
     if (fun->lazyScript()->isDerivedClassConstructor())
         funbox->setDerivedClassConstructor();
@@ -6617,21 +6604,18 @@ Parser<ParseHandler>::yieldExpression(In
 
     MOZ_CRASH("yieldExpr");
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::withStatement(YieldHandling yieldHandling)
 {
-    // This is intentionally different from other abortIfSyntaxParser()
-    // bailouts: `with` statements rule out syntax-only parsing for the entire
-    // compilation unit. This `return null()` causes us to bail out all the way
-    // to BytecodeCompiler::compileScript(), which retries with syntax parsing
-    // disabled. See bug 892583.
+    // test262/ch12/12.10/12.10-0-1.js fails if we try to parse with-statements
+    // in syntax-parse mode. See bug 892583.
     if (handler.syntaxParser) {
         handler.disableSyntaxParser();
         abortedSyntaxParse = true;
         return null();
     }
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
     uint32_t begin = pos().begin;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -290,17 +290,17 @@ struct MOZ_STACK_CLASS ParseContext : pu
 
     bool init(Parser<ParseHandler>& parser);
 
     unsigned blockid() { return stmtStack.innermost() ? stmtStack.innermost()->blockid : bodyid; }
 
     StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); }
     StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
     StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); }
-    StaticScope* innermostStaticScope() const {
+    JSObject* innermostStaticScope() const {
         if (StmtInfoPC* stmt = innermostScopeStmt())
             return stmt->staticScope;
         return sc->staticScope();
     }
 
     bool isBodyLevelLexicallyDeclaredName(HandleAtom name) {
         return bodyLevelLexicallyDeclaredNames_.has(name);
     }
@@ -320,17 +320,19 @@ struct MOZ_STACK_CLASS ParseContext : pu
     //
     bool atBodyLevel(StmtInfoPC* stmt) {
         // 'eval' and non-syntactic scripts are always under an invisible
         // lexical scope, but since it is not syntactic, it should still be
         // considered at body level.
         if (sc->staticScope()->is<StaticEvalScope>()) {
             bool bl = !stmt->enclosing;
             MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
-            MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope());
+            MOZ_ASSERT_IF(bl, stmt->staticScope
+                                  ->template as<StaticBlockScope>()
+                                  .enclosingStaticScope() == sc->staticScope());
             return bl;
         }
         return !stmt;
     }
 
     bool atBodyLevel() {
         return atBodyLevel(innermostStmt());
     }
@@ -515,44 +517,44 @@ class Parser : private JS::AutoGCRooter,
 
     /*
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     ObjectBox* newObjectBox(JSObject* obj);
     FunctionBox* newFunctionBox(Node fn, JSFunction* fun, ParseContext<ParseHandler>* outerpc,
                                 Directives directives, GeneratorKind generatorKind,
-                                Handle<StaticScope*> enclosingStaticScope);
+                                JSObject* enclosingStaticScope);
 
     // Use when the funbox is the outermost.
     FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives,
-                                GeneratorKind generatorKind, Handle<StaticScope*> enclosingStaticScope)
+                                GeneratorKind generatorKind, HandleObject enclosingStaticScope)
     {
         return newFunctionBox(fn, fun, nullptr, directives, generatorKind,
                               enclosingStaticScope);
     }
 
     // Use when the funbox should be linked to the outerpc's innermost scope.
     FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext<ParseHandler>* outerpc,
                                 Directives directives, GeneratorKind generatorKind)
     {
-        Rooted<StaticScope*> enclosing(context, outerpc->innermostStaticScope());
+        RootedObject enclosing(context, outerpc->innermostStaticScope());
         return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
     }
 
     ModuleBox* newModuleBox(Node pn, HandleModuleObject module, ModuleBuilder& builder);
 
     /*
      * Create a new function object given a name (which is optional if this is
      * a function expression).
      */
     JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind,
                             HandleObject proto);
 
-    bool generateBlockId(StaticScope* staticScope, uint32_t* blockIdOut) {
+    bool generateBlockId(JSObject* staticScope, uint32_t* blockIdOut) {
         if (blockScopes.length() == StmtInfoPC::BlockIdLimit) {
             tokenStream.reportError(JSMSG_NEED_DIET, "program");
             return false;
         }
         MOZ_ASSERT(blockScopes.length() < StmtInfoPC::BlockIdLimit);
         *blockIdOut = blockScopes.length();
         return blockScopes.append(staticScope);
     }
@@ -608,17 +610,17 @@ class Parser : private JS::AutoGCRooter,
     // Parse a module.
     Node standaloneModule(Handle<ModuleObject*> module, ModuleBuilder& builder);
 
     // Parse a function, given only its body. Used for the Function and
     // Generator constructors.
     Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,
                                 GeneratorKind generatorKind,
                                 Directives inheritedDirectives, Directives* newDirectives,
-                                Handle<StaticScope*> enclosingStaticScope);
+                                HandleObject enclosingStaticScope);
 
     // Parse a function, given only its arguments and body. Used for lazily
     // parsed functions.
     Node standaloneLazyFunction(HandleFunction fun, bool strict, GeneratorKind generatorKind);
 
     /*
      * Parse a function body.  Pass StatementListBody if the body is a list of
      * statements; pass ExpressionBody if the body is a single expression.
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -224,20 +224,20 @@ class SharedContext
         superScopeAlreadyNeedsHomeObject_(false)
     { }
 
     // The unfortunate reason that staticScope() is a virtual is because
     // GlobalSharedContext and FunctionBox have different lifetimes.
     // GlobalSharedContexts are stack allocated and thus may use RootedObject
     // for the static scope. FunctionBoxes are LifoAlloc'd and need to
     // manually trace their static scope.
-    virtual StaticScope* staticScope() const = 0;
-    void computeAllowSyntax(StaticScope* staticScope);
-    void computeInWith(StaticScope* staticScope);
-    void computeThisBinding(StaticScope* staticScope);
+    virtual JSObject* staticScope() const = 0;
+    void computeAllowSyntax(JSObject* staticScope);
+    void computeInWith(JSObject* staticScope);
+    void computeThisBinding(JSObject* staticScope);
 
     virtual ObjectBox* toObjectBox() { return nullptr; }
     bool isObjectBox() { return toObjectBox() != nullptr; }
     bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
     inline FunctionBox* asFunctionBox();
     bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); }
     inline ModuleBox* asModuleBox();
     bool isGlobalContext() { return !toObjectBox(); }
@@ -295,29 +295,29 @@ class MOZ_STACK_CLASS GlobalSharedContex
     {
         computeAllowSyntax(staticScope);
         computeInWith(staticScope);
 
         // If we're executing a Debugger eval-in-frame, staticScope is always a
         // non-function scope, so we have to compute our ThisBinding based on
         // the actual callee.
         if (maybeEvalCaller)
-            computeThisBinding(maybeEvalCaller->nonLazyScript()->staticScope());
+            computeThisBinding(maybeEvalCaller);
         else
             computeThisBinding(staticScope);
     }
 
-    StaticScope* staticScope() const override { return staticScope_; }
+    JSObject* staticScope() const override { return staticScope_; }
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
-    StaticFunctionScope* staticScope_;
+    JSObject*       enclosingStaticScope_;
     uint32_t        bufStart;
     uint32_t        bufEnd;
     uint32_t        startLine;
     uint32_t        startColumn;
     uint16_t        length;
 
     uint8_t         generatorKindBits_;     /* The GeneratorKind of this function. */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
@@ -330,25 +330,23 @@ class FunctionBox : public ObjectBox, pu
     bool            usesArguments:1;  /* contains a free use of 'arguments' */
     bool            usesApply:1;      /* contains an f.apply() call */
     bool            usesThis:1;       /* contains 'this' */
 
     FunctionContextFlags funCxFlags;
 
     template <typename ParseHandler>
     FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
-                ParseContext<ParseHandler>* pc, Directives directives, bool extraWarnings,
-                GeneratorKind generatorKind);
-
-    bool initStaticScope(Handle<StaticScope*> enclosingScope);
+                JSObject* enclosingStaticScope, ParseContext<ParseHandler>* pc,
+                Directives directives, bool extraWarnings, GeneratorKind generatorKind);
 
     ObjectBox* toObjectBox() override { return this; }
     JSFunction* function() const { return &object->as<JSFunction>(); }
-    StaticFunctionScope* staticScope() const override { return staticScope_; }
-    StaticScope* enclosingStaticScope() const { return staticScope_->enclosingScope(); }
+    JSObject* staticScope() const override { return function(); }
+    JSObject* enclosingStaticScope() const { return enclosingStaticScope_; }
 
     GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
     bool isGenerator() const { return generatorKind() != NotGenerator; }
     bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == StarGenerator; }
     bool isArrow() const { return function()->isArrow(); }
 
     void setGeneratorKind(GeneratorKind kind) {
@@ -424,17 +422,17 @@ class ModuleBox : public ObjectBox, publ
     ModuleBuilder& builder;
 
     template <typename ParseHandler>
     ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
               ModuleBuilder& builder, ParseContext<ParseHandler>* pc);
 
     ObjectBox* toObjectBox() override { return this; }
     ModuleObject* module() const { return &object->as<ModuleObject>(); }
-    StaticModuleScope* staticScope() const override { return module()->staticScope(); }
+    JSObject* staticScope() const override { return module(); }
 
     void trace(JSTracer* trc) override;
 };
 
 inline FunctionBox*
 SharedContext::asFunctionBox()
 {
     MOZ_ASSERT(isFunctionBox());
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -950,18 +950,18 @@ LazyScript::traceChildren(JSTracer* trc)
         TraceWeakEdge(trc, &script_, "script");
 
     if (function_)
         TraceEdge(trc, &function_, "function");
 
     if (sourceObject_)
         TraceEdge(trc, &sourceObject_, "sourceObject");
 
-    if (staticScope_)
-        TraceEdge(trc, &staticScope_, "staticScope");
+    if (enclosingScope_)
+        TraceEdge(trc, &enclosingScope_, "enclosingScope");
 
     // We rely on the fact that atoms are always tenured.
     FreeVariable* freeVariables = this->freeVariables();
     for (auto i : MakeRange(numFreeVariables())) {
         JSAtom* atom = freeVariables[i].atom();
         TraceManuallyBarrieredEdge(trc, &atom, "lazyScriptFreeVariable");
     }
 
@@ -976,18 +976,18 @@ js::GCMarker::eagerlyMarkChildren(LazySc
         noteWeakEdge(thing->script_.unsafeUnbarrieredForTracing());
 
     if (thing->function_)
         traverseEdge(thing, static_cast<JSObject*>(thing->function_));
 
     if (thing->sourceObject_)
         traverseEdge(thing, static_cast<JSObject*>(thing->sourceObject_));
 
-    if (thing->staticScope_)
-        traverseEdge(thing, static_cast<JSObject*>(thing->staticScope_));
+    if (thing->enclosingScope_)
+        traverseEdge(thing, static_cast<JSObject*>(thing->enclosingScope_));
 
     // We rely on the fact that atoms are always tenured.
     LazyScript::FreeVariable* freeVariables = thing->freeVariables();
     for (auto i : MakeRange(thing->numFreeVariables()))
         traverseEdge(thing, static_cast<JSString*>(freeVariables[i].atom()));
 
     HeapPtrFunction* innerFunctions = thing->innerFunctions();
     for (auto i : MakeRange(thing->numInnerFunctions()))
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -95,18 +95,16 @@ class JitCode;
     D(js::PlainObject*) \
     D(js::PropertyName*) \
     D(js::RegExpObject*) \
     D(js::SavedFrame*) \
     D(js::ScopeObject*) \
     D(js::ScriptSourceObject*) \
     D(js::Shape*) \
     D(js::SharedArrayBufferObject*) \
-    D(js::StaticFunctionScope*) \
-    D(js::StaticScope*) \
     D(js::StructTypeDescr*) \
     D(js::UnownedBaseShape*) \
     D(js::jit::JitCode*)
 
 // Expand the given macro D for each internal tagged GC pointer type.
 #define FOR_EACH_INTERNAL_TAGGED_GC_POINTER_TYPE(D) \
     D(js::TaggedProto)
 
deleted file mode 100644
--- a/js/src/jit-test/tests/gc/withStatementOffThread.js
+++ /dev/null
@@ -1,12 +0,0 @@
-if (helperThreadCount() == 0)
-   quit();
-
-var code = `
-function f() {
-    [function() { { function g() { } } },
-     function() { with ({}) { } }];
-}
-`;
-
-offThreadCompileScript(code);
-runOffThreadScript();
--- a/js/src/jit-test/tests/xdr/scope.js
+++ b/js/src/jit-test/tests/xdr/scope.js
@@ -1,63 +1,19 @@
 load(libdir + 'bytecode-cache.js');
 var test = "";
 
-// code a nested function after calling it
-test = `
-    function f() {
-        function g() {
-            return [f, g];
-        }
-        return g();
-    }
-    f()
-`;
-evalWithCache(test, {
-    assertEqBytecode: true,
-    checkAfter(ctx) {
-        let [f, g] = ctx.global.f();
-        assertEq(f, ctx.global.f);
-        assertEq(f()[0], f);
-        assertEq(f()[1] === g, false); // second call, fresh g closure
-        assertEq(f()[1].toString(), g.toString()); // but the same source code
-        assertEq(g()[0], f);
-        assertEq(g()[1], g);
-    }
-});
-
-// code an unused function that contains an unused nested function
-test = `
-    function f() {
-        function g() {
-            return [f, g];
-        }
-        return g;
-    }
-    f
-`;
-evalWithCache(test, {
-    assertEqBytecode: true,
-    checkAfter(ctx) {
-        let f = ctx.global.f;
-        let g = f();
-        let [f1, g1] = g();
-        assertEq(f1, f);
-        assertEq(g1, g);
-    }
-});
-
 // code a function which has both used and unused inner functions.
 test  = (function () {
   function f() {
     var x = 3;
     (function() {
       with(obj) {
         (function() {
           assertEq(x, 2);
         })();
       }
     })();
   };
 
   return "var obj = { x : 2 };" + f.toSource() + "; f()";
 })();
-evalWithCache(test, { assertEqBytecode: true, assertEqResult: true });
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3458,19 +3458,18 @@ CreateNonSyntacticScopeChain(JSContext* 
                              MutableHandle<StaticScope*> staticScopeObj)
 {
     Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
     if (!js::CreateScopeObjectsForScopeChain(cx, scopeChain, globalLexical, dynamicScopeObj))
         return false;
 
     staticScopeObj.set(&globalLexical->staticBlock());
     if (!scopeChain.empty()) {
-        Rooted<StaticNonSyntacticScope*> scope(cx,
-            StaticNonSyntacticScope::create(cx, staticScopeObj));
-        if (!scope)
+        staticScopeObj.set(StaticNonSyntacticScope::create(cx, staticScopeObj));
+        if (!staticScopeObj)
             return false;
 
         // The XPConnect subscript loader, which may pass in its own dynamic
         // scopes to load scripts in, expects the dynamic scope chain to be
         // the holder of "var" declarations. In SpiderMonkey, such objects are
         // called "qualified varobjs", the "qualified" part meaning the
         // declaration was qualified by "var". There is only sadness.
         //
@@ -3481,52 +3480,51 @@ CreateNonSyntacticScopeChain(JSContext* 
         // Also get a non-syntactic lexical scope to capture 'let' and 'const'
         // bindings. To persist lexical bindings, we have a 1-1 mapping with
         // the final unwrapped dynamic scope object (the scope that stores the
         // 'var' bindings) and the lexical scope.
         //
         // TODOshu: disallow the subscript loader from using non-distinguished
         // objects as dynamic scopes.
         dynamicScopeObj.set(
-            cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, scope, dynamicScopeObj));
+            cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, staticScopeObj,
+                                                                   dynamicScopeObj));
         if (!dynamicScopeObj)
             return false;
-
-        staticScopeObj.set(scope);
     }
 
     return true;
 }
 
 static bool
 IsFunctionCloneable(HandleFunction fun)
 {
     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 (StaticScope* scope = fun->nonLazyScript()->enclosingStaticScope()) {
+    if (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) {
         // If the script is directly under the global scope, we can clone it.
         if (IsStaticGlobalLexicalScope(scope))
             return true;
 
         // If the script already deals with non-syntactic scopes, we can clone
         // it.
         if (scope->is<StaticNonSyntacticScope>())
             return true;
 
         // 'eval' scopes are always scoped immediately under a non-extensible
         // lexical scope.
         if (scope->is<StaticBlockScope>()) {
             StaticBlockScope& block = scope->as<StaticBlockScope>();
             if (block.needsClone())
                 return false;
 
-            StaticScope* enclosing = block.enclosingScope();
+            JSObject* enclosing = block.enclosingStaticScope();
 
             // If the script is an indirect eval that is immediately scoped
             // under the global, we can clone it.
             if (enclosing->is<StaticEvalScope>())
                 return !enclosing->as<StaticEvalScope>().isNonGlobal();
         }
 
         // Any other enclosing static scope (e.g., function, block) cannot be
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -500,17 +500,17 @@ JSCompartment::wrap(JSContext* cx, Mutab
             return false;
     }
 
     return wrap(cx, desc.value());
 }
 
 ClonedBlockObject*
 JSCompartment::getOrCreateNonSyntacticLexicalScope(JSContext* cx,
-                                                   Handle<StaticNonSyntacticScope*> enclosingStatic,
+                                                   HandleObject enclosingStatic,
                                                    HandleObject enclosingScope)
 {
     if (!nonSyntacticLexicalScopes_) {
         nonSyntacticLexicalScopes_ = cx->new_<ObjectWeakMap>(cx);
         if (!nonSyntacticLexicalScopes_ || !nonSyntacticLexicalScopes_->init())
             return nullptr;
     }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -27,17 +27,16 @@ class JitCompartment;
 } // namespace jit
 
 namespace gc {
 template<class Node> class ComponentFinder;
 } // namespace gc
 
 struct NativeIterator;
 class ClonedBlockObject;
-class StaticNonSyntacticScope;
 
 /*
  * A single-entry cache for some base-10 double-to-string conversions. This
  * helps date-format-xparb.js.  It also avoids skewing the results for
  * v8-splay.js when measured by the SunSpider harness, where the splay tree
  * initialization (which includes many repeated double-to-string conversions)
  * is erroneously included in the measurement; see bug 562553.
  */
@@ -527,20 +526,19 @@ struct JSCompartment
     void removeWrapper(js::WrapperMap::Ptr p) {
         crossCompartmentWrappers.remove(p);
     }
 
     struct WrapperEnum : public js::WrapperMap::Enum {
         explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
     };
 
-    js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(
-        JSContext* cx,
-        js::Handle<js::StaticNonSyntacticScope*> enclosingStatic,
-        js::HandleObject enclosingScope);
+    js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(JSContext* cx,
+                                                               js::HandleObject enclosingStatic,
+                                                               js::HandleObject enclosingScope);
     js::ClonedBlockObject* getNonSyntacticLexicalScope(JSObject* enclosingScope) const;
 
     /*
      * This method traces data that is live iff we know that this compartment's
      * global is still live.
      */
     void trace(JSTracer* trc);
     /*
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -400,19 +400,19 @@ js::GetOutermostEnclosingFunctionOfScrip
 
     if (iter.done())
         return nullptr;
 
     if (!iter.isFunctionFrame())
         return nullptr;
 
     RootedFunction curr(cx, iter.callee(cx));
-    for (StaticScopeIter<NoGC> i(curr->nonLazyScript()->staticScope()); !i.done(); i++) {
+    for (StaticScopeIter<NoGC> i(curr); !i.done(); i++) {
         if (i.type() == StaticScopeIter<NoGC>::Function)
-            curr = &i.fun().function();
+            curr = &i.fun();
     }
     return curr;
 }
 
 JS_FRIEND_API(JSFunction*)
 js::DefineFunctionWithReserved(JSContext* cx, JSObject* objArg, const char* name, JSNative call,
                                unsigned nargs, unsigned attrs)
 {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -37,17 +37,16 @@
 #include "jit/InlinableNatives.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/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
@@ -531,18 +530,18 @@ fun_resolve(JSContext* cx, HandleObject 
         return true;
     }
 
     return true;
 }
 
 template<XDRMode mode>
 bool
-js::XDRInterpretedFunction(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
-                           HandleScript enclosingScript, MutableHandleFunction objp)
+js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+                           MutableHandleFunction objp)
 {
     enum FirstWordFlag {
         HasAtom             = 0x1,
         IsStarGenerator     = 0x2,
         IsLazy              = 0x4,
         HasSingletonType    = 0x8
     };
 
@@ -648,22 +647,20 @@ js::XDRInterpretedFunction(XDRState<mode
             return false;
         objp.set(fun);
     }
 
     return true;
 }
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript,
-                           MutableHandleFunction);
+js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, MutableHandleFunction);
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript,
-                           MutableHandleFunction);
+js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleObject, HandleScript, MutableHandleFunction);
 
 /*
  * [[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)
@@ -749,24 +746,25 @@ CreateFunctionConstructor(JSContext* cx,
     return functionCtor;
 
 }
 
 static JSObject*
 CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> self(cx, cx->global());
+
     RootedObject objectProto(cx, &self->getPrototype(JSProto_Object).toObject());
-
-    // Bizarrely, |Function.prototype| must be an interpreted function, so
-    // give it the guts to be one.
-    Rooted<ClonedBlockObject*> globalLexicalEnv(cx, &self->lexicalScope());
+    /*
+     * Bizarrely, |Function.prototype| must be an interpreted function, so
+     * give it the guts to be one.
+     */
     JSObject* functionProto_ =
         NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
-                             globalLexicalEnv, nullptr, objectProto, AllocKind::FUNCTION,
+                             self, nullptr, objectProto, AllocKind::FUNCTION,
                              SingletonObject);
     if (!functionProto_)
         return nullptr;
 
     RootedFunction functionProto(cx, &functionProto_->as<JSFunction>());
 
     const char* rawSource = "() {\n}";
     size_t sourceLen = strlen(rawSource);
@@ -784,22 +782,18 @@ CreateFunctionPrototype(JSContext* cx, J
     ss->setSource(source, sourceLen);
     CompileOptions options(cx);
     options.setNoScriptRval(true)
            .setVersion(JSVERSION_DEFAULT);
     RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss));
     if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options))
         return nullptr;
 
-    Rooted<StaticScope*> globalScope(cx, &globalLexicalEnv->staticBlock());
-    Rooted<StaticScope*> funScope(cx, StaticFunctionScope::create(cx, functionProto, globalScope));
-    if (!funScope)
-        return nullptr;
     RootedScript script(cx, JSScript::Create(cx,
-                                             funScope,
+                                             /* enclosingScope = */ nullptr,
                                              /* savedCallerFun = */ false,
                                              options,
                                              sourceObject,
                                              0,
                                              ss->length()));
     if (!script || !JSScript::fullyInitTrivial(cx, script))
         return nullptr;
 
@@ -1446,17 +1440,17 @@ JSFunction::createScriptForLazilyInterpr
         // GCs, to avoid resurrecting dead scripts after incremental sweeping
         // has started.
         if (canRelazify && !JS::IsIncrementalGCInProgress(cx->runtime())) {
             LazyScriptCache::Lookup lookup(cx, lazy);
             cx->runtime()->lazyScriptCache.lookup(lookup, script.address());
         }
 
         if (script) {
-            Rooted<StaticScope*> enclosingScope(cx, lazy->enclosingScope());
+            RootedObject enclosingScope(cx, lazy->enclosingScope());
             RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
             if (!clonedScript)
                 return false;
 
             clonedScript->setSourceObject(lazy->sourceObject());
 
             fun->initAtom(script->functionNonDelazifying()->displayAtom());
 
@@ -2053,17 +2047,17 @@ js::CloneFunctionReuseScript(JSContext* 
      */
     if (fun->getProto() == clone->getProto())
         clone->setGroup(fun->group());
     return clone;
 }
 
 JSFunction*
 js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                           Handle<StaticScope*> newStaticScope,
+                           HandleObject newStaticScope,
                            gc::AllocKind allocKind /* = FUNCTION */,
                            HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(NewFunctionScopeIsWellFormed(cx, parent));
     MOZ_ASSERT(!fun->isBoundFunction());
 
     JSScript::AutoDelazify funScript(cx);
     if (fun->isInterpreted()) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -723,17 +723,17 @@ 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,
-                       Handle<StaticScope*> newStaticScope,
+                       HandleObject newStaticScope,
                        gc::AllocKind kind = gc::AllocKind::FUNCTION,
                        HandleObject proto = nullptr);
 
 extern bool
 FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
          size_t* bodyEnd);
 
 } // namespace js
@@ -784,17 +784,17 @@ JSFunction::getExtendedSlot(size_t which
 }
 
 namespace js {
 
 JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen);
 
 template<XDRMode mode>
 bool
-XDRInterpretedFunction(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope,
                        HandleScript enclosingScript, MutableHandleFunction objp);
 
 /*
  * 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
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -87,15 +87,15 @@ CloneFunctionObjectIfNotSingleton(JSCont
                          : finalizeKind;
 
     if (CanReuseScriptForClone(cx->compartment(), fun, parent))
         return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
 
     RootedScript script(cx, fun->getOrCreateScript(cx));
     if (!script)
         return nullptr;
-    Rooted<StaticScope*> staticScope(cx, script->enclosingStaticScope());
+    RootedObject staticScope(cx, script->enclosingStaticScope());
     return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto);
 }
 
 } /* namespace js */
 
 #endif /* jsfuninlines_h */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -6853,17 +6853,17 @@ gc::MergeCompartments(JSCompartment* sou
     // be in the source zone
     rt->gc.releaseHeldRelocatedArenas();
 
     // Fixup compartment pointers in source to refer to target, and make sure
     // type information generations are in sync.
 
     // Get the static global lexical scope of the target compartment. Static
     // scopes need to be fixed up below.
-    Rooted<StaticScope*> targetStaticGlobalLexicalScope(rt);
+    RootedObject targetStaticGlobalLexicalScope(rt);
     targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock();
 
     for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) {
         JSScript* script = iter.get<JSScript>();
         MOZ_ASSERT(script->compartment() == source);
         script->compartment_ = target;
         script->setTypesGeneration(target->zone()->types.generation);
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -511,17 +511,17 @@ XDRLazyFreeVariables(XDRState<mode>* xdr
 
     return true;
 }
 
 // Code the missing part needed to re-create a LazyScript from a JSScript.
 template<XDRMode mode>
 static bool
 XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript script,
-                      Handle<StaticFunctionScope*> funScope, MutableHandle<LazyScript*> lazy)
+                      HandleObject enclosingScope, MutableHandle<LazyScript*> lazy)
 {
     MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
     MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
 
     JSContext* cx = xdr->cx();
 
     uint64_t packedFields;
     {
@@ -541,18 +541,18 @@ XDRRelazificationInfo(XDRState<mode>* xd
             // JSFunction::createScriptForLazilyInterpretedFunction.
             MOZ_ASSERT(lazy->numInnerFunctions() == 0);
         }
 
         if (!xdr->codeUint64(&packedFields))
             return false;
 
         if (mode == XDR_DECODE) {
-            lazy.set(LazyScript::Create(cx, fun, script, funScope, script, packedFields,
-                                        begin, end, lineno, column));
+            lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
+                                        packedFields, begin, end, lineno, column));
 
             // As opposed to XDRLazyScript, we need to restore the runtime bits
             // of the script, as we are trying to match the fact this function
             // has already been parsed and that it would need to be re-lazified.
             lazy->initRuntimeFields(packedFields);
         }
     }
 
@@ -588,18 +588,18 @@ enum XDRClassKind {
     CK_WithObject   = 1,
     CK_RegexpObject = 2,
     CK_JSFunction   = 3,
     CK_JSObject     = 4
 };
 
 template<XDRMode mode>
 bool
-js::XDRScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScopeArg,
-              HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp)
+js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript,
+              HandleFunction fun, MutableHandleScript scriptp)
 {
     /* NB: Keep this in sync with CopyScript. */
 
     MOZ_ASSERT(enclosingScopeArg);
 
     enum ScriptBits {
         NoScriptRval,
         SavedCallerFun,
@@ -633,17 +633,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
     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);
-    Rooted<StaticScope*> enclosingScope(cx, enclosingScopeArg);
+    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;
@@ -841,22 +841,16 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (scriptBits & (1 << HasNonSyntacticScope) &&
             IsStaticGlobalLexicalScope(enclosingScope))
         {
             enclosingScope = StaticNonSyntacticScope::create(cx, enclosingScope);
             if (!enclosingScope)
                 return false;
         }
 
-        if (fun) {
-            enclosingScope = StaticFunctionScope::create(cx, fun, enclosingScope);
-            if (!enclosingScope)
-                return false;
-        }
-
         script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
                                   options, 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) {
@@ -1045,43 +1039,38 @@ js::XDRScript(XDRState<mode>* xdr, Handl
                     else
                         enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
                 } else {
                     enclosingStaticScopeIndex = UINT32_MAX;
                 }
             }
             if (!xdr->codeUint32(&enclosingStaticScopeIndex))
                 return false;
-            Rooted<StaticScope*> enclosingStaticScope(cx);
+            Rooted<JSObject*> enclosingStaticScope(cx);
             if (mode == XDR_DECODE) {
                 if (enclosingStaticScopeIndex != UINT32_MAX) {
                     MOZ_ASSERT(enclosingStaticScopeIndex < i);
-                    enclosingStaticScope = &script->objects()->vector[enclosingStaticScopeIndex]
-                                                             ->as<StaticScope>();
+                    enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
                 } else {
                     // This is not ternary because MSVC can't typecheck the
                     // ternary.
                     if (fun)
-                        enclosingStaticScope = script->staticScope();
+                        enclosingStaticScope = fun;
                     else
                         enclosingStaticScope = enclosingScope;
                 }
             }
 
             if (classk == CK_BlockObject) {
-                Rooted<StaticBlockScope*> tmp(cx);
-                if (mode == XDR_ENCODE)
-                    tmp = &(*objp)->as<StaticBlockScope>();
+                Rooted<StaticBlockScope*> tmp(cx, static_cast<StaticBlockScope*>(objp->get()));
                 if (!XDRStaticBlockScope(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
             } else {
-                Rooted<StaticWithScope*> tmp(cx);
-                if (mode == XDR_ENCODE)
-                    tmp = &(*objp)->as<StaticWithScope>();
+                Rooted<StaticWithScope*> tmp(cx, static_cast<StaticWithScope*>(objp->get()));
                 if (!XDRStaticWithScope(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
             }
             break;
           }
 
           case CK_RegexpObject: {
@@ -1093,17 +1082,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             if (mode == XDR_DECODE)
                 *objp = regexp;
             break;
           }
 
           case CK_JSFunction: {
             /* Code the nested function's enclosing scope. */
             uint32_t funEnclosingScopeIndex = 0;
-            Rooted<StaticScope*> funEnclosingScope(cx);
+            RootedObject funEnclosingScope(cx);
             if (mode == XDR_ENCODE) {
                 RootedFunction function(cx, &(*objp)->as<JSFunction>());
 
                 if (function->isInterpretedLazy())
                     funEnclosingScope = function->lazyScript()->enclosingScope();
                 else if (function->isInterpreted())
                     funEnclosingScope = function->nonLazyScript()->enclosingStaticScope();
                 else {
@@ -1140,23 +1129,22 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             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 = script->staticScope();
+                        funEnclosingScope = fun;
                     else
                         funEnclosingScope = enclosingScope;
                 } else {
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
-                    funEnclosingScope = &script->objects()->vector[funEnclosingScopeIndex]
-                                                          .get()->as<StaticScope>();
+                    funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
                 }
             }
 
             // Code nested function and script.
             RootedFunction tmp(cx);
             if (mode == XDR_ENCODE)
                 tmp = &(*objp)->as<JSFunction>();
             if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
@@ -1213,20 +1201,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             return false;
     }
 
     if (scriptBits & (1 << HasLazyScript)) {
         Rooted<LazyScript*> lazy(cx);
         if (mode == XDR_ENCODE)
             lazy = script->maybeLazyScript();
 
-        Rooted<StaticFunctionScope*> lazyScope(cx, mode == XDR_DECODE
-                                                   ? &enclosingScope->as<StaticFunctionScope>()
-                                                   : nullptr);
-        if (!XDRRelazificationInfo(xdr, fun, script, lazyScope, &lazy))
+        if (!XDRRelazificationInfo(xdr, fun, script, enclosingScope, &lazy))
             return false;
 
         if (mode == XDR_DECODE)
             script->setLazyScript(lazy);
     }
 
     if (mode == XDR_DECODE) {
         scriptp.set(script);
@@ -1235,28 +1220,27 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (!fun)
             Debugger::onNewScript(cx, script);
     }
 
     return true;
 }
 
 template bool
-js::XDRScript(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript, HandleFunction,
+js::XDRScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, HandleFunction,
               MutableHandleScript);
 
 template bool
-js::XDRScript(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript, HandleFunction,
+js::XDRScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript, HandleFunction,
               MutableHandleScript);
 
 template<XDRMode mode>
 bool
-js::XDRLazyScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
-                  HandleScript enclosingScript, HandleFunction fun,
-                  MutableHandle<LazyScript*> lazy)
+js::XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+                  HandleFunction fun, MutableHandle<LazyScript*> lazy)
 {
     JSContext* cx = xdr->cx();
 
     {
         uint32_t begin;
         uint32_t end;
         uint32_t lineno;
         uint32_t column;
@@ -1279,59 +1263,54 @@ 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) {
-            Rooted<StaticFunctionScope*> funScope(cx,
-                StaticFunctionScope::create(cx, fun, enclosingScope));
-            if (!funScope)
-                return false;
-            lazy.set(LazyScript::Create(cx, fun, nullptr, funScope, enclosingScript,
+            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.
     {
         RootedFunction func(cx);
-        Rooted<StaticFunctionScope*> funScope(cx, lazy->staticScope());
         HeapPtrFunction* innerFunctions = lazy->innerFunctions();
         size_t numInnerFunctions = lazy->numInnerFunctions();
         for (size_t i = 0; i < numInnerFunctions; i++) {
             if (mode == XDR_ENCODE)
                 func = innerFunctions[i];
 
-            if (!XDRInterpretedFunction(xdr, funScope, enclosingScript, &func))
+            if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func))
                 return false;
 
             if (mode == XDR_DECODE)
                 innerFunctions[i] = func;
         }
     }
 
     return true;
 }
 
 template bool
-js::XDRLazyScript(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript,
+js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript,
                   HandleFunction, MutableHandle<LazyScript*>);
 
 template bool
-js::XDRLazyScript(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript,
+js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript,
                   HandleFunction, MutableHandle<LazyScript*>);
 
 void
 JSScript::setSourceObject(JSObject* object)
 {
     MOZ_ASSERT(compartment() == object->compartment());
     sourceObject_ = object;
 }
@@ -2761,43 +2740,41 @@ ScriptDataSize(uint32_t nbindings, uint3
 
 void
 JSScript::initCompartment(ExclusiveContext* cx)
 {
     compartment_ = cx->compartment_;
 }
 
 /* static */ JSScript*
-JSScript::Create(ExclusiveContext* cx, Handle<StaticScope*> staticScope, bool savedCallerFun,
+JSScript::Create(ExclusiveContext* cx, HandleObject enclosingScope, bool savedCallerFun,
                  const ReadOnlyCompileOptions& options, HandleObject sourceObject,
                  uint32_t bufStart, uint32_t bufEnd)
 {
     MOZ_ASSERT(bufStart <= bufEnd);
 
     RootedScript script(cx, Allocate<JSScript>(cx));
     if (!script)
         return nullptr;
 
     PodZero(script.get());
     new (&script->bindings) Bindings;
 
-    script->staticScope_ = staticScope;
+    script->enclosingStaticScope_ = enclosingScope;
     script->savedCallerFun_ = savedCallerFun;
     script->initCompartment(cx);
 
     script->selfHosted_ = options.selfHostingMode;
     script->noScriptRval_ = options.noScriptRval;
     script->treatAsRunOnce_ = options.isRunOnce;
 
-    // Compute whether this script is under a non-syntactic scope, passing
-    // staticScope->enclosingScope() in a case where staticScope itself is not
-    // a non-syntactic scope and may not be fully initialized yet.
-    Rooted<StaticScope*> enclosingScope(cx, staticScope);
-    if (staticScope && staticScope->is<StaticFunctionScope>())
-        enclosingScope = staticScope->enclosingScope();
+    // 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
 
     script->setSourceObject(sourceObject);
     script->sourceStart_ = bufStart;
     script->sourceEnd_ = bufEnd;
@@ -3126,30 +3103,25 @@ js::GlobalObject&
 JSScript::uninlinedGlobal() const
 {
     return global();
 }
 
 void
 JSScript::fixEnclosingStaticGlobalLexicalScope()
 {
-    if (function_) {
-        MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_->enclosingScope()));
-        staticScope_->setEnclosingScope(&global().lexicalScope().staticBlock());
-    } else {
-        MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_));
-        staticScope_ = &global().lexicalScope().staticBlock();
-    }
+    MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingStaticScope_));
+    enclosingStaticScope_ = &global().lexicalScope().staticBlock();
 }
 
 void
 LazyScript::fixEnclosingStaticGlobalLexicalScope()
 {
-    MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope_->enclosingScope()));
-    staticScope_->setEnclosingScope(&function_->global().lexicalScope().staticBlock());
+    MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingScope_));
+    enclosingScope_ = &function_->global().lexicalScope().staticBlock();
 }
 
 void
 JSScript::finalize(FreeOp* fop)
 {
     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
     // may have created it and partially initialized it with
     // JSScript::Create(), but not yet finished initializing it with
@@ -3417,18 +3389,17 @@ 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, Handle<StaticScope*> enclosingScope,
-                              HandleFunction srcFun)
+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;
     }
@@ -3458,17 +3429,17 @@ CloneInnerInterpretedFunction(JSContext*
 
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return nullptr;
 
     return clone;
 }
 
 bool
-js::detail::CopyScript(JSContext* cx, Handle<StaticScope*> scriptStaticScope, HandleScript src,
+js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
                        HandleScript dst)
 {
     if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
         // Toplevel run-once scripts may not be cloned.
         JS_ReportError(cx, "No cloning toplevel run-once scripts");
         return false;
     }
 
@@ -3504,25 +3475,24 @@ js::detail::CopyScript(JSContext* cx, Ha
     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<NestedStaticScope>()) {
                 Rooted<NestedStaticScope*> innerBlock(cx, &obj->as<NestedStaticScope>());
 
-                Rooted<StaticScope*> enclosingScope(cx);
+                RootedObject enclosingScope(cx);
                 if (NestedStaticScope* enclosingBlock = innerBlock->enclosingNestedScope()) {
                     if (IsStaticGlobalLexicalScope(enclosingBlock)) {
                         MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) ||
                                    scriptStaticScope->is<StaticNonSyntacticScope>());
                         enclosingScope = scriptStaticScope;
                     } else {
-                        enclosingScope = &objects[FindScopeObjectIndex(src, *enclosingBlock)]
-                                                 .get()->as<StaticScope>();
+                        enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)];
                     }
                 } else {
                     enclosingScope = scriptStaticScope;
                 }
 
                 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
 
             } else if (obj->is<RegExpObject>()) {
@@ -3538,37 +3508,34 @@ js::detail::CopyScript(JSContext* cx, Ha
                     }
                     clone = innerFun;
                 } else {
                     if (innerFun->isInterpretedLazy()) {
                         AutoCompartment ac(cx, innerFun);
                         if (!innerFun->getOrCreateScript(cx))
                             return false;
                     }
-                    Rooted<StaticScope*> staticScope(cx, innerFun->nonLazyScript()
-                                                                 ->enclosingStaticScope());
+                    RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
                     StaticScopeIter<CanGC> ssi(cx, staticScope);
-                    Rooted<StaticScope*> enclosingScope(cx);
+                    RootedObject enclosingScope(cx);
                     if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::NonSyntactic) {
                         enclosingScope = scriptStaticScope;
                     } else if (ssi.type() == StaticScopeIter<CanGC>::Function) {
-                        MOZ_ASSERT(scriptStaticScope->is<StaticFunctionScope>());
+                        MOZ_ASSERT(scriptStaticScope->is<JSFunction>());
                         enclosingScope = scriptStaticScope;
                     } else if (ssi.type() == StaticScopeIter<CanGC>::Block) {
                         if (ssi.block().isGlobal()) {
                             MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) ||
                                        scriptStaticScope->is<StaticNonSyntacticScope>());
                             enclosingScope = scriptStaticScope;
                         } else {
-                            enclosingScope = &objects[FindScopeObjectIndex(src, ssi.block())]
-                                                     .get()->as<StaticBlockScope>();
+                            enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
                         }
                     } else {
-                        enclosingScope = &objects[FindScopeObjectIndex(src, ssi.staticWith())]
-                                                 .get()->as<StaticWithScope>();
+                        enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
                     }
 
                     clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun);
                 }
 
             } else {
                 clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
             }
@@ -3656,17 +3623,17 @@ js::detail::CopyScript(JSContext* cx, Ha
      * 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, Handle<StaticScope*> enclosingScope, HandleScript src)
+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.
      */
     RootedObject sourceObject(cx);
     if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
@@ -3708,49 +3675,44 @@ js::CloneGlobalScript(JSContext* cx, Han
 
     if (!detail::CopyScript(cx, enclosingScope, src, dst))
         return nullptr;
 
     return dst;
 }
 
 JSScript*
-js::CloneScriptIntoFunction(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleFunction fun,
+js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
                             HandleScript src)
 {
     MOZ_ASSERT(fun->isInterpreted());
 
-    Rooted<StaticFunctionScope*> funScope(cx, StaticFunctionScope::create(cx, fun,
-                                                                          enclosingScope));
-    if (!funScope)
-        return nullptr;
-
     // 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, funScope, src));
+    RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
     if (!dst)
         return nullptr;
 
     // Save flags in case we need to undo the early mutations.
     const int preservedFlags = fun->flags();
 
     dst->setFunction(fun);
     Rooted<LazyScript*> lazy(cx);
     if (fun->isInterpretedLazy()) {
         lazy = fun->lazyScriptOrNull();
         fun->setUnlazifiedScript(dst);
     } else {
         fun->initScript(dst);
     }
 
-    if (!detail::CopyScript(cx, funScope, src, dst)) {
+    if (!detail::CopyScript(cx, fun, src, dst)) {
         if (lazy)
             fun->initLazyScript(lazy);
         else
             fun->setScript(nullptr);
         fun->setFlags(preservedFlags);
         return nullptr;
     }
 
@@ -3980,18 +3942,18 @@ JSScript::traceChildren(JSTracer* trc)
     }
 
     if (functionNonDelazifying())
         TraceEdge(trc, &function_, "function");
 
     if (module_)
         TraceEdge(trc, &module_, "module");
 
-    if (staticScope_)
-        TraceEdge(trc, &staticScope_, "staticScope");
+    if (enclosingStaticScope_)
+        TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope");
 
     if (maybeLazyScript())
         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
 
     if (trc->isMarkingTracer()) {
         compartment()->mark();
 
         if (code())
@@ -4083,28 +4045,30 @@ JSScript::getStaticBlockScope(jsbytecode
         } else {
             top = mid;
         }
     }
 
     return blockChain;
 }
 
-StaticScope*
+JSObject*
 JSScript::innermostStaticScopeInScript(jsbytecode* pc)
 {
-    if (NestedStaticScope* scope = getStaticBlockScope(pc))
+    if (JSObject* scope = getStaticBlockScope(pc))
         return scope;
-    return staticScope_;
+    if (module())
+        return module();
+    return functionNonDelazifying();
 }
 
-StaticScope*
+JSObject*
 JSScript::innermostStaticScope(jsbytecode* pc)
 {
-    if (StaticScope* scope = innermostStaticScopeInScript(pc))
+    if (JSObject* scope = innermostStaticScopeInScript(pc))
         return scope;
     return enclosingStaticScope();
 }
 
 void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
@@ -4139,23 +4103,18 @@ js::SetFrameArgumentsObject(JSContext* c
         while (*pc != JSOP_ARGUMENTS)
             pc += GetBytecodeLength(pc);
         pc += JSOP_ARGUMENTS_LENGTH;
         MOZ_ASSERT(*pc == JSOP_SETALIASEDVAR);
 
         // Note that here and below, it is insufficient to only check for
         // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
         // arguments slot.
-        if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>()
-                                                  .aliasedVar(ScopeCoordinate(pc))))
-        {
-            frame.callObj().as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc),
-                                                            cx->names().arguments,
-                                                            ObjectValue(*argsobj));
-        }
+        if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(ScopeCoordinate(pc))))
+            frame.callObj().as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc), cx->names().arguments, ObjectValue(*argsobj));
     } else {
         if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(bi.frameIndex())))
             frame.unaliasedLocal(bi.frameIndex()) = ObjectValue(*argsobj);
     }
 }
 
 /* static */ bool
 JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
@@ -4243,22 +4202,20 @@ JSScript::localIsAliased(unsigned localS
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
 
-LazyScript::LazyScript(JSFunction* fun, StaticFunctionScope* funScope, void* table,
-                       uint64_t packedFields, uint32_t begin, uint32_t end,
-                       uint32_t lineno, uint32_t column)
+LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
   : script_(nullptr),
     function_(fun),
-    staticScope_(funScope),
+    enclosingScope_(nullptr),
     sourceObject_(nullptr),
     table_(table),
     packedFields_(packedFields),
     begin_(begin),
     end_(end),
     lineno_(lineno),
     column_(column)
 {
@@ -4276,20 +4233,23 @@ LazyScript::initScript(JSScript* script)
 void
 LazyScript::resetScript()
 {
     MOZ_ASSERT(script_.unbarrieredGet());
     script_.set(nullptr);
 }
 
 void
-LazyScript::initSource(ScriptSourceObject* sourceObject)
+LazyScript::setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject)
 {
-    MOZ_ASSERT(!sourceObject_);
+    MOZ_ASSERT(!sourceObject_ && !enclosingScope_);
+    MOZ_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment());
     MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
+
+    enclosingScope_ = enclosingScope;
     sourceObject_ = sourceObject;
 }
 
 ScriptSourceObject*
 LazyScript::sourceObject() const
 {
     return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr;
 }
@@ -4297,17 +4257,16 @@ LazyScript::sourceObject() const
 ScriptSource*
 LazyScript::maybeForwardedScriptSource() const
 {
     return UncheckedUnwrap(MaybeForwarded(sourceObject()))->as<ScriptSourceObject>().source();
 }
 
 /* static */ LazyScript*
 LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
-                      Handle<StaticFunctionScope*> funScope,
                       uint64_t packedFields, uint32_t begin, uint32_t end,
                       uint32_t lineno, uint32_t column)
 {
     union {
         PackedView p;
         uint64_t packed;
     };
 
@@ -4327,22 +4286,21 @@ LazyScript::CreateRaw(ExclusiveContext* 
     }
 
     LazyScript* res = Allocate<LazyScript>(cx);
     if (!res)
         return nullptr;
 
     cx->compartment()->scheduleDelazificationForDebugger();
 
-    return new (res) LazyScript(fun, funScope, table.forget(), packed, begin, end, lineno, column);
+    return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column);
 }
 
 /* static */ LazyScript*
 LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
-                      Handle<StaticFunctionScope*> funScope,
                       uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version,
                       uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
 {
     union {
         PackedView p;
         uint64_t packedFields;
     };
 
@@ -4353,38 +4311,36 @@ LazyScript::CreateRaw(ExclusiveContext* 
     p.strict = false;
     p.bindingsAccessedDynamically = false;
     p.hasDebuggerStatement = false;
     p.hasDirectEval = false;
     p.usesArgumentsApplyAndThis = false;
     p.isDerivedClassConstructor = false;
     p.needsHomeObject = false;
 
-    LazyScript* res = LazyScript::CreateRaw(cx, fun, funScope, packedFields,
-                                            begin, end, lineno, column);
+    LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
     MOZ_ASSERT_IF(res, res->version() == version);
     return res;
 }
 
 /* static */ LazyScript*
 LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
-                   HandleScript script, Handle<StaticFunctionScope*> funScope,
+                   HandleScript script, HandleObject enclosingScope,
                    HandleScript sourceObjectScript,
                    uint64_t packedFields, uint32_t begin, uint32_t end,
                    uint32_t lineno, uint32_t column)
 {
     // Dummy atom which is not a valid property name.
     RootedAtom dummyAtom(cx, cx->names().comma);
 
     // Dummy function which is not a valid function as this is the one which is
     // holding this lazy script.
     HandleFunction dummyFun = fun;
 
-    LazyScript* res = LazyScript::CreateRaw(cx, fun, funScope, packedFields,
-                                            begin, end, lineno, column);
+    LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
     if (!res)
         return nullptr;
 
     // Fill with dummies, to be GC-safe after the initialization of the free
     // variables and inner functions.
     size_t i, num;
     FreeVariable* variables = res->freeVariables();
     for (i = 0, num = res->numFreeVariables(); i < num; i++)
@@ -4392,17 +4348,17 @@ LazyScript::Create(ExclusiveContext* cx,
 
     HeapPtrFunction* functions = res->innerFunctions();
     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
         functions[i].init(dummyFun);
 
     // Set the enclosing scope of the lazy function, this would later be
     // used to define the environment when the function would be used.
     MOZ_ASSERT(!res->sourceObject());
-    res->initSource(&sourceObjectScript->scriptSourceUnwrap());
+    res->setParent(enclosingScope, &sourceObjectScript->scriptSourceUnwrap());
 
     MOZ_ASSERT(!res->hasScript());
     if (script)
         res->initScript(script);
 
     return res;
 }
 
@@ -4425,20 +4381,20 @@ LazyScript::hasUncompiledEnclosingScript
     // It can happen that we created lazy scripts while compiling an enclosing
     // script, but we errored out while compiling that script. When we iterate
     // over lazy script in a compartment, we might see lazy scripts that never
     // escaped to script and should be ignored.
     //
     // If the enclosing scope is a function with a null script or has a script
     // without code, it was not successfully compiled.
 
-    if (!enclosingScope() || !enclosingScope()->is<StaticFunctionScope>())
+    if (!enclosingScope() || !enclosingScope()->is<JSFunction>())
         return false;
 
-    JSFunction& fun = enclosingScope()->as<StaticFunctionScope>().function();
+    JSFunction& fun = enclosingScope()->as<JSFunction>();
     return !fun.hasScript() || fun.hasUncompiledScript() || !fun.nonLazyScript()->code();
 }
 
 void
 JSScript::updateBaselineOrIonRaw(JSContext* maybecx)
 {
     if (hasBaselineScript() && baseline->hasPendingIonBuilder()) {
         MOZ_ASSERT(maybecx);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -45,35 +45,33 @@ namespace jit {
 
 class BreakpointSite;
 class BindingIter;
 class Debugger;
 class LazyScript;
 class ModuleObject;
 class NestedStaticScope;
 class StaticScope;
-class StaticFunctionScope;
 class RegExpObject;
 struct SourceCompressionTask;
 class Shape;
 
 namespace frontend {
     struct BytecodeEmitter;
     class UpvarCookie;
     class FunctionBox;
     class ModuleBox;
 } // namespace frontend
 
 namespace detail {
 
 // Do not call this directly! It is exposed for the friend declarations in
 // this file.
 bool
-CopyScript(JSContext* cx, Handle<StaticScope*> scriptStaticScope, HandleScript src,
-           HandleScript dst);
+CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst);
 
 } // namespace detail
 
 } // namespace js
 
 /*
  * 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
@@ -143,17 +141,17 @@ 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, Handle<StaticScope*> scriptStaticScope, HandleScript src,
+    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;
@@ -917,47 +915,47 @@ GeneratorKindFromBits(unsigned val) {
     return static_cast<GeneratorKind>(val);
 }
 
 /*
  * NB: after a successful XDR_DECODE, XDRScript callers must do any required
  * subsequent set-up of owning function or script object and then call
  * CallNewScriptHook.
  */
-template <XDRMode mode>
+template<XDRMode mode>
 bool
-XDRScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
-          HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp);
-
-template <XDRMode mode>
+XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+          HandleFunction fun, MutableHandleScript scriptp);
+
+template<XDRMode mode>
 bool
-XDRLazyScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
-              HandleScript enclosingScript, HandleFunction fun, MutableHandle<LazyScript*> lazy);
+XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+              HandleFunction fun, MutableHandle<LazyScript*> lazy);
 
 /*
  * Code any constant value.
  */
-template <XDRMode mode>
+template<XDRMode mode>
 bool
 XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp);
 
 } /* namespace js */
 
 class JSScript : public js::gc::TenuredCell
 {
     template <js::XDRMode mode>
     friend
     bool
-    js::XDRScript(js::XDRState<mode>* xdr, js::Handle<js::StaticScope*> enclosingScope,
+    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::Handle<js::StaticScope*> scriptStaticScope,
-                           js::HandleScript src, js::HandleScript dst);
+    js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src,
+                           js::HandleScript dst);
 
   public:
     //
     // We order fields according to their size in order to avoid wasting space
     // for alignment.
     //
 
     // Larger-than-word-sized fields.
@@ -1001,32 +999,17 @@ class JSScript : public js::gc::TenuredC
     // This script's ScriptSourceObject, or a CCW thereof.
     //
     // (When we clone a JSScript into a new compartment, we don't clone its
     // source object. Instead, the clone refers to a wrapper.)
     js::HeapPtrObject sourceObject_;
 
     js::HeapPtrFunction function_;
     js::HeapPtr<js::ModuleObject*> module_;
-
-    // The static scope this script runs in.
-    //
-    // Specifically, it depends on the case:
-    //
-    // *   direct eval: staticScope_ is the StaticEvalScope for the eval call.
-    //
-    // *   function script: staticScope_ is function_'s StaticFunctionScope.
-    //
-    // *   module script: staticScope_ is module_'s StaticModuleScope.
-    //
-    // *   plain old global script or indirect eval: staticScope_ is the static
-    //     global lexical scope (regardless of whether the script uses any
-    //     global lexical bindings).
-    //
-    js::HeapPtr<js::StaticScope*> staticScope_;
+    js::HeapPtrObject   enclosingStaticScope_;
 
     /*
      * Information attached by Ion. Nexto a valid IonScript this could be
      * ION_DISABLED_SCRIPT, ION_COMPILING_SCRIPT or ION_PENDING_SCRIPT.
      * The later is a ion compilation that is ready, but hasn't been linked
      * yet.
      */
     js::jit::IonScript* ion;
@@ -1224,17 +1207,17 @@ class JSScript : public js::gc::TenuredC
 #endif
 
     //
     // End of fields.  Start methods.
     //
 
   public:
     static JSScript* Create(js::ExclusiveContext* cx,
-                            js::Handle<js::StaticScope*> staticScope, bool savedCallerFun,
+                            js::HandleObject enclosingScope, bool savedCallerFun,
                             const JS::ReadOnlyCompileOptions& options,
                             js::HandleObject sourceObject, uint32_t sourceStart,
                             uint32_t sourceEnd);
 
     void initCompartment(js::ExclusiveContext* cx);
 
     // Three ways ways to initialize a JSScript. Callers of partiallyInit()
     // and fullyInitTrivial() are responsible for notifying the debugger after
@@ -1708,23 +1691,20 @@ class JSScript : public js::gc::TenuredC
 
     inline js::TypeScript* types();
 
     void maybeSweepTypes(js::AutoClearTypeInferenceStateOnOOM* oom);
 
     inline js::GlobalObject& global() const;
     js::GlobalObject& uninlinedGlobal() const;
 
-    js::StaticScope* staticScope() const { return staticScope_; }
-
-    /*
-     * The static scope this script runs in, skipping the StaticFunctionScope
-     * if this is a non-eval function script.
-     */
-    inline js::StaticScope* enclosingStaticScope() const;
+    /* See StaticScopeIter comment. */
+    JSObject* enclosingStaticScope() const {
+        return enclosingStaticScope_;
+    }
 
     // Switch the script over from the off-thread compartment's static
     // global lexical scope to the main thread compartment's.
     void fixEnclosingStaticGlobalLexicalScope();
 
   private:
     bool makeTypes(JSContext* cx);
 
@@ -1878,23 +1858,23 @@ class JSScript : public js::gc::TenuredC
 
     // The following 4 functions find the static scope just before the
     // execution of the instruction pointed to by pc.
 
     js::NestedStaticScope* getStaticBlockScope(jsbytecode* pc);
 
     // Returns the innermost static scope at pc if it falls within the extent
     // of the script. Returns nullptr otherwise.
-    js::StaticScope* innermostStaticScopeInScript(jsbytecode* pc);
+    JSObject* innermostStaticScopeInScript(jsbytecode* pc);
 
     // As innermostStaticScopeInScript, but returns the enclosing static scope
     // if the innermost static scope falls without the extent of the script.
-    js::StaticScope* innermostStaticScope(jsbytecode* pc);
-
-    js::StaticScope* innermostStaticScope() { return innermostStaticScope(main()); }
+    JSObject* innermostStaticScope(jsbytecode* pc);
+
+    JSObject* innermostStaticScope() { return innermostStaticScope(main()); }
 
     /*
      * The isEmpty method tells whether this script has code that computes any
      * result (not return value, result AKA normal completion value) other than
      * JSVAL_VOID, or any other effects.
      */
     bool isEmpty() const {
         if (length() > 3)
@@ -2147,19 +2127,18 @@ class LazyScript : public gc::TenuredCel
     // If non-nullptr, the script has been compiled and this is a forwarding
     // pointer to the result. This is a weak pointer: after relazification, we
     // can collect the script if there are no other pointers to it.
     WeakRef<JSScript*> script_;
 
     // Original function with which the lazy script is associated.
     HeapPtrFunction function_;
 
-    // Static scope of this function. (All lazy scripts are for functions;
-    // global scripts and eval scripts are never lazified.)
-    HeapPtr<StaticFunctionScope*> staticScope_;
+    // Function or block chain in which the script is nested, or nullptr.
+    HeapPtrObject enclosingScope_;
 
     // ScriptSourceObject. We leave this set to nullptr until we generate
     // bytecode for our immediate parent. This is never a CCW; we don't clone
     // LazyScripts into other compartments.
     HeapPtrObject sourceObject_;
 
     // Heap allocated table with any free variables or inner functions.
     void* table_;
@@ -2201,48 +2180,46 @@ class LazyScript : public gc::TenuredCel
     };
 
     // Source location for the script.
     uint32_t begin_;
     uint32_t end_;
     uint32_t lineno_;
     uint32_t column_;
 
-    LazyScript(JSFunction* fun, StaticFunctionScope* funScope, void* table, uint64_t packedFields,
+    LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
                uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
 
     // Create a LazyScript without initializing the freeVariables and the
     // innerFunctions. To be GC-safe, the caller must initialize both vectors
     // with valid atoms and functions.
     static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
-                                 Handle<StaticFunctionScope*> funScope,
                                  uint64_t packedData, uint32_t begin, uint32_t end,
                                  uint32_t lineno, uint32_t column);
 
   public:
     // Create a LazyScript without initializing the freeVariables and the
     // innerFunctions. To be GC-safe, the caller must initialize both vectors
     // with valid atoms and functions.
     static LazyScript* CreateRaw(ExclusiveContext* cx, HandleFunction fun,
-                                 Handle<StaticFunctionScope*> funScope,
                                  uint32_t numFreeVariables, uint32_t numInnerFunctions,
                                  JSVersion version, uint32_t begin, uint32_t end,
                                  uint32_t lineno, uint32_t column);
 
     // Create a LazyScript and initialize the freeVariables and the
     // innerFunctions with dummy values to be replaced in a later initialization
     // phase.
     //
     // The "script" argument to this function can be null.  If it's non-null,
     // then this LazyScript should be associated with the given JSScript.
     //
     // The sourceObjectScript argument must be non-null and is the script that
     // should be used to get the sourceObject_ of this lazyScript.
     static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
-                              HandleScript script, Handle<StaticFunctionScope*> funScope,
+                              HandleScript script, HandleObject enclosingScope,
                               HandleScript sourceObjectScript,
                               uint64_t packedData, uint32_t begin, uint32_t end,
                               uint32_t lineno, uint32_t column);
 
     void initRuntimeFields(uint64_t packedFields);
 
     inline JSFunction* functionDelazifying(JSContext* cx) const;
     JSFunction* functionNonDelazifying() const {
@@ -2257,18 +2234,19 @@ class LazyScript : public gc::TenuredCel
     }
     const JSScript* maybeScriptUnbarriered() const {
         return script_.unbarrieredGet();
     }
     bool hasScript() const {
         return bool(script_);
     }
 
-    StaticFunctionScope* staticScope() const { return staticScope_; }
-    StaticScope* enclosingScope() const;
+    JSObject* enclosingScope() const {
+        return enclosingScope_;
+    }
 
     // Switch the script over from the off-thread compartment's static
     // global lexical scope to the main thread compartment's.
     void fixEnclosingStaticGlobalLexicalScope();
 
     ScriptSourceObject* sourceObject() const;
     ScriptSource* scriptSource() const {
         return sourceObject()->source();
@@ -2277,17 +2255,17 @@ class LazyScript : public gc::TenuredCel
     bool mutedErrors() const {
         return scriptSource()->mutedErrors();
     }
     JSVersion version() const {
         JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
         return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version);
     }
 
-    void initSource(ScriptSourceObject* sourceObject);
+    void setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject);
 
     uint32_t numFreeVariables() const {
         return p_.numFreeVariables;
     }
     FreeVariable* freeVariables() {
         return (FreeVariable*)table_;
     }
 
@@ -2537,29 +2515,29 @@ 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, Handle<StaticScope*> enclosingScope, HandleFunction fun,
+CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
                         HandleScript src);
 
 JSScript*
 CloneGlobalScript(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleScript src);
 
 } /* 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 <>
+template<>
 struct Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> {
     CoarseType coarseType() const final { return CoarseType::Script; }
     Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
     const char* scriptFilename() const final;
 
   protected:
     explicit Concrete(js::LazyScript *ptr) : TracerConcrete<js::LazyScript>(ptr) { }
 
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -87,33 +87,18 @@ SetFrameArgumentsObject(JSContext* cx, A
 inline JSFunction*
 LazyScript::functionDelazifying(JSContext* cx) const
 {
     if (function_ && !function_->getOrCreateScript(cx))
         return nullptr;
     return function_;
 }
 
-inline StaticScope*
-LazyScript::enclosingScope() const
-{
-    return staticScope_->enclosingScope();
-}
-
 } // namespace js
 
-inline js::StaticScope*
-JSScript::enclosingStaticScope() const
-{
-    // The static scope of a function script is the function scope (which
-    // contains arguments and local variables). This method's callers want to
-    // skip that scope.
-    return function_ ? staticScope_->enclosingScope() : staticScope_;
-}
-
 inline JSFunction*
 JSScript::functionDelazifying() const
 {
     if (function_ && function_->isInterpretedLazy()) {
         function_->setUnlazifiedScript(const_cast<JSScript*>(this));
         // If this script has a LazyScript, make sure the LazyScript has a
         // reference to the script when delazifying its canonical function.
         if (lazyScript && !lazyScript->maybeScript())
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4670,18 +4670,16 @@ DumpStaticScopeChain(JSContext* cx, unsi
 
     if (obj->is<JSFunction>()) {
         RootedFunction fun(cx, &obj->as<JSFunction>());
         if (!fun->isInterpreted()) {
             ReportUsageError(cx, callee, "Argument must be an interpreted function");
             return false;
         }
         script = fun->getOrCreateScript(cx);
-        if (!script)
-            return false;
     } else {
         script = obj->as<ModuleObject>().script();
     }
 
     js::DumpStaticScopeChain(script);
 
     args.rval().setUndefined();
     return true;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -922,18 +922,19 @@ js::TypeOfValue(const Value& v)
 }
 
 /*
  * Enter the new with scope using an object at sp[-1] and associate the depth
  * of the with block with sp + stackIndex.
  */
 bool
 js::EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
-                       Handle<StaticWithScope*> staticWith)
-{
+                       HandleObject staticWith)
+{
+    MOZ_ASSERT(staticWith->is<StaticWithScope>());
     RootedObject obj(cx);
     if (val.isObject()) {
         obj = &val.toObject();
     } else {
         obj = ToObject(cx, val);
         if (!obj)
             return false;
     }
@@ -1033,18 +1034,18 @@ ForcedReturn(JSContext* cx, InterpreterR
 }
 
 static void
 SettleOnTryNote(JSContext* cx, JSTryNote* tn, ScopeIter& si, InterpreterRegs& regs)
 {
     // Unwind the scope to the beginning of the JSOP_TRY.
     UnwindScope(cx, si, UnwindScopeToTryPc(regs.fp()->script(), tn));
 
-    // Set pc to the first bytecode after the span of the try note, the
-    // beginning of the first catch or finally block.
+    // Set pc to the first bytecode after the the try note to point
+    // to the beginning of catch or finally.
     regs.pc = regs.fp()->script()->main() + tn->start + tn->length;
     regs.sp = regs.spForStackDepth(tn->stackDepth);
 }
 
 class InterpreterFrameStackDepthOp
 {
     const InterpreterRegs& regs_;
   public:
@@ -1488,19 +1489,16 @@ class ReservedRooted : public ReservedRo
         *savedRoot = js::GCPolicy<T>::initial();
     }
 
     void set(const T& p) const { *savedRoot = p; }
     operator Handle<T>() { return *savedRoot; }
     operator Rooted<T>&() { return *savedRoot; }
     MutableHandle<T> operator&() { return &*savedRoot; }
 
-    template <typename U>
-    Handle<U*> as() { return savedRoot->template as<U>(); }
-
     DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_POINTER_CONSTREF_OPS(T)
     DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
 };
 
 template <>
 class ReservedRootedBase<Value> : public ValueOperations<ReservedRooted<Value>>
@@ -1870,17 +1868,17 @@ CASE(JSOP_GETRVAL)
 END_CASE(JSOP_GETRVAL)
 
 CASE(JSOP_ENTERWITH)
 {
     ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
     REGS.sp--;
     ReservedRooted<JSObject*> staticWith(&rootObject0, script->getObject(REGS.pc));
 
-    if (!EnterWithOperation(cx, REGS.fp(), val, staticWith.as<StaticWithScope>()))
+    if (!EnterWithOperation(cx, REGS.fp(), val, staticWith))
         goto error;
 }
 END_CASE(JSOP_ENTERWITH)
 
 CASE(JSOP_LEAVEWITH)
     REGS.fp()->popWith(cx);
 END_CASE(JSOP_LEAVEWITH)
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -16,17 +16,16 @@
 
 #include "frontend/ParseNode.h"
 
 #include "vm/Stack.h"
 
 namespace js {
 
 class ScopeIter;
-class StaticWithScope;
 
 /*
  * For a given |call|, convert null/undefined |this| into the global object for
  * the callee and replace other primitives with boxed versions. This assumes
  * that call.callee() is not strict mode code. This is the special/slow case of
  * ComputeThis.
  */
 extern bool
@@ -424,18 +423,18 @@ InitGetterSetterOperation(JSContext* cx,
 bool
 InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandlePropertyName name,
                           HandleObject val);
 
 unsigned
 GetInitDataPropAttrs(JSOp op);
 
 bool
-EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
-                   Handle<StaticWithScope*> staticWith);
+EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith);
+
 
 bool
 InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleValue idval,
                           HandleObject val);
 
 bool
 SpreadCallOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue thisv,
                     HandleValue callee, HandleValue arr, HandleValue newTarget, MutableHandleValue res);
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -79,163 +79,180 @@ LexicalScopeBase::initAliasedLexicalsToT
 {
     initRemainingSlotsToUninitializedLexicals(script->bindings.aliasedBodyLevelLexicalBegin());
 }
 
 template <AllowGC allowGC>
 inline void
 StaticScopeIter<allowGC>::operator++(int)
 {
-    if (!scope->template is<StaticFunctionScope>()) {
-        scope = scope->enclosingScope();
-    } else if (onNamedLambda || !scope->template as<StaticFunctionScope>().isNamedLambda()) {
+    if (obj->template is<NestedStaticScope>()) {
+        obj = obj->template as<NestedStaticScope>().enclosingScope();
+    } else if (obj->template is<StaticEvalScope>()) {
+        obj = obj->template as<StaticEvalScope>().enclosingScope();
+    } else if (obj->template is<StaticNonSyntacticScope>()) {
+        obj = obj->template as<StaticNonSyntacticScope>().enclosingScope();
+    } else if (obj->template is<ModuleObject>()) {
+        obj = obj->template as<ModuleObject>().enclosingStaticScope();
+    } else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
         onNamedLambda = false;
-        scope = scope->enclosingScope();
+        JSFunction& fun = obj->template as<JSFunction>();
+        if (fun.isBeingParsed())
+            obj = fun.functionBox()->enclosingStaticScope();
+        else
+            obj = fun.nonLazyScript()->enclosingStaticScope();
     } else {
         onNamedLambda = true;
     }
-    MOZ_ASSERT_IF(scope, scope->template is<StaticScope>());
-    MOZ_ASSERT_IF(onNamedLambda, scope->template is<StaticFunctionScope>());
+    MOZ_ASSERT_IF(obj, IsStaticScope(obj));
+    MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>());
 }
 
 template <AllowGC allowGC>
 inline bool
 StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
 {
-    if (scope->template is<StaticFunctionScope>()) {
-        JSFunction& fun = scope->template as<StaticFunctionScope>().function();
+    if (obj->template is<JSFunction>()) {
+        JSFunction& fun = obj->template as<JSFunction>();
         if (fun.isBeingParsed())
             return fun.functionBox()->needsCallObject();
         return fun.needsCallObject();
     }
-    if (scope->template is<StaticModuleScope>())
+    if (obj->template is<ModuleObject>())
         return true;
-    if (scope->template is<StaticBlockScope>()) {
-        return scope->template as<StaticBlockScope>().needsClone() ||
-               scope->template as<StaticBlockScope>().isGlobal();
+    if (obj->template is<StaticBlockScope>()) {
+        return obj->template as<StaticBlockScope>().needsClone() ||
+               obj->template as<StaticBlockScope>().isGlobal();
     }
-    if (scope->template is<StaticWithScope>())
+    if (obj->template is<StaticWithScope>())
         return true;
-    if (scope->template is<StaticEvalScope>())
-        return scope->template as<StaticEvalScope>().isStrict();
-    MOZ_ASSERT(scope->template is<StaticNonSyntacticScope>());
+    if (obj->template is<StaticEvalScope>())
+        return obj->template as<StaticEvalScope>().isStrict();
+    MOZ_ASSERT(obj->template is<StaticNonSyntacticScope>());
     return false;
 }
 
 template <AllowGC allowGC>
 inline Shape*
 StaticScopeIter<allowGC>::scopeShape() const
 {
     MOZ_ASSERT(hasSyntacticDynamicScopeObject());
     MOZ_ASSERT(type() != NamedLambda && type() != Eval);
     if (type() == Block)
         return block().lastProperty();
     if (type() == Module)
-        return module().environmentShape();
-    return fun().environmentShape();
+        return moduleScript()->callObjShape();
+    return funScript()->callObjShape();
 }
 
 template <AllowGC allowGC>
 inline typename StaticScopeIter<allowGC>::Type
 StaticScopeIter<allowGC>::type() const
 {
     if (onNamedLambda)
         return NamedLambda;
-    if (scope->template is<StaticBlockScope>())
+    if (obj->template is<StaticBlockScope>())
         return Block;
-    if (scope->template is<StaticModuleScope>())
-        return Module;
-    if (scope->template is<StaticWithScope>())
+    if (obj->template is<StaticWithScope>())
         return With;
-    if (scope->template is<StaticEvalScope>())
+    if (obj->template is<StaticEvalScope>())
         return Eval;
-    if (scope->template is<StaticNonSyntacticScope>())
+    if (obj->template is<StaticNonSyntacticScope>())
         return NonSyntactic;
-    MOZ_ASSERT(scope->template is<StaticFunctionScope>());
+    if (obj->template is<ModuleObject>())
+        return Module;
+    MOZ_ASSERT(obj->template is<JSFunction>());
     return Function;
 }
 
 template <AllowGC allowGC>
 inline StaticBlockScope&
 StaticScopeIter<allowGC>::block() const
 {
     MOZ_ASSERT(type() == Block);
-    return scope->template as<StaticBlockScope>();
+    return obj->template as<StaticBlockScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticWithScope&
 StaticScopeIter<allowGC>::staticWith() const
 {
     MOZ_ASSERT(type() == With);
-    return scope->template as<StaticWithScope>();
+    return obj->template as<StaticWithScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticEvalScope&
 StaticScopeIter<allowGC>::eval() const
 {
     MOZ_ASSERT(type() == Eval);
-    return scope->template as<StaticEvalScope>();
+    return obj->template as<StaticEvalScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticNonSyntacticScope&
 StaticScopeIter<allowGC>::nonSyntactic() const
 {
     MOZ_ASSERT(type() == NonSyntactic);
-    return scope->template as<StaticNonSyntacticScope>();
+    return obj->template as<StaticNonSyntacticScope>();
 }
 
 template <AllowGC allowGC>
 inline JSScript*
 StaticScopeIter<allowGC>::funScript() const
 {
     MOZ_ASSERT(type() == Function);
-    return scope->template as<StaticFunctionScope>().function().nonLazyScript();
+    return obj->template as<JSFunction>().nonLazyScript();
 }
 
 template <AllowGC allowGC>
-inline StaticFunctionScope&
+inline JSFunction&
 StaticScopeIter<allowGC>::fun() const
 {
     MOZ_ASSERT(type() == Function);
-    return scope->template as<StaticFunctionScope>();
+    return obj->template as<JSFunction>();
 }
 
 template <AllowGC allowGC>
 inline frontend::FunctionBox*
 StaticScopeIter<allowGC>::maybeFunctionBox() const
 {
     MOZ_ASSERT(type() == Function);
-    if (fun().function().isBeingParsed())
-        return fun().function().functionBox();
+    if (fun().isBeingParsed())
+        return fun().functionBox();
     return nullptr;
 }
 
 template <AllowGC allowGC>
-inline StaticModuleScope&
+inline JSScript*
+StaticScopeIter<allowGC>::moduleScript() const
+{
+    MOZ_ASSERT(type() == Module);
+    return obj->template as<ModuleObject>().script();
+}
+
+template <AllowGC allowGC>
+inline ModuleObject&
 StaticScopeIter<allowGC>::module() const
 {
     MOZ_ASSERT(type() == Module);
-    return scope->template as<StaticModuleScope>();
+    return obj->template as<ModuleObject>();
 }
 
 }  /* namespace js */
 
 inline JSObject*
 JSObject::enclosingScope()
 {
     if (is<js::ScopeObject>())
         return &as<js::ScopeObject>().enclosingScope();
 
     if (is<js::DebugScopeObject>())
         return &as<js::DebugScopeObject>().enclosingScope();
 
     if (is<js::GlobalObject>())
         return nullptr;
 
-    MOZ_ASSERT(!is<JSFunction>());
-    MOZ_ASSERT(!is<js::StaticScope>());
+    MOZ_ASSERT_IF(is<JSFunction>(), as<JSFunction>().isInterpreted());
     return &global();
 }
 
 #endif /* vm_ScopeObject_inl_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -34,20 +34,20 @@ using mozilla::PodZero;
 
 typedef Rooted<ArgumentsObject*> RootedArgumentsObject;
 typedef MutableHandle<ArgumentsObject*> MutableHandleArgumentsObject;
 
 
 /*** Static scope objects ************************************************************************/
 
 void
-StaticScope::setEnclosingScope(StaticScope* scope)
+StaticScope::setEnclosingScope(HandleObject obj)
 {
-    MOZ_ASSERT_IF(scope->is<StaticBlockScope>(), scope->isDelegate());
-    setFixedSlot(ENCLOSING_SCOPE_SLOT, ObjectValue(*scope));
+    MOZ_ASSERT_IF(obj->is<StaticBlockScope>(), obj->isDelegate());
+    setFixedSlot(ENCLOSING_SCOPE_SLOT, ObjectValue(*obj));
 }
 
 bool
 StaticBlockScope::isExtensible() const
 {
     return nonProxyIsExtensible();
 }
 
@@ -113,88 +113,31 @@ StaticBlockScope::addVar(ExclusiveContex
                                              /* setter = */ nullptr,
                                              slot,
                                              propFlags,
                                              /* attrs = */ 0,
                                              entry,
                                              /* allowDictionary = */ false);
 }
 
-const Class StaticFunctionScope::class_ = {
-    "StaticFunctionScope",
-    JSCLASS_HAS_RESERVED_SLOTS(StaticFunctionScope::RESERVED_SLOTS) |
-    JSCLASS_IS_ANONYMOUS
-};
-
-StaticFunctionScope*
-StaticFunctionScope::create(ExclusiveContext* cx, Handle<JSFunction*> functionObject,
-                            Handle<StaticScope*> enclosingScope)
-{
-    Rooted<TaggedProto> proto(cx, TaggedProto(nullptr));
-    JSObject* obj = NewObjectWithGivenTaggedProto(cx, &class_, proto, TenuredObject,
-                                                  BaseShape::DELEGATE);
-    if (!obj)
-        return nullptr;
-
-    StaticFunctionScope* scope = &obj->as<StaticFunctionScope>();
-    scope->initEnclosingScope(enclosingScope);
-    scope->setReservedSlot(FUNCTION_OBJECT_SLOT, ObjectValue(*functionObject));
-    return scope;
-}
-
-StaticModuleScope*
-StaticModuleScope::create(ExclusiveContext* cx, Handle<ModuleObject*> moduleObject,
-                          Handle<StaticScope*> enclosingScope)
-{
-    Rooted<TaggedProto> nullProto(cx, TaggedProto(nullptr));
-    JSObject* obj = NewObjectWithGivenTaggedProto(cx, &ModuleEnvironmentObject::class_, nullProto,
-                                                  TenuredObject, BaseShape::DELEGATE);
-    if (!obj)
-        return nullptr;
-
-    StaticModuleScope* scope = &obj->as<StaticModuleScope>();
-    scope->initEnclosingScope(enclosingScope);
-    scope->setReservedSlot(MODULE_OBJECT_SLOT, ObjectValue(*moduleObject));
-    return scope;
-}
-
-ModuleObject&
-StaticModuleScope::moduleObject()
-{
-    return getReservedSlot(MODULE_OBJECT_SLOT).toObject().as<ModuleObject>();
-}
-
-JSScript*
-StaticModuleScope::script()
-{
-    return moduleObject().script();
-}
-
-Shape*
-StaticModuleScope::environmentShape()
-{
-    ModuleObject* module = &getReservedSlot(MODULE_OBJECT_SLOT).toObject().as<ModuleObject>();
-    return module->script()->bindings.callObjShape();
-}
-
 const Class StaticWithScope::class_ = {
     "WithTemplate",
     JSCLASS_HAS_RESERVED_SLOTS(StaticWithScope::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS
 };
 
 StaticWithScope*
 StaticWithScope::create(ExclusiveContext* cx)
 {
     return NewObjectWithNullTaggedProto<StaticWithScope>(cx, TenuredObject, BaseShape::DELEGATE);
 }
 
 template<XDRMode mode>
 bool
-js::XDRStaticWithScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+js::XDRStaticWithScope(XDRState<mode>* xdr, HandleObject enclosingScope,
                        MutableHandle<StaticWithScope*> objp)
 {
     if (mode == XDR_DECODE) {
         JSContext* cx = xdr->cx();
         Rooted<StaticWithScope*> obj(cx, StaticWithScope::create(cx));
         if (!obj)
             return false;
         obj->initEnclosingScope(enclosingScope);
@@ -203,22 +146,20 @@ js::XDRStaticWithScope(XDRState<mode>* x
     // For encoding, there is nothing to do.  The only information that is
     // encoded by a StaticWithScope is its presence on the scope chain, and the
     // script XDR handler already takes care of that.
 
     return true;
 }
 
 template bool
-js::XDRStaticWithScope(XDRState<XDR_ENCODE>*, Handle<StaticScope*>,
-                       MutableHandle<StaticWithScope*>);
+js::XDRStaticWithScope(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticWithScope*>);
 
 template bool
-js::XDRStaticWithScope(XDRState<XDR_DECODE>*, Handle<StaticScope*>,
-                       MutableHandle<StaticWithScope*>);
+js::XDRStaticWithScope(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticWithScope*>);
 
 
 /*****************************************************************************/
 
 Shape*
 js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc)
 {
     MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
@@ -494,19 +435,16 @@ CallObject::createHollowForDebug(JSConte
 
 const Class CallObject::class_ = {
     "Call",
     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS)
 };
 
 /*****************************************************************************/
 
-static_assert(StaticModuleScope::RESERVED_SLOTS == ModuleEnvironmentObject::RESERVED_SLOTS,
-              "static module scopes and dynamic module environments share a Class");
-
 const Class ModuleEnvironmentObject::class_ = {
     "ModuleEnvironmentObject",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
@@ -537,18 +475,17 @@ const Class ModuleEnvironmentObject::cla
 
 /* static */ ModuleEnvironmentObject*
 ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
 {
     RootedScript script(cx, module->script());
     RootedShape shape(cx, script->bindings.callObjShape());
     MOZ_ASSERT(shape->getObjectClass() == &class_);
 
-    Rooted<TaggedProto> proto(cx, module->staticScope());
-    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, proto));
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
     JSObject* obj = JSObject::create(cx, kind, TenuredHeap, shape, group);
@@ -583,22 +520,16 @@ ModuleEnvironmentObject::create(Exclusiv
 }
 
 ModuleObject&
 ModuleEnvironmentObject::module()
 {
     return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>();
 }
 
-StaticModuleScope&
-ModuleEnvironmentObject::staticScope()
-{
-    return getProto()->as<StaticModuleScope>();
-}
-
 IndirectBindingMap&
 ModuleEnvironmentObject::importBindings()
 {
     return module().importBindings();
 }
 
 bool
 ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importName,
@@ -784,33 +715,34 @@ DeclEnvObject::create(JSContext* cx, Han
     if (!obj)
         return nullptr;
 
     obj->setEnclosingScope(enclosing);
     obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
     return obj;
 }
 
-static StaticWithScope*
-CloneStaticWithScope(JSContext* cx, Handle<StaticScope*> enclosingScope,
-                     Handle<StaticWithScope*> srcWith)
+static JSObject*
+CloneStaticWithScope(JSContext* cx, HandleObject enclosingScope, Handle<StaticWithScope*> srcWith)
 {
     Rooted<StaticWithScope*> clone(cx, StaticWithScope::create(cx));
     if (!clone)
         return nullptr;
 
     clone->initEnclosingScope(enclosingScope);
 
     return clone;
 }
 
 DynamicWithObject*
 DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
-                          Handle<StaticWithScope*> staticWith, WithKind kind)
+                          HandleObject staticWith, WithKind kind)
 {
+    MOZ_ASSERT(staticWith->is<StaticWithScope>());
+
     Rooted<TaggedProto> proto(cx, TaggedProto(staticWith));
     Rooted<DynamicWithObject*> obj(cx);
     obj = NewObjectWithGivenTaggedProto<DynamicWithObject>(cx, proto, GenericObject,
                                                            BaseShape::DELEGATE);
     if (!obj)
         return nullptr;
 
     Value thisv = GetThisValue(object);
@@ -923,26 +855,26 @@ const Class DynamicWithObject::class_ = 
         nullptr, nullptr,    /* watch/unwatch */
         nullptr,             /* getElements */
         nullptr,             /* enumerate (native enumeration of target doesn't work) */
         nullptr,
     }
 };
 
 /* static */ StaticEvalScope*
-StaticEvalScope::create(JSContext* cx, Handle<StaticScope*> enclosing)
+StaticEvalScope::create(JSContext* cx, HandleObject enclosing)
 {
-    StaticEvalScope* scope =
+    StaticEvalScope* obj =
         NewObjectWithNullTaggedProto<StaticEvalScope>(cx, TenuredObject, BaseShape::DELEGATE);
-    if (!scope)
+    if (!obj)
         return nullptr;
 
-    scope->initEnclosingScope(enclosing);
-    scope->setReservedSlot(STRICT_SLOT, BooleanValue(false));
-    return scope;
+    obj->setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(enclosing));
+    obj->setReservedSlot(STRICT_SLOT, BooleanValue(false));
+    return obj;
 }
 
 const Class StaticEvalScope::class_ = {
     "StaticEval",
     JSCLASS_HAS_RESERVED_SLOTS(StaticEvalScope::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS
 };
 
@@ -1056,20 +988,20 @@ ClonedBlockObject::createGlobal(JSContex
     if (!lexical)
         return nullptr;
     if (!JSObject::setSingleton(cx, lexical))
         return nullptr;
     return lexical;
 }
 
 /* static */ ClonedBlockObject*
-ClonedBlockObject::createNonSyntactic(JSContext* cx,
-                                      Handle<StaticNonSyntacticScope*> enclosingStatic,
+ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
                                       HandleObject enclosingScope)
 {
+    MOZ_ASSERT(enclosingStatic->is<StaticNonSyntacticScope>());
     MOZ_ASSERT(!IsSyntacticScope(enclosingScope));
 
     Rooted<StaticBlockScope*> staticLexical(cx, StaticBlockScope::create(cx));
     if (!staticLexical)
         return nullptr;
 
     staticLexical->setLocalOffset(UINT32_MAX);
     staticLexical->initEnclosingScope(enclosingStatic);
@@ -1178,17 +1110,17 @@ const Class ClonedBlockObject::class_ = 
         nullptr,          /* getElements */
         nullptr,          /* enumerate (native enumeration of target doesn't work) */
         nullptr,
     }
 };
 
 template<XDRMode mode>
 bool
-js::XDRStaticBlockScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+js::XDRStaticBlockScope(XDRState<mode>* xdr, HandleObject enclosingScope,
                         MutableHandle<StaticBlockScope*> objp)
 {
     /* NB: Keep this in sync with CloneStaticBlockScope. */
 
     JSContext* cx = xdr->cx();
 
     Rooted<StaticBlockScope*> obj(cx);
     uint32_t count = 0, offset = 0;
@@ -1284,26 +1216,23 @@ js::XDRStaticBlockScope(XDRState<mode>* 
             if (!xdr->codeUint32(&propFlags))
                 return false;
         }
     }
     return true;
 }
 
 template bool
-js::XDRStaticBlockScope(XDRState<XDR_ENCODE>*, Handle<StaticScope*>,
-                        MutableHandle<StaticBlockScope*>);
+js::XDRStaticBlockScope(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticBlockScope*>);
 
 template bool
-js::XDRStaticBlockScope(XDRState<XDR_DECODE>*, Handle<StaticScope*>,
-                        MutableHandle<StaticBlockScope*>);
-
-static StaticBlockScope*
-CloneStaticBlockScope(JSContext* cx, Handle<StaticScope*> enclosingScope,
-                      Handle<StaticBlockScope*> srcBlock)
+js::XDRStaticBlockScope(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticBlockScope*>);
+
+static JSObject*
+CloneStaticBlockScope(JSContext* cx, HandleObject enclosingScope, Handle<StaticBlockScope*> srcBlock)
 {
     /* NB: Keep this in sync with XDRStaticBlockScope. */
 
     Rooted<StaticBlockScope*> clone(cx, StaticBlockScope::create(cx));
     if (!clone)
         return nullptr;
 
     clone->initEnclosingScope(enclosingScope);
@@ -1334,26 +1263,26 @@ CloneStaticBlockScope(JSContext* cx, Han
     if (!srcBlock->isExtensible()) {
         if (!clone->makeNonExtensible(cx))
             return nullptr;
     }
 
     return clone;
 }
 
-NestedStaticScope*
-js::CloneNestedScopeObject(JSContext* cx, Handle<StaticScope*> enclosingScope,
+JSObject*
+js::CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope,
                            Handle<NestedStaticScope*> srcBlock)
 {
     if (srcBlock->is<StaticBlockScope>()) {
-        return CloneStaticBlockScope(cx, enclosingScope,
-                                     HandleObject(srcBlock).as<StaticBlockScope>());
+        Rooted<StaticBlockScope*> blockScope(cx, &srcBlock->as<StaticBlockScope>());
+        return CloneStaticBlockScope(cx, enclosingScope, blockScope);
     } else {
-        return CloneStaticWithScope(cx, enclosingScope,
-                                    HandleObject(srcBlock).as<StaticWithScope>());
+        Rooted<StaticWithScope*> withScope(cx, &srcBlock->as<StaticWithScope>());
+        return CloneStaticWithScope(cx, enclosingScope, withScope);
     }
 }
 
 /* static */ RuntimeLexicalErrorObject*
 RuntimeLexicalErrorObject::create(JSContext* cx, HandleObject enclosing, unsigned errorNumber)
 {
     RuntimeLexicalErrorObject* obj =
         NewObjectWithNullTaggedProto<RuntimeLexicalErrorObject>(cx, GenericObject,
@@ -1469,17 +1398,17 @@ ScopeIter::ScopeIter(JSContext* cx, cons
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : ssi_(cx, si.ssi_),
     scope_(cx, si.scope_),
     frame_(si.frame_)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
-ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope
+ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : ssi_(cx, staticScope),
     scope_(cx, scope),
     frame_(NullFramePtr())
 {
     settle();
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
@@ -1554,17 +1483,17 @@ ScopeIter::settle()
     // settled on a static scope.
     if (frame_ && (ssi_.done() || maybeStaticScope() == frame_.script()->enclosingStaticScope()))
         frame_ = NullFramePtr();
 
 #ifdef DEBUG
     if (!ssi_.done() && hasAnyScopeObject()) {
         switch (ssi_.type()) {
           case StaticScopeIter<CanGC>::Module:
-            MOZ_ASSERT(scope_->as<ModuleEnvironmentObject>().staticScope() == ssi_.module());
+            MOZ_ASSERT(scope_->as<ModuleEnvironmentObject>().module() == ssi_.module());
             break;
           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:
@@ -1625,30 +1554,35 @@ ScopeIter::type() const
 
 ScopeObject&
 ScopeIter::scope() const
 {
     MOZ_ASSERT(hasAnyScopeObject());
     return scope_->as<ScopeObject>();
 }
 
-StaticScope*
+JSObject*
 ScopeIter::maybeStaticScope() const
 {
     if (ssi_.done())
         return nullptr;
 
     switch (ssi_.type()) {
       case StaticScopeIter<CanGC>::Function:
+        return &fun();
       case StaticScopeIter<CanGC>::Module:
+        return &module();
       case StaticScopeIter<CanGC>::Block:
+        return &staticBlock();
       case StaticScopeIter<CanGC>::With:
+        return &staticWith();
       case StaticScopeIter<CanGC>::Eval:
+        return &staticEval();
       case StaticScopeIter<CanGC>::NonSyntactic:
-        return ssi_.staticScope();
+        return &staticNonSyntactic();
       case StaticScopeIter<CanGC>::NamedLambda:
         MOZ_CRASH("named lambda static scopes should have been skipped");
       default:
         MOZ_CRASH("bad SSI type");
     }
 }
 
 /* static */ HashNumber
@@ -2646,17 +2580,17 @@ DebugScopes::hasDebugScope(JSContext* cx
 }
 
 bool
 DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope)
 {
     MOZ_ASSERT(!si.hasSyntacticScopeObject());
     MOZ_ASSERT(cx->compartment() == debugScope.compartment());
     // Generators should always reify their scopes.
-    MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().function().isGenerator());
+    MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator());
 
     if (!CanUseDebugScopeMaps(cx))
         return true;
 
     DebugScopes* scopes = ensureCompartmentData(cx);
     if (!scopes)
         return false;
 
@@ -3004,18 +2938,17 @@ GetDebugScopeForMissing(JSContext* cx, c
      */
     DebugScopeObject* debugScope = nullptr;
     switch (si.type()) {
       case ScopeIter::Module:
           MOZ_CRASH(); // TODO: Implement debug scopes for modules.
           break;
 
       case ScopeIter::Call: {
-        RootedFunction callee(cx, &si.fun().function());
-
+        RootedFunction callee(cx, &si.fun());
         // Generators should always reify their scopes.
         MOZ_ASSERT(!callee->isGenerator());
 
         Rooted<CallObject*> callobj(cx);
         if (si.withinInitialFrame())
             callobj = CallObject::createForFunction(cx, si.initialFrame());
         else
             callobj = CallObject::createHollowForDebug(cx, callee);
@@ -3162,17 +3095,17 @@ js::CreateScopeObjectsForScopeChain(JSCo
         assertSameCompartment(cx, scopeChain[i]);
         MOZ_ASSERT(!scopeChain[i]->is<GlobalObject>());
     }
 #endif
 
     // Construct With object wrappers for the things on this scope
     // chain and use the result as the thing to scope the function to.
     Rooted<StaticWithScope*> staticWith(cx);
-    Rooted<StaticWithScope*> staticEnclosingScope(cx);
+    RootedObject staticEnclosingScope(cx);
     Rooted<DynamicWithObject*> dynamicWith(cx);
     RootedObject dynamicEnclosingScope(cx, dynamicTerminatingScope);
     for (size_t i = scopeChain.length(); i > 0; ) {
         staticWith = StaticWithScope::create(cx);
         if (!staticWith)
             return false;
         staticWith->initEnclosingScope(staticEnclosingScope);
         staticEnclosingScope = staticWith;
@@ -3184,64 +3117,64 @@ js::CreateScopeObjectsForScopeChain(JSCo
         dynamicEnclosingScope = dynamicWith;
     }
 
     dynamicScopeObj.set(dynamicEnclosingScope);
     return true;
 }
 
 bool
-js::HasNonSyntacticStaticScopeChain(StaticScope* staticScope)
+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;
 }
 
 uint32_t
-js::StaticScopeChainLength(StaticScope* staticScope)
+js::StaticScopeChainLength(JSObject* staticScope)
 {
     uint32_t length = 0;
     for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++)
         length++;
     return length;
 }
 
 ModuleEnvironmentObject*
 js::GetModuleEnvironmentForScript(JSScript* script)
 {
     StaticScopeIter<NoGC> ssi(script->enclosingStaticScope());
     while (!ssi.done() && ssi.type() != StaticScopeIter<NoGC>::Module)
         ssi++;
     if (ssi.done())
         return nullptr;
 
-    return ssi.module().moduleObject().environment();
+    return ssi.module().environment();
 }
 
 bool
 js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                              MutableHandleValue res)
 {
     for (ScopeIter si(cx, frame, pc); !si.done(); ++si) {
         if (si.type() == ScopeIter::Module) {
             res.setUndefined();
             return true;
         }
 
-        if (si.type() != ScopeIter::Call || si.fun().function().hasLexicalThis())
+        if (si.type() != ScopeIter::Call || si.fun().hasLexicalThis())
             continue;
 
-        RootedScript script(cx, si.fun().function().nonLazyScript());
+        RootedScript script(cx, si.fun().nonLazyScript());
 
         if (!script->functionHasThisBinding()) {
             MOZ_ASSERT(!script->isDerivedClassConstructor(),
                        "Derived class constructors always have a this-binding");
 
             // If we're still inside `frame`, we can use the this-value passed
             // to it, if it does not require boxing.
             if (si.withinInitialFrame() && (frame.thisArgument().isObject() || script->strict()))
@@ -3415,34 +3348,32 @@ js::CheckEvalDeclarationConflicts(JSCont
     return CheckVarNameConflictsInScope<CallObject>(cx, script, varObj);
 }
 
 #ifdef DEBUG
 
 void
 js::DumpStaticScopeChain(JSScript* script)
 {
-    DumpStaticScopeChain(script->staticScope());
+    DumpStaticScopeChain(script->enclosingStaticScope());
 }
 
 void
-js::DumpStaticScopeChain(StaticScope* staticScope)
+js::DumpStaticScopeChain(JSObject* staticScope)
 {
     for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
         switch (ssi.type()) {
           case StaticScopeIter<NoGC>::Module:
             fprintf(stdout, "module [%p]", &ssi.module());
             break;
           case StaticScopeIter<NoGC>::Function:
-            if (ssi.fun().function().isBeingParsed()) {
-                fprintf(stdout, "funbox [%p box=%p fun=%p]",
-                        &ssi.fun(), ssi.maybeFunctionBox(), &ssi.fun().function());
-            } else {
-                fprintf(stdout, "function [%p fun=%p]", &ssi.fun(), &ssi.fun().function());
-            }
+            if (ssi.fun().isBeingParsed())
+                fprintf(stdout, "funbox [%p fun=%p]", ssi.maybeFunctionBox(), &ssi.fun());
+            else
+                fprintf(stdout, "function [%p]", &ssi.fun());
             break;
           case StaticScopeIter<NoGC>::Block:
             fprintf(stdout, "block [%p]", &ssi.block());
             break;
           case StaticScopeIter<NoGC>::With:
             fprintf(stdout, "with [%p]", &ssi.staticWith());
             break;
           case StaticScopeIter<NoGC>::NamedLambda:
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -45,21 +45,21 @@ typedef Handle<ModuleObject*> HandleModu
  * divided into scopes that have direct correspondence to program text (i.e.,
  * syntactic) and ones used internally for scope walking (i.e., non-syntactic).
  *
  * The following are syntactic static scopes:
  *
  * StaticBlockScope
  *   Scope for non-function body blocks. e.g., |{ let x; }|
  *
- * StaticFunctionScope
+ * JSFunction
  *   Scope for function bodies. e.g., |function f() { var x; let y; }|
  *
- * StaticModuleScope
- *   Scope for modules.
+ * ModuleObject
+ *   Scope for moddules.
  *
  * StaticWithScope
  *   Scope for |with|. e.g., |with ({}) { ... }|
  *
  * StaticEvalScope
  *   Scope for |eval|. e.g., |eval(...)|
  *
  * The following are non-syntactic static scopes:
@@ -77,24 +77,26 @@ typedef Handle<ModuleObject*> HandleModu
  * |JSScript::enclosingStaticScope()|.
  */
 class StaticScope : public NativeObject
 {
   public:
     static const uint32_t ENCLOSING_SCOPE_SLOT = 0;
     static const unsigned RESERVED_SLOTS = ENCLOSING_SCOPE_SLOT + 1;
 
-    inline StaticScope* enclosingScope() const;
-
-    void initEnclosingScope(StaticScope* scope) {
-        MOZ_ASSERT(getReservedSlot(ENCLOSING_SCOPE_SLOT).isUndefined());
-        setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(scope));
+    inline JSObject* enclosingScope() const {
+        return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
     }
 
-    void setEnclosingScope(StaticScope* obj);
+    void initEnclosingScope(JSObject* obj) {
+        MOZ_ASSERT(getReservedSlot(ENCLOSING_SCOPE_SLOT).isUndefined());
+        setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(obj));
+    }
+
+    void setEnclosingScope(HandleObject obj);
 };
 
 class NestedStaticScope : public StaticScope
 {
   public:
     /*
      * A refinement of enclosingScope that returns nullptr if the enclosing
      * scope is not a NestedScopeObject.
@@ -102,17 +104,17 @@ class NestedStaticScope : public StaticS
     inline NestedStaticScope* enclosingNestedScope() const;
 
     /*
      * Note: in the case of hoisting, this prev-link will not ultimately be
      * the same as enclosingNestedScope; initEnclosingNestedScope must be
      * called separately in the emitter. 'reset' is just for asserting
      * stackiness.
      */
-    void initEnclosingScopeFromParser(StaticScope* prev) {
+    void initEnclosingScopeFromParser(JSObject* prev) {
         setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(prev));
     }
 
     void resetEnclosingScopeFromParser() {
         setReservedSlot(ENCLOSING_SCOPE_SLOT, UndefinedValue());
     }
 };
 
@@ -156,29 +158,34 @@ class StaticBlockScope : public NestedSt
     static StaticBlockScope* create(ExclusiveContext* cx);
 
     /*
      * Among block scopes, only the global lexical scope is extensible.
      * (Non-syntactic scopes and some function scopes are also extensible.)
      */
     bool isExtensible() const;
 
+    /* See StaticScopeIter comment. */
+    JSObject* enclosingStaticScope() const {
+        return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
+    }
+
     /*
      * Return the index (in the range [0, numVariables()) corresponding to the
      * given shape of a block object.
      */
     uint32_t shapeToIndex(const Shape& shape) {
         uint32_t slot = shape.slot();
         MOZ_ASSERT(slot - RESERVED_SLOTS < numVariables());
         return slot - RESERVED_SLOTS;
     }
 
     /*
      * A refinement of enclosingStaticScope that returns nullptr if the enclosing
-     * static scope is a StaticFunctionScope.
+     * static scope is a JSFunction.
      */
     inline StaticBlockScope* enclosingBlock() const;
 
     uint32_t localOffset() {
         return getReservedSlot(LOCAL_OFFSET_SLOT).toPrivateUint32();
     }
 
     // Return the local corresponding to the 'var'th binding where 'var' is in the
@@ -201,17 +208,17 @@ class StaticBlockScope : public NestedSt
     // in the range [RESERVED_SLOTS, RESERVED_SLOTS + numVariables()).
     uint32_t localIndexToSlot(uint32_t local) {
         MOZ_ASSERT(local >= localOffset());
         return blockIndexToSlot(local - localOffset());
     }
 
     /*
      * A let binding is aliased if accessed lexically by nested functions or
-     * dynamically through dynamic name lookup (eval, with, etc).
+     * dynamically through dynamic name lookup (eval, with, function::, etc).
      */
     bool isAliased(unsigned i) {
         return slotValue(i).isTrue();
     }
 
     // Look up if the block has an aliased binding named |name|.
     Shape* lookupAliasedName(PropertyName* name);
 
@@ -220,20 +227,17 @@ class StaticBlockScope : public NestedSt
      * variable of the block isAliased.
      */
     bool needsClone() {
         return numVariables() > 0 && !getSlot(RESERVED_SLOTS).isFalse();
     }
 
     // Is this the static global lexical scope?
     bool isGlobal() const {
-        // This method is called from js::gc::MergeCompartments() on scopes
-        // that may be otherwise unreachable (!) and whose enclosing scope slot
-        // may be `undefined`.
-        return getFixedSlot(ENCLOSING_SCOPE_SLOT).isNull();
+        return !enclosingStaticScope();
     }
 
     bool isSyntactic() const {
         return !isExtensible() || isGlobal();
     }
 
     /* Frontend-only functions ***********************************************/
 
@@ -291,83 +295,48 @@ class StaticBlockScope : public NestedSt
      * remove INDEX_LIMIT.
      */
     static const unsigned LOCAL_INDEX_LIMIT = JS_BIT(16);
 
     static Shape* addVar(ExclusiveContext* cx, Handle<StaticBlockScope*> block, HandleId id,
                          bool constant, unsigned index, bool* redeclared);
 };
 
-class StaticFunctionScope : public StaticScope
-{
-    static const unsigned FUNCTION_OBJECT_SLOT = StaticScope::RESERVED_SLOTS;
-
-  public:
-    static const unsigned RESERVED_SLOTS = FUNCTION_OBJECT_SLOT + 1;
-    static const Class class_;
-
-    static StaticFunctionScope* create(ExclusiveContext* cx, HandleFunction functionObject,
-                                       Handle<StaticScope*> enclosingScope);
-
-    JSFunction& function() {
-        return getFixedSlot(FUNCTION_OBJECT_SLOT).toObject().as<JSFunction>();
-    }
-
-    bool isNamedLambda() { return function().isNamedLambda(); }
-
-    Shape* environmentShape() {
-        return function().nonLazyScript()->callObjShape();
-    }
-};
-
-// The top-level scope of a module.
-// Shares ModuleEnvironmentObject::class_.
-class StaticModuleScope : public StaticScope
-{
-    static const unsigned MODULE_OBJECT_SLOT = StaticScope::RESERVED_SLOTS;
-
-  public:
-    static const unsigned RESERVED_SLOTS = MODULE_OBJECT_SLOT + 1;
-
-    static StaticModuleScope* create(ExclusiveContext* cx, Handle<ModuleObject*> moduleObject,
-                                     Handle<StaticScope*> enclosingScope);
-
-    ModuleObject& moduleObject();
-    JSScript* script();
-    Shape* environmentShape();
-};
-
 // Represents the lexical scope of a 'with' statement.
 class StaticWithScope : public NestedStaticScope
 {
   public:
     static const Class class_;
 
     static StaticWithScope* create(ExclusiveContext* cx);
 };
 
 template <XDRMode mode>
 bool
-XDRStaticWithScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+XDRStaticWithScope(XDRState<mode>* xdr, HandleObject enclosingScope,
                    MutableHandle<StaticWithScope*> objp);
 
 /*
  * 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.
  */
 class StaticEvalScope : public StaticScope
 {
     static const uint32_t STRICT_SLOT = StaticScope::RESERVED_SLOTS;
     static const unsigned RESERVED_SLOTS = STRICT_SLOT + 1;
 
   public:
     static const Class class_;
 
-    static StaticEvalScope* create(JSContext* cx, Handle<StaticScope*> enclosing);
+    static StaticEvalScope* create(JSContext* cx, HandleObject enclosing);
+
+    JSObject* enclosingScopeForStaticScopeIter() {
+        return getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
+    }
 
     void setStrict() {
         setReservedSlot(STRICT_SLOT, BooleanValue(true));
     }
 
     bool isStrict() const {
         return getReservedSlot(STRICT_SLOT).isTrue();
     }
@@ -484,79 +453,93 @@ class StaticEvalScope : public StaticSco
  */
 class StaticNonSyntacticScope : public StaticScope
 {
   public:
     static const unsigned RESERVED_SLOTS = StaticScope::RESERVED_SLOTS;
     static const Class class_;
 
     static StaticNonSyntacticScope* create(JSContext* cx, HandleObject enclosing);
+
+    JSObject* enclosingScopeForStaticScopeIter() {
+        return getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
+    }
 };
 
 template <AllowGC allowGC>
 class StaticScopeIter
 {
-    typename MaybeRooted<StaticScope*, allowGC>::RootType scope;
+    typename MaybeRooted<JSObject*, allowGC>::RootType obj;
     bool onNamedLambda;
 
+    static bool IsStaticScope(JSObject* obj) {
+        return obj->is<StaticBlockScope>() ||
+               obj->is<StaticWithScope>() ||
+               obj->is<StaticEvalScope>() ||
+               obj->is<StaticNonSyntacticScope>() ||
+               obj->is<JSFunction>() ||
+               obj->is<ModuleObject>();
+    }
+
   public:
-    StaticScopeIter(ExclusiveContext* cx, StaticScope* scope)
-      : scope(cx, scope), onNamedLambda(false)
+    StaticScopeIter(ExclusiveContext* cx, JSObject* obj)
+      : obj(cx, obj), onNamedLambda(false)
     {
         static_assert(allowGC == CanGC,
                       "the context-accepting constructor should only be used "
                       "in CanGC code");
-        MOZ_ASSERT_IF(scope, scope->is<StaticScope>());
+        MOZ_ASSERT_IF(obj, IsStaticScope(obj));
     }
 
     StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter<CanGC>& ssi)
-      : scope(cx, ssi.scope), onNamedLambda(ssi.onNamedLambda)
+      : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda)
     {
         JS_STATIC_ASSERT(allowGC == CanGC);
     }
 
-    explicit StaticScopeIter(StaticScope* scope)
-      : scope((ExclusiveContext*) nullptr, scope), onNamedLambda(false)
+    explicit StaticScopeIter(JSObject* obj)
+      : obj((ExclusiveContext*) nullptr, obj), onNamedLambda(false)
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
-        MOZ_ASSERT_IF(scope, scope->is<StaticScope>());
+        MOZ_ASSERT_IF(obj, IsStaticScope(obj));
     }
 
     explicit StaticScopeIter(const StaticScopeIter<NoGC>& ssi)
-      : scope((ExclusiveContext*) nullptr, ssi.scope), onNamedLambda(ssi.onNamedLambda)
+      : 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 { return !scope; }
+    bool done() const { return !obj; }
     void operator++(int);
 
-    StaticScope* staticScope() const { MOZ_ASSERT(!done()); return scope; }
+    JSObject* staticScope() const { MOZ_ASSERT(!done()); return obj; }
 
     // 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;
     Shape* scopeShape() const;
 
     enum Type { Module, Function, Block, With, NamedLambda, Eval, NonSyntactic };
     Type type() const;
 
     StaticBlockScope& block() const;
-    StaticModuleScope& module() const;
     StaticWithScope& staticWith() const;
     StaticEvalScope& eval() const;
     StaticNonSyntacticScope& nonSyntactic() const;
-    StaticFunctionScope& fun() const;
     JSScript* funScript() const;
+    JSFunction& fun() const;
     frontend::FunctionBox* maybeFunctionBox() const;
+    JSScript* moduleScript() const;
+    ModuleObject& module() const;
 };
 
 
 /*****************************************************************************/
 
 /*
  * A "scope coordinate" describes how to get from head of the scope chain to a
  * given lexically-enclosing variable. A scope coordinate has two dimensions:
@@ -614,56 +597,52 @@ 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
- *     |
- *     +--StaticScope                   Created at compile time
- *     |   |
- *     |   +--StaticNonSyntacticScope   See "Non-syntactic scopes"
+ *   JSObject                       Generic object
  *     |   |
- *     |   +--StaticEvalScope           Placeholder so eval scopes may be iterated through
+ *     |  StaticScope               Created at compile time
+ *     |   |   |   |
+ *     |   |   |  StaticNonSyntacticScope   See "Non-syntactic scopes"
+ *     |   |   |
+ *     |   |  StaticEvalScope       Placeholder so eval scopes may be iterated through
  *     |   |
- *     |   +--StaticFunctionScope       Scope in a function
- *     |   |
- *     |   +--StaticModuleScope         Toplevel scope in a module
+ *     |  NestedStaticScope         Enclosing scope is in the same JSScript
+ *     |   |   |
+ *     |   |  StaticBlockScope      See NB
  *     |   |
- *     |   +--NestedStaticScope         Enclosing scope is in the same JSScript
- *     |       |
- *     |       +--StaticBlockScope      See "N.B." below.
- *     |       |
- *     |       +--StaticWithScope       Template for "with" object in static scope chain
+ *     |  StaticWithScope           Template for "with" object in static scope chain
  *     |
- *     +--ScopeObject                   Engine-internal scope
- *         |
- *         +--DeclEnvObject             Holds name of recursive/needsCallObject named lambda
- *         |
- *         +--LexicalScopeBase          Shared base for function and modules scopes
- *         |   |
- *         |   +--CallObject            Scope of entire function or strict eval
- *         |   |
- *         |   +--ModuleEnvironmentObject   Module top-level scope on run-time scope chain
- *         |
- *         +--NestedScopeObject         Statement scopes; don't cross script boundaries
- *             |
- *             +--ClonedBlockObject     let, switch, catch, for
- *             |
- *             +--DynamicWithObject     Run-time "with" object on scope chain
+ *   ScopeObject                    Engine-internal scope
+ *     |   |   |
+ *     |   |  DeclEnvObject         Holds name of recursive/needsCallObject named lambda
+ *     |   |
+ *     |  LexicalScopeBase          Shared base for function and modules scopes
+ *     |   |   |
+ *     |   |  CallObject            Scope of entire function or strict eval
+ *     |   |
+ *     |  ModuleEnvironmentObject   Module top-level scope on run-time scope chain
+ *     |
+ *   NestedScopeObject              Statement scopes; don't cross script boundaries
+ *     |   |
+ *     |  DynamicWithObject         Run-time "with" object on scope chain
+ *     |
+ *   ClonedBlockObject              let, switch, catch, for
  *
  * 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.
  *
- * N.B. Static block objects are a special case: these objects are created at
+ * 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.
  *
  * See also "Debug scope objects" below.
  */
 
 class ScopeObject : public NativeObject
@@ -766,23 +745,33 @@ class CallObject : public LexicalScopeBa
 
     static CallObject* createForFunction(JSContext* cx, HandleObject enclosing, HandleFunction callee);
 
     static CallObject* createForFunction(JSContext* cx, AbstractFramePtr frame);
     static CallObject* createForStrictEval(JSContext* cx, AbstractFramePtr frame);
     static CallObject* createHollowForDebug(JSContext* cx, HandleFunction callee);
 
     /* True if this is for a strict mode eval frame. */
-    inline bool isForEval() const;
+    bool isForEval() const {
+        if (is<ModuleEnvironmentObject>())
+            return false;
+        MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
+        MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
+                      getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
+        return getFixedSlot(CALLEE_SLOT).isNull();
+    }
 
     /*
      * Returns the function for which this CallObject was created. (This may
      * only be called if !isForEval.)
      */
-    inline JSFunction& callee() const;
+    JSFunction& callee() const {
+        MOZ_ASSERT(!is<ModuleEnvironmentObject>());
+        return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
+    }
 
     /* For jit access. */
     static size_t offsetOfCallee() {
         return getFixedSlotOffset(CALLEE_SLOT);
     }
 
     static size_t calleeSlot() {
         return CALLEE_SLOT;
@@ -795,17 +784,16 @@ class ModuleEnvironmentObject : public L
 
   public:
     static const Class class_;
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
     ModuleObject& module();
-    StaticModuleScope& staticScope();
     IndirectBindingMap& importBindings();
 
     bool createImportBinding(JSContext* cx, HandleAtom importName, HandleModuleObject module,
                              HandleAtom exportName);
 
     bool hasImportBinding(HandlePropertyName name);
 
     bool lookupImport(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut);
@@ -892,18 +880,18 @@ class DynamicWithObject : public NestedS
     static const Class class_;
 
     enum WithKind {
         SyntacticWith,
         NonSyntacticWith
     };
 
     static DynamicWithObject*
-    create(JSContext* cx, HandleObject object, HandleObject enclosing,
-           Handle<StaticWithScope*> staticWith, WithKind kind = SyntacticWith);
+    create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith,
+           WithKind kind = SyntacticWith);
 
     StaticWithScope& staticWith() const {
         return getProto()->as<StaticWithScope>();
     }
 
     /* Return the 'o' in 'with (o)'. */
     JSObject& object() const {
         return getReservedSlot(OBJECT_SLOT).toObject();
@@ -946,18 +934,17 @@ class ClonedBlockObject : public NestedS
                                      HandleObject enclosing);
 
   public:
     static ClonedBlockObject* create(JSContext* cx, Handle<StaticBlockScope*> block,
                                      AbstractFramePtr frame);
 
     static ClonedBlockObject* createGlobal(JSContext* cx, Handle<GlobalObject*> global);
 
-    static ClonedBlockObject* createNonSyntactic(JSContext* cx,
-                                                 Handle<StaticNonSyntacticScope*> enclosingStatic,
+    static ClonedBlockObject* createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
                                                  HandleObject enclosingScope);
 
     static ClonedBlockObject* createHollowForDebug(JSContext* cx,
                                                    Handle<StaticBlockScope*> block);
 
     /* Return the number of variables associated with this block. */
     uint32_t numVariables() const {
         // TODO: propertyCount() is O(n), use O(1) lastProperty()->slot() instead
@@ -1054,21 +1041,21 @@ class RuntimeLexicalErrorObject : public
 
     unsigned errorNumber() {
         return getReservedSlot(ERROR_SLOT).toInt32();
     }
 };
 
 template<XDRMode mode>
 bool
-XDRStaticBlockScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+XDRStaticBlockScope(XDRState<mode>* xdr, HandleObject enclosingScope,
                     MutableHandle<StaticBlockScope*> objp);
 
-extern NestedStaticScope*
-CloneNestedScopeObject(JSContext* cx, Handle<StaticScope*> enclosingScope,
+extern JSObject*
+CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope,
                        Handle<NestedStaticScope*> src);
 
 
 /*****************************************************************************/
 
 // A scope iterator describes the active scopes starting from a dynamic scope,
 // static scope pair. This pair may be derived from the current point of
 // execution in a frame. If derived in such a fashion, the ScopeIter tracks
@@ -1089,17 +1076,17 @@ class MOZ_RAII ScopeIter
 
   public:
     // Constructing from a copy of an existing ScopeIter.
     ScopeIter(JSContext* cx, const ScopeIter& si
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     // Constructing from a dynamic scope, static scope pair. All scopes are
     // considered not to be withinInitialFrame, since no frame is given.
-    ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope
+    ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     // Constructing from a frame. Places the ScopeIter on the innermost scope
     // at pc.
     ScopeIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     inline bool done() const;
@@ -1113,23 +1100,23 @@ class MOZ_RAII ScopeIter
     Type type() const;
 
     inline bool hasNonSyntacticScopeObject() const;
     inline bool hasSyntacticScopeObject() const;
     inline bool hasAnyScopeObject() const;
     inline bool canHaveSyntacticScopeObject() const;
     ScopeObject& scope() const;
 
-    StaticScope* maybeStaticScope() const;
+    JSObject* maybeStaticScope() const;
     StaticBlockScope& staticBlock() const { return ssi_.block(); }
-    StaticModuleScope& module() const { return ssi_.module(); }
     StaticWithScope& staticWith() const { return ssi_.staticWith(); }
     StaticEvalScope& staticEval() const { return ssi_.eval(); }
     StaticNonSyntacticScope& staticNonSyntactic() const { return ssi_.nonSyntactic(); }
-    StaticFunctionScope& fun() const { return ssi_.fun(); }
+    JSFunction& fun() const { return ssi_.fun(); }
+    ModuleObject& module() const { return ssi_.module(); }
 
     bool withinInitialFrame() const { return !!frame_; }
     AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; }
     AbstractFramePtr maybeInitialFrame() const { return frame_; }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
@@ -1145,28 +1132,28 @@ class MOZ_RAII ScopeIter
 // frame. In other words, the provenance of the scope chain is from allocated
 // closures (i.e., allocation sites) and is irrecoverable from simple stack
 // inspection (i.e., call sites).
 class MissingScopeKey
 {
     friend class LiveScopeVal;
 
     AbstractFramePtr frame_;
-    StaticScope* staticScope_;
+    JSObject* staticScope_;
 
   public:
     explicit MissingScopeKey(const ScopeIter& si)
       : frame_(si.maybeInitialFrame()),
         staticScope_(si.maybeStaticScope())
     { }
 
     AbstractFramePtr frame() const { return frame_; }
-    StaticScope* staticScope() const { return staticScope_; }
+    JSObject* staticScope() const { return staticScope_; }
 
-    void updateStaticScope(StaticScope* scope) { staticScope_ = scope; }
+    void updateStaticScope(JSObject* obj) { staticScope_ = obj; }
     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
 
     // For use as hash policy.
     typedef MissingScopeKey Lookup;
     static HashNumber hash(MissingScopeKey sk);
     static bool match(MissingScopeKey sk1, MissingScopeKey sk2);
     bool operator!=(const MissingScopeKey& other) const {
         return frame_ != other.frame_ || staticScope_ != other.staticScope_;
@@ -1178,28 +1165,28 @@ class MissingScopeKey
 
 // The value in LiveScopeMap, mapped from by live scope objects.
 class LiveScopeVal
 {
     friend class DebugScopes;
     friend class MissingScopeKey;
 
     AbstractFramePtr frame_;
-    RelocatablePtr<StaticScope*> staticScope_;
+    RelocatablePtrObject staticScope_;
 
     static void staticAsserts();
 
   public:
     explicit LiveScopeVal(const ScopeIter& si)
       : frame_(si.initialFrame()),
         staticScope_(si.maybeStaticScope())
     { }
 
     AbstractFramePtr frame() const { return frame_; }
-    StaticScope* staticScope() const { return staticScope_; }
+    JSObject* staticScope() const { return staticScope_; }
 
     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
 
     bool needsSweep();
 };
 
 
 /*****************************************************************************/
@@ -1357,49 +1344,33 @@ template<>
 inline bool
 JSObject::is<js::StaticBlockScope>() const
 {
     return hasClass(&js::ClonedBlockObject::class_) && !getProto();
 }
 
 template<>
 inline bool
-JSObject::is<js::StaticModuleScope>() const
-{
-    return hasClass(&js::ModuleEnvironmentObject::class_) && !getProto();
-}
-
-template<>
-inline bool
 JSObject::is<js::NestedStaticScope>() const
 {
     return is<js::StaticBlockScope>() ||
            is<js::StaticWithScope>();
 }
 
 template<>
 inline bool
 JSObject::is<js::StaticScope>() const
 {
     return is<js::NestedStaticScope>() ||
-           is<js::StaticFunctionScope>() ||
-           is<js::StaticModuleScope>() ||
            is<js::StaticEvalScope>() ||
            is<js::StaticNonSyntacticScope>();
 }
 
 template<>
 inline bool
-JSObject::is<js::ModuleEnvironmentObject>() const
-{
-    return hasClass(&js::ModuleEnvironmentObject::class_) && !!getProto();
-}
-
-template<>
-inline bool
 JSObject::is<js::ClonedBlockObject>() const
 {
     return hasClass(&js::ClonedBlockObject::class_) && !!getProto();
 }
 
 template<>
 inline bool
 JSObject::is<js::NestedScopeObject>() const
@@ -1464,23 +1435,16 @@ IsExtensibleLexicalScope(JSObject* scope
 }
 
 inline bool
 IsGlobalLexicalScope(JSObject* scope)
 {
     return scope->is<ClonedBlockObject>() && scope->as<ClonedBlockObject>().isGlobal();
 }
 
-inline js::StaticScope*
-js::StaticScope::enclosingScope() const
-{
-    JSObject *obj = getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
-    return obj ? &obj->as<StaticScope>() : nullptr;
-}
-
 inline NestedStaticScope*
 NestedStaticScope::enclosingNestedScope() const
 {
     JSObject* obj = getReservedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
     return obj && obj->is<NestedStaticScope>()
            ? &obj->as<NestedStaticScope>()
            : nullptr;
 }
@@ -1564,41 +1528,23 @@ ScopeIter::enclosingScope() const
     // ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
     // chain; every scope chain must start with zero or more ScopeObjects and
     // terminate with one or more non-ScopeObjects (viz., GlobalObject).
     MOZ_ASSERT(done());
     MOZ_ASSERT(!IsSyntacticScope(scope_));
     return *scope_;
 }
 
-inline bool
-js::CallObject::isForEval() const
-{
-    if (is<ModuleEnvironmentObject>())
-        return false;
-    MOZ_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
-    MOZ_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
-                  getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
-    return getFixedSlot(CALLEE_SLOT).isNull();
-}
-
-inline JSFunction&
-js::CallObject::callee() const
-{
-    MOZ_ASSERT(!is<ModuleEnvironmentObject>());
-    return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
-}
-
 extern bool
 CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                 HandleObject dynamicTerminatingScope,
                                 MutableHandleObject dynamicScopeObj);
 
-bool HasNonSyntacticStaticScopeChain(StaticScope* staticScope);
-uint32_t StaticScopeChainLength(StaticScope* staticScope);
+bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
+uint32_t StaticScopeChainLength(JSObject* staticScope);
 
 ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
 
 bool GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                               MutableHandleValue res);
 
 bool CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
                           HandlePropertyName name);
@@ -1610,16 +1556,16 @@ bool CheckGlobalDeclarationConflicts(JSC
                                      Handle<ClonedBlockObject*> lexicalScope,
                                      HandleObject varObj);
 
 bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
                                    HandleObject scopeChain, HandleObject varObj);
 
 #ifdef DEBUG
 void DumpStaticScopeChain(JSScript* script);
-void DumpStaticScopeChain(StaticScope* staticScope);
+void DumpStaticScopeChain(JSObject* staticScope);
 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
@@ -2272,20 +2272,19 @@ CloneObject(JSContext* cx, HandleNativeO
 
         // 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()));
         Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
-        Rooted<StaticScope*> staticGlobalLexical(cx, &globalLexical->staticBlock());
+        RootedObject staticGlobalLexical(cx, &globalLexical->staticBlock());
         clone = CloneFunctionAndScript(cx, selfHostedFunction, globalLexical,
                                        staticGlobalLexical, kind);
-
         // 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(LAZY_FUNCTION_NAME_SLOT,
                                                     StringValue(selfHostedFunction->atom()));
         }
     } else if (selfHostedObject->is<RegExpObject>()) {
         RegExpObject& reobj = selfHostedObject->as<RegExpObject>();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -100,31 +100,31 @@ InterpreterFrame::createRestParameter(JS
                                        ObjectGroup::NewArrayKind::UnknownIndex);
 }
 
 static inline void
 AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* scope)
 {
 #ifdef DEBUG
     RootedObject originalScope(cx, scope);
-    Rooted<StaticScope*> enclosingScope(cx, script->enclosingStaticScope());
+    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>() ||
                    (scope->is<ClonedBlockObject>() &&
                     !scope->as<ClonedBlockObject>().isSyntactic()))
             {
                 MOZ_ASSERT(!IsSyntacticScope(scope));
                 scope = &scope->as<ScopeObject>().enclosingScope();
             }
         } else if (i.hasSyntacticDynamicScopeObject()) {
             switch (i.type()) {
               case StaticScopeIter<NoGC>::Module:
-                MOZ_ASSERT(scope->as<ModuleEnvironmentObject>().module().staticScope() == &i.module());
+                MOZ_ASSERT(scope->as<ModuleEnvironmentObject>().module().script() == i.moduleScript());
                 scope = &scope->as<ModuleEnvironmentObject>().enclosingScope();
                 break;
               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());
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -113,31 +113,31 @@ bool
 XDRState<mode>::codeFunction(MutableHandleFunction objp)
 {
     if (mode == XDR_DECODE)
         objp.set(nullptr);
 
     if (!VersionCheck(this))
         return false;
 
-    Rooted<StaticScope*> staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
+    RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
     return XDRInterpretedFunction(this, staticLexical, nullptr, objp);
 }
 
 template<XDRMode mode>
 bool
 XDRState<mode>::codeScript(MutableHandleScript scriptp)
 {
     if (mode == XDR_DECODE)
         scriptp.set(nullptr);
 
     if (!VersionCheck(this))
         return false;
 
-    Rooted<StaticScope*> staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
+    RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
     if (!XDRScript(this, staticLexical, nullptr, nullptr, scriptp))
         return false;
 
     return true;
 }
 
 template<XDRMode mode>
 bool