Bug 1221144 - Part 8: Change C++ type of static scopes everywhere from JSObject* to StaticScope*. r=shu.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 09 Nov 2015 17:08:58 -0600
changeset 282982 7a9f939bf24daa47024d471dd29c7a9572754f1a
parent 282981 fe0f3776e83ec4ba8863af4dd32d8528259b0f46
child 282983 731746468ebe75d67950049918878678be39d6e8
push id29972
push usercbook@mozilla.com
push dateThu, 04 Feb 2016 11:02:19 +0000
treeherderautoland@f53533d9eb77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
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
Bug 1221144 - Part 8: Change C++ type of static scopes everywhere from JSObject* to StaticScope*. r=shu.
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
js/src/jsapi.cpp
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
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
@@ -471,17 +471,18 @@ 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.
-    RootedObject enclosingStaticScope(cx, script->enclosingStaticScope());
+    Rooted<StaticNonSyntacticScope*> enclosingStaticScope(cx,
+        &script->enclosingStaticScope()->as<StaticNonSyntacticScope>());
     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/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(HandleObject staticScope, bool savedCallerFun = false);
+    bool createScript(Handle<StaticScope*> 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(HandleObject staticScope, bool savedCallerFun)
+BytecodeCompiler::createScript(Handle<StaticScope*> staticScope, bool savedCallerFun)
 {
     script = JSScript::Create(cx, staticScope, savedCallerFun, options,
                               sourceObject, /* sourceStart = */ 0, sourceBuffer.length());
 
     return script != nullptr;
 }
 
 bool
@@ -279,21 +279,18 @@ bool
 BytecodeCompiler::isEvalCompilationUnit()
 {
     return enclosingStaticScope->is<StaticEvalScope>();
 }
 
 bool
 BytecodeCompiler::isNonGlobalEvalCompilationUnit()
 {
-    if (!isEvalCompilationUnit())
-        return false;
-    StaticEvalScope& eval = enclosingStaticScope->as<StaticEvalScope>();
-    JSObject* enclosing = eval.enclosingScopeForStaticScopeIter();
-    return !IsStaticGlobalLexicalScope(enclosing);
+    return isEvalCompilationUnit() &&
+           !IsStaticGlobalLexicalScope(enclosingStaticScope->enclosingScope());
 }
 
 bool
 BytecodeCompiler::isNonSyntacticCompilationUnit()
 {
     return enclosingStaticScope->is<StaticNonSyntacticScope>();
 }
 
@@ -648,17 +645,17 @@ BytecodeCompiler::compileFunctionBody(Mu
         !maybeSetSourceMap(parser->tokenStream))
     {
         return false;
     }
 
     if (fn->pn_funbox->function()->isInterpreted()) {
         MOZ_ASSERT(fun == fn->pn_funbox->function());
 
-        RootedObject scope(cx, fn->pn_funbox->staticScope());
+        Rooted<StaticScope*> scope(cx, fn->pn_funbox->staticScope());
         if (!createScript(scope))
             return false;
 
         script->bindings = fn->pn_funbox->bindings;
 
         if (!createEmitter(fn->pn_funbox) ||
             !emitter->emitFunctionScript(fn->pn_body))
         {
@@ -801,17 +798,17 @@ 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;
 
-    RootedObject staticScope(cx, pn->pn_funbox->staticScope());
+    Rooted<StaticScope*> staticScope(cx, pn->pn_funbox->staticScope());
     MOZ_ASSERT(staticScope);
     RootedScriptSource sourceObject(cx, lazy->sourceObject());
     MOZ_ASSERT(sourceObject);
 
     Rooted<JSScript*> script(cx, JSScript::Create(cx, staticScope, false, options,
                                                   sourceObject, lazy->begin(), lazy->end()));
     if (!script)
         return false;
--- 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;
     }
 }
 
-JSObject*
+StaticScope*
 BytecodeEmitter::innermostStaticScope() const
 {
     if (StmtInfoBCE* stmt = innermostScopeStmt())
         return stmt->staticScope;
     return sc->staticScope();
 }
 
 #ifdef DEBUG
@@ -1357,19 +1357,17 @@ 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
-                              ->as<StaticBlockScope>()
-                              .enclosingStaticScope() == sc->staticScope());
+        MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope());
         return bl;
     }
     return !stmt;
 }
 
 uint32_t
 BytecodeEmitter::computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut)
 {
@@ -3536,19 +3534,19 @@ BytecodeEmitter::emitSetThis(ParseNode* 
     if (!emit1(JSOP_POP))
         return false;
 
     // Emit the set.
     return emitVarOp(name, setOp);
 }
 
 static bool
-IsModuleOnScopeChain(JSObject* obj)
-{
-    for (StaticScopeIter<NoGC> ssi(obj); !ssi.done(); ssi++) {
+IsModuleOnScopeChain(StaticScope* scope)
+{
+    for (StaticScopeIter<NoGC> ssi(scope); !ssi.done(); ssi++) {
         if (ssi.type() == StaticScopeIter<NoGC>::Module)
             return true;
     }
     return false;
 }
 
 bool
 BytecodeEmitter::emitFunctionScript(ParseNode* body)
@@ -6394,19 +6392,18 @@ BytecodeEmitter::emitFunction(ParseNode*
         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*> funScope(cx, fun->lazyScript()->staticScope());
-                RootedObject enclosingScope(cx, innermostStaticScope());
-                funScope->setEnclosingScope(enclosingScope);
+                Rooted<StaticScope*> enclosingScope(cx, innermostStaticScope());
+                fun->lazyScript()->staticScope()->setEnclosingScope(enclosingScope);
 
                 JSObject* source = script->sourceObject();
                 fun->lazyScript()->initSource(&source->as<ScriptSourceObject>());
             }
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
@@ -6417,18 +6414,18 @@ BytecodeEmitter::emitFunction(ParseNode*
             // 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()->as<StaticScope>());
-            RootedObject enclosingScope(cx, innermostStaticScope());
+            Rooted<StaticScope*> funScope(cx, funbox->staticScope());
+            Rooted<StaticScope*> enclosingScope(cx, innermostStaticScope());
             funScope->setEnclosingScope(enclosingScope);
 
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, funScope, false, options,
                                                           sourceObject,
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
                 return false;
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -244,19 +244,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(); }
-    JSObject* innermostStaticScope() const;
-    JSObject* blockScopeOfDef(Definition* dn) const {
-        return parser->blockScopes[dn->pn_blockid];
+    StaticScope* innermostStaticScope() const;
+    StaticScope* blockScopeOfDef(Definition* dn) const {
+        return &parser->blockScopes[dn->pn_blockid].get()->as<StaticScope>();
     }
 
     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/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -114,17 +114,17 @@ 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(JSObject* staticScope)
+SharedContext::computeAllowSyntax(StaticScope* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
         if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().function().isArrow()) {
             // Any function supports new.target.
             allowNewTarget_ = true;
             allowSuperProperty_ = it.fun().function().allowSuperProperty();
             if (it.maybeFunctionBox()) {
                 superScopeAlreadyNeedsHomeObject_ = it.maybeFunctionBox()->needsHomeObject();
@@ -133,17 +133,17 @@ SharedContext::computeAllowSyntax(JSObje
                 allowSuperCall_ = it.fun().function().isDerivedClassConstructor();
             }
             break;
         }
     }
 }
 
 void
-SharedContext::computeThisBinding(JSObject* staticScope)
+SharedContext::computeThisBinding(StaticScope* 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) {
@@ -172,17 +172,17 @@ SharedContext::computeThisBinding(JSObje
             return;
         }
     }
 
     thisBinding_ = ThisBinding::Global;
 }
 
 void
-SharedContext::computeInWith(JSObject* staticScope)
+SharedContext::computeInWith(StaticScope* staticScope)
 {
     for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) {
         if (it.type() == StaticScopeIter<CanGC>::With) {
             inWith_ = true;
             break;
         }
     }
 }
@@ -779,31 +779,31 @@ FunctionBox::FunctionBox(ExclusiveContex
 {
     // 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(JSObject* enclosingStaticScope)
+FunctionBox::initStaticScope(StaticScope* enclosingStaticScope)
 {
     RootedFunction fun(context, function());
-    Rooted<StaticScope*> enclosing(context, &enclosingStaticScope->as<StaticScope>());
+    Rooted<StaticScope*> enclosing(context, enclosingStaticScope);
     staticScope_ = StaticFunctionScope::create(context, fun, enclosing);
     return staticScope_ != nullptr;
 }
 
 template <typename ParseHandler>
 FunctionBox*
 Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun,
                                      ParseContext<ParseHandler>* outerpc,
                                      Directives inheritedDirectives,
                                      GeneratorKind generatorKind,
-                                     JSObject* enclosingStaticScope)
+                                     StaticScope* 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
@@ -1165,17 +1165,17 @@ Parser<SyntaxParseHandler>::defineFuncti
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
                                                  Handle<PropertyNameVector> formals,
                                                  GeneratorKind generatorKind,
                                                  Directives inheritedDirectives,
                                                  Directives* newDirectives,
-                                                 HandleObject enclosingStaticScope)
+                                                 Handle<StaticScope*> enclosingStaticScope)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node fn = handler.newFunctionDefinition();
     if (!fn)
         return null();
 
     ParseNode* argsbody = handler.newList(PNK_ARGSBODY);
@@ -2851,17 +2851,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()->as<StaticFunctionScope>());
+    Rooted<StaticFunctionScope*> funScope(context, funbox->staticScope());
     LazyScript* lazy = LazyScript::CreateRaw(context, fun, funScope,
                                              numFreeVariables, numInnerFunctions,
                                              versionNumber(), funbox->bufStart, funbox->bufEnd,
                                              funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
@@ -3056,17 +3056,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();
 
-    RootedObject enclosing(context, fun->lazyScript()->enclosingScope());
+    Rooted<StaticScope*> 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();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -285,17 +285,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(); }
-    JSObject* innermostStaticScope() const {
+    StaticScope* innermostStaticScope() const {
         if (StmtInfoPC* stmt = innermostScopeStmt())
             return stmt->staticScope;
         return sc->staticScope();
     }
 
     // True if we are at the topmost level of a entire script or function body.
     // For example, while parsing this code we would encounter f1 and f2 at
     // body level, but we would not encounter f3 or f4 at body level:
@@ -305,19 +305,17 @@ 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
-                                  ->template as<StaticBlockScope>()
-                                  .enclosingStaticScope() == sc->staticScope());
+            MOZ_ASSERT_IF(bl, stmt->staticScope->enclosingScope() == sc->staticScope());
             return bl;
         }
         return !stmt;
     }
 
     bool atBodyLevel() {
         return atBodyLevel(innermostStmt());
     }
@@ -502,44 +500,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,
-                                JSObject* enclosingStaticScope);
+                                StaticScope* enclosingStaticScope);
 
     // Use when the funbox is the outermost.
     FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives,
-                                GeneratorKind generatorKind, HandleObject enclosingStaticScope)
+                                GeneratorKind generatorKind, Handle<StaticScope*> 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)
     {
-        RootedObject enclosing(context, outerpc->innermostStaticScope());
+        Rooted<StaticScope*> 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(JSObject* staticScope, uint32_t* blockIdOut) {
+    bool generateBlockId(StaticScope* 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);
     }
@@ -595,17 +593,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,
-                                HandleObject enclosingStaticScope);
+                                Handle<StaticScope*> 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 JSObject* staticScope() const = 0;
-    void computeAllowSyntax(JSObject* staticScope);
-    void computeInWith(JSObject* staticScope);
-    void computeThisBinding(JSObject* staticScope);
+    virtual StaticScope* staticScope() const = 0;
+    void computeAllowSyntax(StaticScope* staticScope);
+    void computeInWith(StaticScope* staticScope);
+    void computeThisBinding(StaticScope* 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(); }
@@ -300,17 +300,17 @@ class MOZ_STACK_CLASS GlobalSharedContex
         // non-function scope, so we have to compute our ThisBinding based on
         // the actual callee.
         if (maybeEvalCaller)
             computeThisBinding(maybeEvalCaller->nonLazyScript()->staticScope());
         else
             computeThisBinding(staticScope);
     }
 
-    JSObject* staticScope() const override { return staticScope_; }
+    StaticScope* staticScope() const override { return staticScope_; }
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
     StaticFunctionScope* staticScope_;
     uint32_t        bufStart;
@@ -333,22 +333,22 @@ class FunctionBox : public ObjectBox, pu
 
     FunctionContextFlags funCxFlags;
 
     template <typename ParseHandler>
     FunctionBox(ExclusiveContext* cx, ObjectBox* traceListHead, JSFunction* fun,
                 ParseContext<ParseHandler>* pc, Directives directives, bool extraWarnings,
                 GeneratorKind generatorKind);
 
-    bool initStaticScope(JSObject* enclosingStaticScope);
+    bool initStaticScope(StaticScope* enclosingStaticScope);
 
     ObjectBox* toObjectBox() override { return this; }
     JSFunction* function() const { return &object->as<JSFunction>(); }
-    JSObject* staticScope() const override { return staticScope_; }
-    JSObject* enclosingStaticScope() const { return staticScope_->enclosingScope(); }
+    StaticFunctionScope* staticScope() const override { return staticScope_; }
+    StaticScope* enclosingStaticScope() const { return staticScope_->enclosingScope(); }
 
     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 +424,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>(); }
-    JSObject* staticScope() const override { return module()->staticScope(); }
+    StaticModuleScope* staticScope() const override { return module()->staticScope(); }
 
     void trace(JSTracer* trc) override;
 };
 
 inline FunctionBox*
 SharedContext::asFunctionBox()
 {
     MOZ_ASSERT(isFunctionBox());
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3451,18 +3451,19 @@ 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()) {
-        staticScopeObj.set(StaticNonSyntacticScope::create(cx, staticScopeObj));
-        if (!staticScopeObj)
+        Rooted<StaticNonSyntacticScope*> scope(cx,
+            StaticNonSyntacticScope::create(cx, staticScopeObj));
+        if (!scope)
             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.
         //
@@ -3473,51 +3474,52 @@ 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, staticScopeObj,
-                                                                   dynamicScopeObj));
+            cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, scope, 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 (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) {
+    if (StaticScope* 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;
 
-            JSObject* enclosing = block.enclosingStaticScope();
+            StaticScope* enclosing = block.enclosingScope();
 
             // 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,
-                                                   HandleObject enclosingStatic,
+                                                   Handle<StaticNonSyntacticScope*> 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,16 +27,17 @@ 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.
  */
@@ -524,19 +525,20 @@ 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::HandleObject enclosingStatic,
-                                                               js::HandleObject enclosingScope);
+    js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(
+        JSContext* cx,
+        js::Handle<js::StaticNonSyntacticScope*> 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/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -531,18 +531,18 @@ fun_resolve(JSContext* cx, HandleObject 
         return true;
     }
 
     return true;
 }
 
 template<XDRMode mode>
 bool
-js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-                           MutableHandleFunction objp)
+js::XDRInterpretedFunction(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+                           HandleScript enclosingScript, MutableHandleFunction objp)
 {
     enum FirstWordFlag {
         HasAtom             = 0x1,
         IsStarGenerator     = 0x2,
         IsLazy              = 0x4,
         HasSingletonType    = 0x8
     };
 
@@ -648,20 +648,22 @@ js::XDRInterpretedFunction(XDRState<mode
             return false;
         objp.set(fun);
     }
 
     return true;
 }
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, MutableHandleFunction);
+js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript,
+                           MutableHandleFunction);
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleObject, HandleScript, MutableHandleFunction);
+js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, Handle<StaticScope*>, 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)
@@ -1444,17 +1446,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) {
-            RootedObject enclosingScope(cx, lazy->enclosingScope());
+            Rooted<StaticScope*> 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());
 
@@ -2051,17 +2053,17 @@ js::CloneFunctionReuseScript(JSContext* 
      */
     if (fun->getProto() == clone->getProto())
         clone->setGroup(fun->group());
     return clone;
 }
 
 JSFunction*
 js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
-                           HandleObject newStaticScope,
+                           Handle<StaticScope*> 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,
-                       HandleObject newStaticScope,
+                       Handle<StaticScope*> 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, HandleObject enclosingScope,
+XDRInterpretedFunction(XDRState<mode>* xdr, Handle<StaticScope*> 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;
-    RootedObject staticScope(cx, script->enclosingStaticScope());
+    Rooted<StaticScope*> staticScope(cx, script->enclosingStaticScope());
     return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto);
 }
 
 } /* namespace js */
 
 #endif /* jsfuninlines_h */
--- 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,
-                      HandleObject funScope, MutableHandle<LazyScript*> lazy)
+                      Handle<StaticFunctionScope*> funScope, 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.as<StaticFunctionScope>(),
-                                        script, packedFields, begin, end, lineno, column));
+            lazy.set(LazyScript::Create(cx, fun, script, funScope, 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);
         }
     }
 
@@ -587,18 +587,18 @@ enum XDRClassKind {
     CK_BlockObject = 0,
     CK_WithObject  = 1,
     CK_JSFunction  = 2,
     CK_JSObject    = 3
 };
 
 template<XDRMode mode>
 bool
-js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript,
-              HandleFunction fun, MutableHandleScript scriptp)
+js::XDRScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScopeArg,
+              HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp)
 {
     /* NB: Keep this in sync with CopyScript. */
 
     MOZ_ASSERT(enclosingScopeArg);
 
     enum ScriptBits {
         NoScriptRval,
         SavedCallerFun,
@@ -632,17 +632,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);
-    RootedObject enclosingScope(cx, enclosingScopeArg);
+    Rooted<StaticScope*> 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;
@@ -845,18 +845,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             IsStaticGlobalLexicalScope(enclosingScope))
         {
             enclosingScope = StaticNonSyntacticScope::create(cx, enclosingScope);
             if (!enclosingScope)
                 return false;
         }
 
         if (fun) {
-            enclosingScope = StaticFunctionScope::create(cx, fun,
-                                                         enclosingScope.as<StaticScope>());
+            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;
@@ -1047,49 +1046,54 @@ js::XDRScript(XDRState<mode>* xdr, Handl
                     else
                         enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
                 } else {
                     enclosingStaticScopeIndex = UINT32_MAX;
                 }
             }
             if (!xdr->codeUint32(&enclosingStaticScopeIndex))
                 return false;
-            Rooted<JSObject*> enclosingStaticScope(cx);
+            Rooted<StaticScope*> enclosingStaticScope(cx);
             if (mode == XDR_DECODE) {
                 if (enclosingStaticScopeIndex != UINT32_MAX) {
                     MOZ_ASSERT(enclosingStaticScopeIndex < i);
-                    enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
+                    enclosingStaticScope = &script->objects()->vector[enclosingStaticScopeIndex]
+                                                             ->as<StaticScope>();
                 } else {
                     // This is not ternary because MSVC can't typecheck the
                     // ternary.
                     if (fun)
                         enclosingStaticScope = script->staticScope();
                     else
                         enclosingStaticScope = enclosingScope;
                 }
             }
 
             if (classk == CK_BlockObject) {
-                Rooted<StaticBlockScope*> tmp(cx, static_cast<StaticBlockScope*>(objp->get()));
+                Rooted<StaticBlockScope*> tmp(cx);
+                if (mode == XDR_ENCODE)
+                    tmp = &(*objp)->as<StaticBlockScope>();
                 if (!XDRStaticBlockScope(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
             } else {
-                Rooted<StaticWithScope*> tmp(cx, static_cast<StaticWithScope*>(objp->get()));
+                Rooted<StaticWithScope*> tmp(cx);
+                if (mode == XDR_ENCODE)
+                    tmp = &(*objp)->as<StaticWithScope>();
                 if (!XDRStaticWithScope(xdr, enclosingStaticScope, &tmp))
                     return false;
                 *objp = tmp;
             }
             break;
           }
 
           case CK_JSFunction: {
             /* Code the nested function's enclosing scope. */
             uint32_t funEnclosingScopeIndex = 0;
-            RootedObject funEnclosingScope(cx);
+            Rooted<StaticScope*> 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 {
@@ -1131,17 +1135,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
                     // This is not ternary because MSVC can't typecheck the
                     // ternary.
                     if (fun)
                         funEnclosingScope = script->staticScope();
                     else
                         funEnclosingScope = enclosingScope;
                 } else {
                     MOZ_ASSERT(funEnclosingScopeIndex < i);
-                    funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
+                    funEnclosingScope = &script->objects()->vector[funEnclosingScopeIndex]
+                                                          .get()->as<StaticScope>();
                 }
             }
 
             // Code nested function and script.
             RootedFunction tmp(cx);
             if (mode == XDR_ENCODE)
                 tmp = &(*objp)->as<JSFunction>();
             if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
@@ -1208,17 +1213,20 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             return false;
     }
 
     if (scriptBits & (1 << HasLazyScript)) {
         Rooted<LazyScript*> lazy(cx);
         if (mode == XDR_ENCODE)
             lazy = script->maybeLazyScript();
 
-        if (!XDRRelazificationInfo(xdr, fun, script, enclosingScope, &lazy))
+        Rooted<StaticFunctionScope*> lazyScope(cx, mode == XDR_DECODE
+                                                   ? &enclosingScope->as<StaticFunctionScope>()
+                                                   : nullptr);
+        if (!XDRRelazificationInfo(xdr, fun, script, lazyScope, &lazy))
             return false;
 
         if (mode == XDR_DECODE)
             script->setLazyScript(lazy);
     }
 
     if (mode == XDR_DECODE) {
         scriptp.set(script);
@@ -1227,27 +1235,28 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (!fun)
             Debugger::onNewScript(cx, script);
     }
 
     return true;
 }
 
 template bool
-js::XDRScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, HandleFunction,
+js::XDRScript(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript, HandleFunction,
               MutableHandleScript);
 
 template bool
-js::XDRScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript, HandleFunction,
+js::XDRScript(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript, HandleFunction,
               MutableHandleScript);
 
 template<XDRMode mode>
 bool
-js::XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-                  HandleFunction fun, MutableHandle<LazyScript*> lazy)
+js::XDRLazyScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+                  HandleScript enclosingScript, HandleFunction fun,
+                  MutableHandle<LazyScript*> lazy)
 {
     JSContext* cx = xdr->cx();
 
     {
         uint32_t begin;
         uint32_t end;
         uint32_t lineno;
         uint32_t column;
@@ -1270,19 +1279,18 @@ 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<StaticScope*> enclosingStaticScope(cx, &enclosingScope->as<StaticScope>());
             Rooted<StaticFunctionScope*> funScope(cx,
-                StaticFunctionScope::create(cx, fun, enclosingStaticScope));
+                StaticFunctionScope::create(cx, fun, enclosingScope));
             if (!funScope)
                 return false;
             lazy.set(LazyScript::Create(cx, fun, nullptr, funScope, enclosingScript,
                                         packedFields, begin, end, lineno, column));
             if (!lazy)
                 return false;
             fun->initLazyScript(lazy);
         }
@@ -1309,21 +1317,21 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
                 innerFunctions[i] = func;
         }
     }
 
     return true;
 }
 
 template bool
-js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript,
+js::XDRLazyScript(XDRState<XDR_ENCODE>*, Handle<StaticScope*>, HandleScript,
                   HandleFunction, MutableHandle<LazyScript*>);
 
 template bool
-js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript,
+js::XDRLazyScript(XDRState<XDR_DECODE>*, Handle<StaticScope*>, HandleScript,
                   HandleFunction, MutableHandle<LazyScript*>);
 
 void
 JSScript::setSourceObject(JSObject* object)
 {
     MOZ_ASSERT(compartment() == object->compartment());
     sourceObject_ = object;
 }
@@ -2755,43 +2763,43 @@ ScriptDataSize(uint32_t nbindings, uint3
 
 void
 JSScript::initCompartment(ExclusiveContext* cx)
 {
     compartment_ = cx->compartment_;
 }
 
 /* static */ JSScript*
-JSScript::Create(ExclusiveContext* cx, HandleObject staticScope, bool savedCallerFun,
+JSScript::Create(ExclusiveContext* cx, Handle<StaticScope*> staticScope, 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 ? &staticScope->as<StaticScope>() : nullptr;
+    script->staticScope_ = staticScope;
     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.
-    RootedObject enclosingScope(cx, staticScope);
+    Rooted<StaticScope*> enclosingScope(cx, staticScope);
     if (staticScope && staticScope->is<StaticFunctionScope>())
-        enclosingScope = staticScope->as<StaticScope>().enclosingScope();
+        enclosingScope = staticScope->enclosingScope();
     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;
@@ -3424,17 +3432,18 @@ template <class T>
 static inline T*
 Rebase(JSScript* dst, JSScript* src, T* srcp)
 {
     size_t off = reinterpret_cast<uint8_t*>(srcp) - src->data;
     return reinterpret_cast<T*>(dst->data + off);
 }
 
 static JSObject*
-CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun)
+CloneInnerInterpretedFunction(JSContext* cx, Handle<StaticScope*> 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;
     }
@@ -3464,17 +3473,17 @@ CloneInnerInterpretedFunction(JSContext*
 
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return nullptr;
 
     return clone;
 }
 
 bool
-js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
+js::detail::CopyScript(JSContext* cx, Handle<StaticScope*> 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;
     }
 
@@ -3511,24 +3520,25 @@ 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>());
 
-                RootedObject enclosingScope(cx);
+                Rooted<StaticScope*> 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)];
+                        enclosingScope = &objects[FindScopeObjectIndex(src, *enclosingBlock)]
+                                                 .get()->as<StaticScope>();
                     }
                 } else {
                     enclosingScope = scriptStaticScope;
                 }
 
                 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
             } else if (obj->is<JSFunction>()) {
                 RootedFunction innerFun(cx, &obj->as<JSFunction>());
@@ -3540,34 +3550,37 @@ js::detail::CopyScript(JSContext* cx, Ha
                     }
                     clone = innerFun;
                 } else {
                     if (innerFun->isInterpretedLazy()) {
                         AutoCompartment ac(cx, innerFun);
                         if (!innerFun->getOrCreateScript(cx))
                             return false;
                     }
-                    RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
+                    Rooted<StaticScope*> staticScope(cx, innerFun->nonLazyScript()
+                                                                 ->enclosingStaticScope());
                     StaticScopeIter<CanGC> ssi(cx, staticScope);
-                    RootedObject enclosingScope(cx);
+                    Rooted<StaticScope*> enclosingScope(cx);
                     if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::NonSyntactic) {
                         enclosingScope = scriptStaticScope;
                     } else if (ssi.type() == StaticScopeIter<CanGC>::Function) {
                         MOZ_ASSERT(scriptStaticScope->is<StaticFunctionScope>());
                         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())];
+                            enclosingScope = &objects[FindScopeObjectIndex(src, ssi.block())]
+                                                     .get()->as<StaticBlockScope>();
                         }
                     } else {
-                        enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
+                        enclosingScope = &objects[FindScopeObjectIndex(src, ssi.staticWith())]
+                                                 .get()->as<StaticWithScope>();
                     }
 
                     clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun);
                 }
             } else {
                 clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
             }
             if (!clone || !objects.append(clone))
@@ -3671,17 +3684,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, HandleObject enclosingScope, HandleScript src)
+CreateEmptyScriptForClone(JSContext* cx, Handle<StaticScope*> 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())) {
@@ -3723,23 +3736,23 @@ js::CloneGlobalScript(JSContext* cx, Han
 
     if (!detail::CopyScript(cx, enclosingScope, src, dst))
         return nullptr;
 
     return dst;
 }
 
 JSScript*
-js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
+js::CloneScriptIntoFunction(JSContext* cx, Handle<StaticScope*> enclosingScope, HandleFunction fun,
                             HandleScript src)
 {
     MOZ_ASSERT(fun->isInterpreted());
 
-    Rooted<StaticScope*> enclosing(cx, &enclosingScope->as<StaticScope>());
-    Rooted<StaticFunctionScope*> funScope(cx, StaticFunctionScope::create(cx, fun, enclosing));
+    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
@@ -4103,28 +4116,28 @@ JSScript::getStaticBlockScope(jsbytecode
         } else {
             top = mid;
         }
     }
 
     return blockChain;
 }
 
-JSObject*
+StaticScope*
 JSScript::innermostStaticScopeInScript(jsbytecode* pc)
 {
-    if (JSObject* scope = getStaticBlockScope(pc))
+    if (NestedStaticScope* scope = getStaticBlockScope(pc))
         return scope;
     return staticScope_;
 }
 
-JSObject*
+StaticScope*
 JSScript::innermostStaticScope(jsbytecode* pc)
 {
-    if (JSObject* scope = innermostStaticScopeInScript(pc))
+    if (StaticScope* scope = innermostStaticScopeInScript(pc))
         return scope;
     return enclosingStaticScope();
 }
 
 void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
@@ -4159,18 +4172,23 @@ 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)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -62,17 +62,18 @@ namespace frontend {
     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, HandleObject scriptStaticScope, HandleScript src, HandleScript dst);
+CopyScript(JSContext* cx, Handle<StaticScope*> 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
@@ -142,17 +143,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, HandleObject scriptStaticScope, HandleScript src,
+    detail::CopyScript(JSContext* cx, Handle<StaticScope*> 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;
@@ -916,47 +917,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, HandleObject enclosingScope, HandleScript enclosingScript,
-          HandleFunction fun, MutableHandleScript scriptp);
-
-template<XDRMode mode>
+XDRScript(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
+          HandleScript enclosingScript, HandleFunction fun, MutableHandleScript scriptp);
+
+template <XDRMode mode>
 bool
-XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
-              HandleFunction fun, MutableHandle<LazyScript*> lazy);
+XDRLazyScript(XDRState<mode>* xdr, Handle<StaticScope*> 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::HandleObject enclosingScope,
+    js::XDRScript(js::XDRState<mode>* xdr, js::Handle<js::StaticScope*> enclosingScope,
                   js::HandleScript enclosingScript,
                   js::HandleFunction fun, js::MutableHandleScript scriptp);
 
     friend bool
-    js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src,
-                           js::HandleScript dst);
+    js::detail::CopyScript(JSContext* cx, js::Handle<js::StaticScope*> 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.
@@ -1224,17 +1225,17 @@ class JSScript : public js::gc::TenuredC
 #endif
 
     //
     // End of fields.  Start methods.
     //
 
   public:
     static JSScript* Create(js::ExclusiveContext* cx,
-                            js::HandleObject staticScope, bool savedCallerFun,
+                            js::Handle<js::StaticScope*> staticScope, 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
@@ -1715,17 +1716,17 @@ class JSScript : public js::gc::TenuredC
     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 JSObject* enclosingStaticScope() const;
+    inline js::StaticScope* enclosingStaticScope() const;
 
     // 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);
 
@@ -1886,23 +1887,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.
-    JSObject* innermostStaticScopeInScript(jsbytecode* pc);
+    js::StaticScope* innermostStaticScopeInScript(jsbytecode* pc);
 
     // As innermostStaticScopeInScript, but returns the enclosing static scope
     // if the innermost static scope falls without the extent of the script.
-    JSObject* innermostStaticScope(jsbytecode* pc);
-
-    JSObject* innermostStaticScope() { return innermostStaticScope(main()); }
+    js::StaticScope* innermostStaticScope(jsbytecode* pc);
+
+    js::StaticScope* 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)
@@ -2266,17 +2267,17 @@ class LazyScript : public gc::TenuredCel
     const JSScript* maybeScriptUnbarriered() const {
         return script_.unbarrieredGet();
     }
     bool hasScript() const {
         return bool(script_);
     }
 
     StaticFunctionScope* staticScope() const { return staticScope_; }
-    JSObject* enclosingScope() const;
+    StaticScope* enclosingScope() const;
 
     // 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();
@@ -2545,29 +2546,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, HandleObject enclosingScope, HandleFunction fun,
+CloneScriptIntoFunction(JSContext* cx, Handle<StaticScope*> 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,25 +87,25 @@ SetFrameArgumentsObject(JSContext* cx, A
 inline JSFunction*
 LazyScript::functionDelazifying(JSContext* cx) const
 {
     if (function_ && !function_->getOrCreateScript(cx))
         return nullptr;
     return function_;
 }
 
-inline JSObject*
+inline StaticScope*
 LazyScript::enclosingScope() const
 {
     return staticScope_->enclosingScope();
 }
 
 } // namespace js
 
-inline JSObject*
+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_;
 }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -922,19 +922,18 @@ 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,
-                       HandleObject staticWith)
-{
-    MOZ_ASSERT(staticWith->is<StaticWithScope>());
+                       Handle<StaticWithScope*> staticWith)
+{
     RootedObject obj(cx);
     if (val.isObject()) {
         obj = &val.toObject();
     } else {
         obj = ToObject(cx, val);
         if (!obj)
             return false;
     }
@@ -1489,16 +1488,19 @@ 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>>
@@ -1868,17 +1870,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))
+    if (!EnterWithOperation(cx, REGS.fp(), val, staticWith.as<StaticWithScope>()))
         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,16 +16,17 @@
 
 #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
@@ -423,18 +424,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, HandleObject staticWith);
-
+EnterWithOperation(JSContext* cx, AbstractFramePtr frame, HandleValue val,
+                   Handle<StaticWithScope*> 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,55 +79,49 @@ LexicalScopeBase::initAliasedLexicalsToT
 {
     initRemainingSlotsToUninitializedLexicals(script->bindings.aliasedBodyLevelLexicalBegin());
 }
 
 template <AllowGC allowGC>
 inline void
 StaticScopeIter<allowGC>::operator++(int)
 {
-    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<StaticModuleScope>()) {
-        obj = obj->template as<StaticModuleScope>().enclosingScope();
-    } else if (onNamedLambda || !obj->template as<StaticFunctionScope>().isNamedLambda()) {
+    if (!scope->template is<StaticFunctionScope>()) {
+        scope = scope->enclosingScope();
+    } else if (onNamedLambda || !scope->template as<StaticFunctionScope>().isNamedLambda()) {
         onNamedLambda = false;
-        obj = obj->template as<StaticFunctionScope>().enclosingScope();
+        scope = scope->enclosingScope();
     } else {
         onNamedLambda = true;
     }
-    MOZ_ASSERT_IF(obj, IsStaticScope(obj));
-    MOZ_ASSERT_IF(onNamedLambda, obj->template is<StaticFunctionScope>());
+    MOZ_ASSERT_IF(scope, IsStaticScope(scope));
+    MOZ_ASSERT_IF(onNamedLambda, scope->template is<StaticFunctionScope>());
 }
 
 template <AllowGC allowGC>
 inline bool
 StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
 {
-    if (obj->template is<StaticFunctionScope>()) {
-        JSFunction& fun = obj->template as<StaticFunctionScope>().function();
+    if (scope->template is<StaticFunctionScope>()) {
+        JSFunction& fun = scope->template as<StaticFunctionScope>().function();
         if (fun.isBeingParsed())
             return fun.functionBox()->needsCallObject();
         return fun.needsCallObject();
     }
-    if (obj->template is<StaticModuleScope>())
+    if (scope->template is<StaticModuleScope>())
         return true;
-    if (obj->template is<StaticBlockScope>()) {
-        return obj->template as<StaticBlockScope>().needsClone() ||
-               obj->template as<StaticBlockScope>().isGlobal();
+    if (scope->template is<StaticBlockScope>()) {
+        return scope->template as<StaticBlockScope>().needsClone() ||
+               scope->template as<StaticBlockScope>().isGlobal();
     }
-    if (obj->template is<StaticWithScope>())
+    if (scope->template is<StaticWithScope>())
         return true;
-    if (obj->template is<StaticEvalScope>())
-        return obj->template as<StaticEvalScope>().isStrict();
-    MOZ_ASSERT(obj->template is<StaticNonSyntacticScope>());
+    if (scope->template is<StaticEvalScope>())
+        return scope->template as<StaticEvalScope>().isStrict();
+    MOZ_ASSERT(scope->template is<StaticNonSyntacticScope>());
     return false;
 }
 
 template <AllowGC allowGC>
 inline Shape*
 StaticScopeIter<allowGC>::scopeShape() const
 {
     MOZ_ASSERT(hasSyntacticDynamicScopeObject());
@@ -140,76 +134,76 @@ StaticScopeIter<allowGC>::scopeShape() c
 }
 
 template <AllowGC allowGC>
 inline typename StaticScopeIter<allowGC>::Type
 StaticScopeIter<allowGC>::type() const
 {
     if (onNamedLambda)
         return NamedLambda;
-    if (obj->template is<StaticBlockScope>())
+    if (scope->template is<StaticBlockScope>())
         return Block;
-    if (obj->template is<StaticModuleScope>())
+    if (scope->template is<StaticModuleScope>())
         return Module;
-    if (obj->template is<StaticWithScope>())
+    if (scope->template is<StaticWithScope>())
         return With;
-    if (obj->template is<StaticEvalScope>())
+    if (scope->template is<StaticEvalScope>())
         return Eval;
-    if (obj->template is<StaticNonSyntacticScope>())
+    if (scope->template is<StaticNonSyntacticScope>())
         return NonSyntactic;
-    MOZ_ASSERT(obj->template is<StaticFunctionScope>());
+    MOZ_ASSERT(scope->template is<StaticFunctionScope>());
     return Function;
 }
 
 template <AllowGC allowGC>
 inline StaticBlockScope&
 StaticScopeIter<allowGC>::block() const
 {
     MOZ_ASSERT(type() == Block);
-    return obj->template as<StaticBlockScope>();
+    return scope->template as<StaticBlockScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticWithScope&
 StaticScopeIter<allowGC>::staticWith() const
 {
     MOZ_ASSERT(type() == With);
-    return obj->template as<StaticWithScope>();
+    return scope->template as<StaticWithScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticEvalScope&
 StaticScopeIter<allowGC>::eval() const
 {
     MOZ_ASSERT(type() == Eval);
-    return obj->template as<StaticEvalScope>();
+    return scope->template as<StaticEvalScope>();
 }
 
 template <AllowGC allowGC>
 inline StaticNonSyntacticScope&
 StaticScopeIter<allowGC>::nonSyntactic() const
 {
     MOZ_ASSERT(type() == NonSyntactic);
-    return obj->template as<StaticNonSyntacticScope>();
+    return scope->template as<StaticNonSyntacticScope>();
 }
 
 template <AllowGC allowGC>
 inline JSScript*
 StaticScopeIter<allowGC>::funScript() const
 {
     MOZ_ASSERT(type() == Function);
-    return obj->template as<StaticFunctionScope>().function().nonLazyScript();
+    return scope->template as<StaticFunctionScope>().function().nonLazyScript();
 }
 
 template <AllowGC allowGC>
 inline StaticFunctionScope&
 StaticScopeIter<allowGC>::fun() const
 {
     MOZ_ASSERT(type() == Function);
-    return obj->template as<StaticFunctionScope>();
+    return scope->template as<StaticFunctionScope>();
 }
 
 template <AllowGC allowGC>
 inline frontend::FunctionBox*
 StaticScopeIter<allowGC>::maybeFunctionBox() const
 {
     MOZ_ASSERT(type() == Function);
     if (fun().function().isBeingParsed())
@@ -217,17 +211,17 @@ StaticScopeIter<allowGC>::maybeFunctionB
     return nullptr;
 }
 
 template <AllowGC allowGC>
 inline StaticModuleScope&
 StaticScopeIter<allowGC>::module() const
 {
     MOZ_ASSERT(type() == Module);
-    return obj->template as<StaticModuleScope>();
+    return scope->template as<StaticModuleScope>();
 }
 
 }  /* namespace js */
 
 inline JSObject*
 JSObject::enclosingScope()
 {
     if (is<js::ScopeObject>())
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -184,17 +184,17 @@ const Class StaticWithScope::class_ = {
 StaticWithScope*
 StaticWithScope::create(ExclusiveContext* cx)
 {
     return NewObjectWithNullTaggedProto<StaticWithScope>(cx, TenuredObject, BaseShape::DELEGATE);
 }
 
 template<XDRMode mode>
 bool
-js::XDRStaticWithScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+js::XDRStaticWithScope(XDRState<mode>* xdr, Handle<StaticScope*> 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,20 +203,22 @@ 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>*, HandleObject, MutableHandle<StaticWithScope*>);
+js::XDRStaticWithScope(XDRState<XDR_ENCODE>*, Handle<StaticScope*>,
+                       MutableHandle<StaticWithScope*>);
 
 template bool
-js::XDRStaticWithScope(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticWithScope*>);
+js::XDRStaticWithScope(XDRState<XDR_DECODE>*, Handle<StaticScope*>,
+                       MutableHandle<StaticWithScope*>);
 
 
 /*****************************************************************************/
 
 Shape*
 js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc)
 {
     MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
@@ -770,34 +772,33 @@ DeclEnvObject::create(JSContext* cx, Han
     if (!obj)
         return nullptr;
 
     obj->setEnclosingScope(enclosing);
     obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
     return obj;
 }
 
-static JSObject*
-CloneStaticWithScope(JSContext* cx, HandleObject enclosingScope, Handle<StaticWithScope*> srcWith)
+static StaticWithScope*
+CloneStaticWithScope(JSContext* cx, Handle<StaticScope*> 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,
-                          HandleObject staticWith, WithKind kind)
+                          Handle<StaticWithScope*> 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);
@@ -1043,20 +1044,20 @@ ClonedBlockObject::createGlobal(JSContex
     if (!lexical)
         return nullptr;
     if (!JSObject::setSingleton(cx, lexical))
         return nullptr;
     return lexical;
 }
 
 /* static */ ClonedBlockObject*
-ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
+ClonedBlockObject::createNonSyntactic(JSContext* cx,
+                                      Handle<StaticNonSyntacticScope*> 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);
@@ -1165,17 +1166,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, HandleObject enclosingScope,
+js::XDRStaticBlockScope(XDRState<mode>* xdr, Handle<StaticScope*> 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;
@@ -1271,23 +1272,26 @@ js::XDRStaticBlockScope(XDRState<mode>* 
             if (!xdr->codeUint32(&propFlags))
                 return false;
         }
     }
     return true;
 }
 
 template bool
-js::XDRStaticBlockScope(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticBlockScope*>);
+js::XDRStaticBlockScope(XDRState<XDR_ENCODE>*, Handle<StaticScope*>,
+                        MutableHandle<StaticBlockScope*>);
 
 template bool
-js::XDRStaticBlockScope(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticBlockScope*>);
-
-static JSObject*
-CloneStaticBlockScope(JSContext* cx, HandleObject enclosingScope, Handle<StaticBlockScope*> srcBlock)
+js::XDRStaticBlockScope(XDRState<XDR_DECODE>*, Handle<StaticScope*>,
+                        MutableHandle<StaticBlockScope*>);
+
+static StaticBlockScope*
+CloneStaticBlockScope(JSContext* cx, Handle<StaticScope*> 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);
@@ -1318,26 +1322,26 @@ CloneStaticBlockScope(JSContext* cx, Han
     if (!srcBlock->isExtensible()) {
         if (!clone->makeNonExtensible(cx))
             return nullptr;
     }
 
     return clone;
 }
 
-JSObject*
-js::CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope,
+NestedStaticScope*
+js::CloneNestedScopeObject(JSContext* cx, Handle<StaticScope*> enclosingScope,
                            Handle<NestedStaticScope*> srcBlock)
 {
     if (srcBlock->is<StaticBlockScope>()) {
-        Rooted<StaticBlockScope*> blockScope(cx, &srcBlock->as<StaticBlockScope>());
-        return CloneStaticBlockScope(cx, enclosingScope, blockScope);
+        return CloneStaticBlockScope(cx, enclosingScope,
+                                     HandleObject(srcBlock).as<StaticBlockScope>());
     } else {
-        Rooted<StaticWithScope*> withScope(cx, &srcBlock->as<StaticWithScope>());
-        return CloneStaticWithScope(cx, enclosingScope, withScope);
+        return CloneStaticWithScope(cx, enclosingScope,
+                                    HandleObject(srcBlock).as<StaticWithScope>());
     }
 }
 
 /* static */ RuntimeLexicalErrorObject*
 RuntimeLexicalErrorObject::create(JSContext* cx, HandleObject enclosing, unsigned errorNumber)
 {
     RuntimeLexicalErrorObject* obj =
         NewObjectWithNullTaggedProto<RuntimeLexicalErrorObject>(cx, GenericObject,
@@ -1453,17 +1457,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, JSObject* staticScope
+ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, StaticScope* staticScope
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : ssi_(cx, staticScope),
     scope_(cx, scope),
     frame_(NullFramePtr())
 {
     settle();
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
@@ -1609,35 +1613,30 @@ ScopeIter::type() const
 
 ScopeObject&
 ScopeIter::scope() const
 {
     MOZ_ASSERT(hasAnyScopeObject());
     return scope_->as<ScopeObject>();
 }
 
-JSObject*
+StaticScope*
 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 &staticNonSyntactic();
+        return ssi_.staticScope();
       case StaticScopeIter<CanGC>::NamedLambda:
         MOZ_CRASH("named lambda static scopes should have been skipped");
       default:
         MOZ_CRASH("bad SSI type");
     }
 }
 
 /* static */ HashNumber
@@ -3156,17 +3155,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);
-    RootedObject staticEnclosingScope(cx);
+    Rooted<StaticWithScope*> 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;
@@ -3178,31 +3177,31 @@ js::CreateScopeObjectsForScopeChain(JSCo
         dynamicEnclosingScope = dynamicWith;
     }
 
     dynamicScopeObj.set(dynamicEnclosingScope);
     return true;
 }
 
 bool
-js::HasNonSyntacticStaticScopeChain(JSObject* staticScope)
+js::HasNonSyntacticStaticScopeChain(StaticScope* 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(JSObject* staticScope)
+js::StaticScopeChainLength(StaticScope* staticScope)
 {
     uint32_t length = 0;
     for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++)
         length++;
     return length;
 }
 
 ModuleEnvironmentObject*
@@ -3413,17 +3412,17 @@ js::CheckEvalDeclarationConflicts(JSCont
 
 void
 js::DumpStaticScopeChain(JSScript* script)
 {
     DumpStaticScopeChain(script->enclosingStaticScope());
 }
 
 void
-js::DumpStaticScopeChain(JSObject* staticScope)
+js::DumpStaticScopeChain(StaticScope* 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()) {
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -77,23 +77,21 @@ 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 JSObject* enclosingScope() const {
-        return getFixedSlot(ENCLOSING_SCOPE_SLOT).toObjectOrNull();
-    }
+    inline StaticScope* enclosingScope() const;
 
-    void initEnclosingScope(JSObject* obj) {
+    void initEnclosingScope(StaticScope* scope) {
         MOZ_ASSERT(getReservedSlot(ENCLOSING_SCOPE_SLOT).isUndefined());
-        setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(obj));
+        setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(scope));
     }
 
     void setEnclosingScope(JSObject* obj);
 };
 
 class NestedStaticScope : public StaticScope
 {
   public:
@@ -104,17 +102,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(JSObject* prev) {
+    void initEnclosingScopeFromParser(StaticScope* prev) {
         setReservedSlot(ENCLOSING_SCOPE_SLOT, ObjectOrNullValue(prev));
     }
 
     void resetEnclosingScopeFromParser() {
         setReservedSlot(ENCLOSING_SCOPE_SLOT, UndefinedValue());
     }
 };
 
@@ -158,21 +156,16 @@ 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;
@@ -348,17 +341,17 @@ class StaticWithScope : public NestedSta
   public:
     static const Class class_;
 
     static StaticWithScope* create(ExclusiveContext* cx);
 };
 
 template <XDRMode mode>
 bool
-XDRStaticWithScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+XDRStaticWithScope(XDRState<mode>* xdr, Handle<StaticScope*> 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
@@ -366,20 +359,16 @@ class StaticEvalScope : public StaticSco
     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, 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();
     }
 
@@ -495,74 +484,70 @@ 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<JSObject*, allowGC>::RootType obj;
+    typename MaybeRooted<StaticScope*, allowGC>::RootType scope;
     bool onNamedLambda;
 
     static bool IsStaticScope(JSObject* obj) {
         return obj->is<StaticBlockScope>() ||
                obj->is<StaticFunctionScope>() ||
                obj->is<StaticModuleScope>() ||
                obj->is<StaticWithScope>() ||
                obj->is<StaticEvalScope>() ||
                obj->is<StaticNonSyntacticScope>();
     }
 
   public:
-    StaticScopeIter(ExclusiveContext* cx, JSObject* obj)
-      : obj(cx, obj), onNamedLambda(false)
+    StaticScopeIter(ExclusiveContext* cx, StaticScope* scope)
+      : scope(cx, scope), onNamedLambda(false)
     {
         static_assert(allowGC == CanGC,
                       "the context-accepting constructor should only be used "
                       "in CanGC code");
-        MOZ_ASSERT_IF(obj, IsStaticScope(obj));
+        MOZ_ASSERT_IF(scope, IsStaticScope(scope));
     }
 
     StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter<CanGC>& ssi)
-      : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda)
+      : scope(cx, ssi.scope), onNamedLambda(ssi.onNamedLambda)
     {
         JS_STATIC_ASSERT(allowGC == CanGC);
     }
 
-    explicit StaticScopeIter(JSObject* obj)
-      : obj((ExclusiveContext*) nullptr, obj), onNamedLambda(false)
+    explicit StaticScopeIter(StaticScope* scope)
+      : scope((ExclusiveContext*) nullptr, scope), onNamedLambda(false)
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
-        MOZ_ASSERT_IF(obj, IsStaticScope(obj));
+        MOZ_ASSERT_IF(scope, IsStaticScope(scope));
     }
 
     explicit StaticScopeIter(const StaticScopeIter<NoGC>& ssi)
-      : obj((ExclusiveContext*) nullptr, ssi.obj), onNamedLambda(ssi.onNamedLambda)
+      : scope((ExclusiveContext*) nullptr, ssi.scope), onNamedLambda(ssi.onNamedLambda)
     {
         static_assert(allowGC == NoGC,
                       "the constructor not taking a context should only be "
                       "used in NoGC code");
     }
 
-    bool done() const { return !obj; }
+    bool done() const { return !scope; }
     void operator++(int);
 
-    JSObject* staticScope() const { MOZ_ASSERT(!done()); return obj; }
+    StaticScope* staticScope() const { MOZ_ASSERT(!done()); return scope; }
 
     // 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 };
@@ -916,18 +901,18 @@ class DynamicWithObject : public NestedS
     static const Class class_;
 
     enum WithKind {
         SyntacticWith,
         NonSyntacticWith
     };
 
     static DynamicWithObject*
-    create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith,
-           WithKind kind = SyntacticWith);
+    create(JSContext* cx, HandleObject object, HandleObject enclosing,
+           Handle<StaticWithScope*> 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();
@@ -970,17 +955,18 @@ 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, HandleObject enclosingStatic,
+    static ClonedBlockObject* createNonSyntactic(JSContext* cx,
+                                                 Handle<StaticNonSyntacticScope*> 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
@@ -1077,21 +1063,21 @@ class RuntimeLexicalErrorObject : public
 
     unsigned errorNumber() {
         return getReservedSlot(ERROR_SLOT).toInt32();
     }
 };
 
 template<XDRMode mode>
 bool
-XDRStaticBlockScope(XDRState<mode>* xdr, HandleObject enclosingScope,
+XDRStaticBlockScope(XDRState<mode>* xdr, Handle<StaticScope*> enclosingScope,
                     MutableHandle<StaticBlockScope*> objp);
 
-extern JSObject*
-CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope,
+extern NestedStaticScope*
+CloneNestedScopeObject(JSContext* cx, Handle<StaticScope*> 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
@@ -1112,17 +1098,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, JSObject* staticScope
+    ScopeIter(JSContext* cx, JSObject* scope, StaticScope* 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;
@@ -1136,17 +1122,17 @@ 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;
 
-    JSObject* maybeStaticScope() const;
+    StaticScope* 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(); }
 
     bool withinInitialFrame() const { return !!frame_; }
@@ -1168,28 +1154,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_;
-    JSObject* staticScope_;
+    StaticScope* staticScope_;
 
   public:
     explicit MissingScopeKey(const ScopeIter& si)
       : frame_(si.maybeInitialFrame()),
         staticScope_(si.maybeStaticScope())
     { }
 
     AbstractFramePtr frame() const { return frame_; }
-    JSObject* staticScope() const { return staticScope_; }
+    StaticScope* staticScope() const { return staticScope_; }
 
-    void updateStaticScope(JSObject* obj) { staticScope_ = obj; }
+    void updateStaticScope(StaticScope* scope) { staticScope_ = scope; }
     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_;
@@ -1201,28 +1187,28 @@ class MissingScopeKey
 
 // The value in LiveScopeMap, mapped from by live scope objects.
 class LiveScopeVal
 {
     friend class DebugScopes;
     friend class MissingScopeKey;
 
     AbstractFramePtr frame_;
-    RelocatablePtrObject staticScope_;
+    RelocatablePtr<StaticScope*> staticScope_;
 
     static void staticAsserts();
 
   public:
     explicit LiveScopeVal(const ScopeIter& si)
       : frame_(si.initialFrame()),
         staticScope_(si.maybeStaticScope())
     { }
 
     AbstractFramePtr frame() const { return frame_; }
-    JSObject* staticScope() const { return staticScope_; }
+    StaticScope* staticScope() const { return staticScope_; }
 
     void updateFrame(AbstractFramePtr frame) { frame_ = frame; }
 
     bool needsSweep();
 };
 
 
 /*****************************************************************************/
@@ -1487,16 +1473,23 @@ 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;
 }
@@ -1603,18 +1596,18 @@ js::CallObject::callee() const
     return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
 }
 
 extern bool
 CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
                                 HandleObject dynamicTerminatingScope,
                                 MutableHandleObject dynamicScopeObj);
 
-bool HasNonSyntacticStaticScopeChain(JSObject* staticScope);
-uint32_t StaticScopeChainLength(JSObject* staticScope);
+bool HasNonSyntacticStaticScopeChain(StaticScope* staticScope);
+uint32_t StaticScopeChainLength(StaticScope* staticScope);
 
 ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
 
 bool GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                               MutableHandleValue res);
 
 bool CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
                           HandlePropertyName name);
@@ -1626,16 +1619,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(JSObject* staticScope);
+void DumpStaticScopeChain(StaticScope* 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
@@ -2220,19 +2220,20 @@ 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());
-        RootedObject staticGlobalLexical(cx, &globalLexical->staticBlock());
+        Rooted<StaticScope*> 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,17 +100,17 @@ InterpreterFrame::createRestParameter(JS
                                        ObjectGroup::NewArrayKind::UnknownIndex);
 }
 
 static inline void
 AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* scope)
 {
 #ifdef DEBUG
     RootedObject originalScope(cx, scope);
-    RootedObject enclosingScope(cx, script->enclosingStaticScope());
+    Rooted<StaticScope*> 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));
--- 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;
 
-    RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
+    Rooted<StaticScope*> 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;
 
-    RootedObject staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
+    Rooted<StaticScope*> staticLexical(cx(), &cx()->global()->lexicalScope().staticBlock());
     if (!XDRScript(this, staticLexical, nullptr, nullptr, scriptp))
         return false;
 
     return true;
 }
 
 template<XDRMode mode>
 bool