Bug 713311 - give the remaining scope objects typed interfaces (r=waldo)
authorLuke Wagner <luke@mozilla.com>
Mon, 02 Jan 2012 15:02:05 -0800
changeset 84889 f0d76403ae9caa10901b1f53269e3c252d614cd7
parent 84888 5c3e0e48bb8c8cbbf1fb8d1d70f40b29a8b08f2c
child 84890 6ff43da616bed93b5264b0cdf62aca4349f13006
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs713311
milestone12.0a1
Bug 713311 - give the remaining scope objects typed interfaces (r=waldo)
js/src/Makefile.in
js/src/builtin/RegExp.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/jsapi-tests/testArgumentsObject.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsbool.cpp
js/src/jsclone.cpp
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfuninlines.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jspropertycache.cpp
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jsweakmap.cpp
js/src/jswrapper.cpp
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastBuiltins.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.h
js/src/vm/BooleanObject-inl.h
js/src/vm/BooleanObject.h
js/src/vm/CallObject-inl.h
js/src/vm/CallObject.cpp
js/src/vm/CallObject.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/NumberObject-inl.h
js/src/vm/NumberObject.h
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/ScopeObject-inl.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/vm/StringObject-inl.h
js/src/vm/StringObject.h
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -152,17 +152,17 @@ CPPSRCS		= \
 		jsutil.cpp \
 		jswatchpoint.cpp \
 		jsweakmap.cpp \
 		jswrapper.cpp \
 		jsxdrapi.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
-		CallObject.cpp \
+		ScopeObject.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
 		Stack.cpp \
 		String.cpp \
 		BytecodeCompiler.cpp \
 		BytecodeEmitter.cpp \
 		FoldConstants.cpp \
 		ParseMaps.cpp \
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -239,17 +239,17 @@ CompileRegExpObject(JSContext *cx, RegEx
          * Note: the regexp static flags are not taken into consideration here.
          */
         JSObject &sourceObj = sourceValue.toObject();
         if (argc >= 2 && !argv[1].isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
             return false;
         }
 
-        RegExpObject *reobj = builder.build(sourceObj.asRegExp());
+        RegExpObject *reobj = builder.build(&sourceObj.asRegExp());
         if (!reobj)
             return false;
         *rval = ObjectValue(*reobj);
         return true;
     }
 
     JSLinearString *sourceStr;
     if (sourceValue.isUndefined()) {
@@ -295,17 +295,17 @@ regexp_compile(JSContext *cx, uintN argc
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
-    RegExpObjectBuilder builder(cx, obj->asRegExp());
+    RegExpObjectBuilder builder(cx, &obj->asRegExp());
     return CompileRegExpObject(cx, builder, args.length(), args.array(), &args.rval());
 }
 
 static JSBool
 regexp_construct(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = JS_ARGV(cx, vp);
 
@@ -334,17 +334,17 @@ regexp_toString(JSContext *cx, uintN arg
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, regexp_toString, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
-    JSString *str = obj->asRegExp()->toString(cx);
+    JSString *str = obj->asRegExp().toString(cx);
     if (!str)
         return false;
 
     *vp = StringValue(str);
     return true;
 }
 
 static JSFunctionSpec regexp_methods[] = {
@@ -448,24 +448,24 @@ static JSPropertySpec regexp_static_prop
     {0,0,0,0,0}
 };
 
 JSObject *
 js_InitRegExpClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
     if (!proto)
         return NULL;
     proto->setPrivate(NULL);
 
-    RegExpObject *reproto = proto->asRegExp();
+    RegExpObject *reproto = &proto->asRegExp();
     RegExpObjectBuilder builder(cx, reproto);
     if (!builder.build(cx->runtime->emptyString, RegExpFlag(0)))
         return NULL;
 
     if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
         return NULL;
 
     JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass,
@@ -509,17 +509,17 @@ ExecuteRegExp(JSContext *cx, Native nati
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
-    RegExpObject *reobj = obj->asRegExp();
+    RegExpObject *reobj = &obj->asRegExp();
 
     RegExpMatcher matcher(cx);
     if (reobj->startsWithAtomizedGreedyStar()) {
         if (!matcher.resetWithTestOptimized(reobj))
             return false;
     } else {
         if (!matcher.reset(reobj))
             return false;
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -187,19 +187,19 @@ frontend::CompileScript(JSContext *cx, J
     BytecodeEmitter bce(&parser, tokenStream.getLineno());
     if (!bce.init(cx, TreeContext::USED_AS_TREE_CONTEXT))
         return NULL;
 
     Probes::compileScriptBegin(cx, filename, lineno);
     MUST_FLOW_THROUGH("out");
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
-    JSObject *globalObj = scopeChain && scopeChain == scopeChain->getGlobal()
-                        ? scopeChain->getGlobal()
-                        : NULL;
+    JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global()
+                          ? &scopeChain->global()
+                          : NULL;
 
     JS_ASSERT_IF(globalObj, globalObj->isNative());
     JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass()));
 
     /* Null script early in case of error, to reduce our code footprint. */
     script = NULL;
 
     GlobalScope globalScope(cx, globalObj, &bce);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -185,22 +185,22 @@ EmitCheck(JSContext *cx, BytecodeEmitter
         JS_ASSERT(newlength >= size_t(offset + delta));
         bce->current->base = newbase;
         bce->current->limit = newbase + newlength;
         bce->current->next = newbase + offset;
     }
     return offset;
 }
 
-static JSObject *
+static StaticBlockObject &
 CurrentBlock(BytecodeEmitter *bce)
 {
     JS_ASSERT(bce->topStmt->type == STMT_BLOCK || bce->topStmt->type == STMT_SWITCH);
     JS_ASSERT(bce->topStmt->blockObj->isStaticBlock());
-    return bce->topStmt->blockObj;
+    return *bce->topStmt->blockObj;
 }
 
 static void
 UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
 {
     jsbytecode *pc = bce->code(target);
     JSOp op = (JSOp) *pc;
     const JSCodeSpec *cs = &js_CodeSpec[op];
@@ -220,21 +220,21 @@ UpdateDepth(JSContext *cx, BytecodeEmitt
     /*
      * Specially handle any case that would call js_GetIndexFromBytecode since
      * it requires a well-formed script. This allows us to safely pass NULL as
      * the 'script' parameter.
      */
     intN nuses, ndefs;
     if (op == JSOP_ENTERBLOCK) {
         nuses = 0;
-        ndefs = OBJ_BLOCK_COUNT(cx, CurrentBlock(bce));
+        ndefs = CurrentBlock(bce).slotCount();
     } else if (op == JSOP_ENTERLET0) {
-        nuses = ndefs = OBJ_BLOCK_COUNT(cx, CurrentBlock(bce));
+        nuses = ndefs = CurrentBlock(bce).slotCount();
     } else if (op == JSOP_ENTERLET1) {
-        nuses = ndefs = OBJ_BLOCK_COUNT(cx, CurrentBlock(bce)) + 1;
+        nuses = ndefs = CurrentBlock(bce).slotCount() + 1;
     } else {
         nuses = StackUses(NULL, pc);
         ndefs = StackDefs(NULL, pc);
     }
 
     bce->stackDepth -= nuses;
     JS_ASSERT(bce->stackDepth >= 0);
     bce->stackDepth += ndefs;
@@ -1351,25 +1351,25 @@ frontend::PushStatement(TreeContext *tc,
         stmt->downScope = tc->topScopeStmt;
         tc->topScopeStmt = stmt;
     } else {
         stmt->downScope = NULL;
     }
 }
 
 void
-frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top)
+frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top)
 {
     PushStatement(tc, stmt, STMT_BLOCK, top);
     stmt->flags |= SIF_SCOPE;
-    blockObj->setStaticBlockScopeChain(tc->blockChain);
+    blockObj.setEnclosingBlock(tc->blockChain);
     stmt->downScope = tc->topScopeStmt;
     tc->topScopeStmt = stmt;
-    tc->blockChain = blockObj;
-    stmt->blockObj = blockObj;
+    tc->blockChain = &blockObj;
+    stmt->blockObj = &blockObj;
 }
 
 /*
  * Emit a backpatch op with offset pointing to the previous jump of this type,
  * so that we can walk back up the chain fixing up the op and jump offset.
  */
 static ptrdiff_t
 EmitBackPatchOp(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t *lastp)
@@ -1555,17 +1555,17 @@ EmitNonLocalJumpFixup(JSContext *cx, Byt
             npops += 2;
             break;
 
           default:;
         }
 
         if (stmt->flags & SIF_SCOPE) {
             FLUSH_POPS();
-            uintN blockObjCount = OBJ_BLOCK_COUNT(cx, stmt->blockObj);
+            uintN blockObjCount = stmt->blockObj->slotCount();
             if (stmt->flags & SIF_FOR_BLOCK) {
                 /*
                  * For a for-let-in statement, pushing/popping the block is
                  * interleaved with JSOP_(END)ITER. Just handle both together
                  * here and skip over the enclosing STMT_FOR_IN_LOOP.
                  */
                 JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP);
                 stmt = stmt->down;
@@ -1644,17 +1644,17 @@ BackPatch(JSContext *cx, BytecodeEmitter
 void
 frontend::PopStatementTC(TreeContext *tc)
 {
     StmtInfo *stmt = tc->topStmt;
     tc->topStmt = stmt->down;
     if (STMT_LINKS_SCOPE(stmt)) {
         tc->topScopeStmt = stmt->downScope;
         if (stmt->flags & SIF_SCOPE)
-            tc->blockChain = stmt->blockObj->staticBlockScopeChain();
+            tc->blockChain = stmt->blockObj->enclosingBlock();
     }
 }
 
 JSBool
 frontend::PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
 {
     StmtInfo *stmt = bce->topStmt;
     if (!STMT_IS_TRYING(stmt) &&
@@ -1686,27 +1686,23 @@ frontend::LexicalLookup(TreeContext *tc,
     for (; stmt; stmt = stmt->downScope) {
         if (stmt->type == STMT_WITH)
             break;
 
         /* Skip "maybe scope" statements that don't contain let bindings. */
         if (!(stmt->flags & SIF_SCOPE))
             continue;
 
-        JSObject *obj = stmt->blockObj;
-        JS_ASSERT(obj->isStaticBlock());
-
-        const Shape *shape = obj->nativeLookup(tc->parser->context, ATOM_TO_JSID(atom));
+        StaticBlockObject &blockObj = *stmt->blockObj;
+        const Shape *shape = blockObj.nativeLookup(tc->parser->context, ATOM_TO_JSID(atom));
         if (shape) {
             JS_ASSERT(shape->hasShortID());
 
-            if (slotp) {
-                JS_ASSERT(obj->getSlot(JSSLOT_BLOCK_DEPTH).isInt32());
-                *slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid();
-            }
+            if (slotp)
+                *slotp = blockObj.stackDepth() + shape->shortid();
             return stmt;
         }
     }
 
     if (slotp)
         *slotp = -1;
     return stmt;
 }
@@ -1958,70 +1954,61 @@ AdjustBlockSlot(JSContext *cx, BytecodeE
 
 static bool
 EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
         return false;
 
-    JSObject *blockObj = pn->pn_objbox->object;
-    JS_ASSERT(blockObj->isStaticBlock());
-    JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined());
+    StaticBlockObject &blockObj = pn->pn_objbox->object->asStaticBlock();
 
     int depth = bce->stackDepth -
-                (OBJ_BLOCK_COUNT(cx, blockObj) + ((op == JSOP_ENTERLET1) ? 1 : 0));
+                (blockObj.slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
     JS_ASSERT(depth >= 0);
-    OBJ_SET_BLOCK_DEPTH(cx, blockObj, depth);
+
+    blockObj.setStackDepth(depth);
+
     int depthPlusFixed = AdjustBlockSlot(cx, bce, depth);
     if (depthPlusFixed < 0)
         return false;
 
-    uintN base = JSSLOT_FREE(&BlockClass);
-    for (uintN slot = base, limit = base + OBJ_BLOCK_COUNT(cx, blockObj); slot < limit; slot++) {
-        const Value &v = blockObj->getSlot(slot);
+    for (unsigned i = 0; i < blockObj.slotCount(); i++) {
+        Definition *dn = blockObj.maybeDefinitionParseNode(i);
+        blockObj.poisonDefinitionParseNode(i);
 
         /* Beware the empty destructuring dummy. */
-        if (v.isUndefined()) {
-            JS_ASSERT(slot + 1 <= limit);
+        if (!dn) {
+            JS_ASSERT(i + 1 <= blockObj.slotCount());
             continue;
         }
 
-        Definition *dn = (Definition *) v.toPrivate();
         JS_ASSERT(dn->isDefn());
         JS_ASSERT(uintN(dn->frameSlot() + depthPlusFixed) < JS_BIT(16));
         dn->pn_cookie.set(dn->pn_cookie.level(), uint16_t(dn->frameSlot() + depthPlusFixed));
 #ifdef DEBUG
         for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             JS_ASSERT(pnu->pn_lexdef == dn);
             JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             JS_ASSERT(pnu->pn_cookie.isFree());
         }
 #endif
-
-        /*
-         * If this variable is closed over, and |eval| is not present, then
-         * then set a bit in dslots so the Method JIT can deoptimize this
-         * slot.
-         */
-        bool isClosed = bce->shouldNoteClosedName(dn);
-        blockObj->setSlot(slot, BooleanValue(isClosed));
     }
 
     /*
      * If clones of this block will have any extensible parents, then the
      * clones must get unique shapes; see the comments for
      * js::Bindings::extensibleParents.
      */
     if ((bce->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
         bce->bindings.extensibleParents()) {
-        Shape *newShape = Shape::setExtensibleParents(cx, blockObj->lastProperty());
+        Shape *newShape = Shape::setExtensibleParents(cx, blockObj.lastProperty());
         if (!newShape)
             return false;
-        blockObj->setLastPropertyInfallible(newShape);
+        blockObj.setLastPropertyInfallible(newShape);
     }
 
     return true;
 }
 
 /*
  * Try to convert a *NAME op to a *GNAME op, which optimizes access to
  * undeclared globals. Return true if a conversion was made.
@@ -3222,33 +3209,33 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
     defaultOffset = -1;
 
     pn2 = pn->pn_right;
 #if JS_HAS_BLOCK_SCOPE
     /*
      * If there are hoisted let declarations, their stack slots go under the
      * discriminant's value so push their slots now and enter the block later.
      */
-    uint32_t blockCount = 0;
+    uint32_t blockObjCount = 0;
     if (pn2->isKind(PNK_LEXICALSCOPE)) {
-        blockCount = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object);
-        for (uint32_t i = 0; i < blockCount; ++i) {
+        blockObjCount = pn2->pn_objbox->object->asStaticBlock().slotCount();
+        for (uint32_t i = 0; i < blockObjCount; ++i) {
             if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
                 return JS_FALSE;
         }
     }
 #endif
 
     /* Push the discriminant. */
     if (!EmitTree(cx, bce, pn->pn_left))
         return JS_FALSE;
 
 #if JS_HAS_BLOCK_SCOPE
     if (pn2->isKind(PNK_LEXICALSCOPE)) {
-        PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object, -1);
+        PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object->asStaticBlock(), -1);
         stmtInfo.type = STMT_SWITCH;
         if (!EmitEnterBlock(cx, bce, pn2, JSOP_ENTERLET1))
             return JS_FALSE;
     }
 #endif
 
     /* Switch bytecodes run from here till end of final case. */
     top = bce->offset();
@@ -3705,17 +3692,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
 out:
     if (table)
         cx->free_(table);
     if (ok) {
         ok = PopStatementBCE(cx, bce);
 
 #if JS_HAS_BLOCK_SCOPE
         if (ok && pn->pn_right->isKind(PNK_LEXICALSCOPE))
-            EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockCount);
+            EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
 #endif
     }
     return ok;
 
 bad:
     ok = JS_FALSE;
     goto out;
 }
@@ -5119,17 +5106,17 @@ EmitTry(JSContext *cx, BytecodeEmitter *
             CATCHNOTE(stmtInfo) = catchNote;
 
             /*
              * Emit the lexical scope and catch body.  Save the catch's
              * block object population via count, for use when targeting
              * guardJump at the next catch (the guard mismatch case).
              */
             JS_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
-            count = OBJ_BLOCK_COUNT(cx, pn3->pn_objbox->object);
+            count = pn3->pn_objbox->object->asStaticBlock().slotCount();
             if (!EmitTree(cx, bce, pn3))
                 return false;
 
             /* gosub <finally>, if required */
             if (pn->pn_kid3) {
                 if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &GOSUBS(stmtInfo)) < 0)
                     return false;
                 JS_ASSERT(bce->stackDepth == depth);
@@ -5358,29 +5345,28 @@ EmitIf(JSContext *cx, BytecodeEmitter *b
 static bool
 EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
 {
     JS_ASSERT(pnLet->isArity(PN_BINARY));
     ParseNode *varList = pnLet->pn_left;
     JS_ASSERT(varList->isArity(PN_LIST));
     ParseNode *letBody = pnLet->pn_right;
     JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
-    JSObject *blockObj = letBody->pn_objbox->object;
-    JS_ASSERT(blockObj->isStaticBlock());
+    StaticBlockObject &blockObj = letBody->pn_objbox->object->asStaticBlock();
 
     ptrdiff_t letHeadOffset = bce->offset();
     intN letHeadDepth = bce->stackDepth;
 
     LetNotes letNotes(cx);
     if (!EmitVariables(cx, bce, varList, PushInitialValues, &letNotes))
         return false;
 
     /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */
     uint32_t alreadyPushed = uintN(bce->stackDepth - letHeadDepth);
-    uint32_t blockObjCount = OBJ_BLOCK_COUNT(cx, blockObj);
+    uint32_t blockObjCount = blockObj.slotCount();
     for (uint32_t i = alreadyPushed; i < blockObjCount; ++i) {
         /* Tell the decompiler not to print the decl in the let head. */
         if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
             return false;
         if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
             return false;
     }
 
@@ -5403,17 +5389,17 @@ EmitLet(JSContext *cx, BytecodeEmitter *
 
     JSOp leaveOp = letBody->getOp();
     if (leaveOp == JSOP_LEAVEBLOCKEXPR) {
         if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - letHeadOffset) < 0)
             return false;
     }
 
     JS_ASSERT(leaveOp == JSOP_LEAVEBLOCK || leaveOp == JSOP_LEAVEBLOCKEXPR);
-    EMIT_UINT16_IMM_OP(leaveOp, OBJ_BLOCK_COUNT(cx, blockObj));
+    EMIT_UINT16_IMM_OP(leaveOp, blockObj.slotCount());
 
     ptrdiff_t bodyEnd = bce->offset();
     JS_ASSERT(bodyEnd > bodyBegin);
 
     if (!PopStatementBCE(cx, bce))
         return false;
 
     ptrdiff_t o = PackLetData((bodyEnd - bodyBegin) -
@@ -5501,18 +5487,17 @@ EmitXMLProcessingInstruction(JSContext *
 static bool
 EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK);
 
     StmtInfo stmtInfo;
     ObjectBox *objbox = pn->pn_objbox;
-    JSObject *blockObj = objbox->object;
-    JS_ASSERT(blockObj->isStaticBlock());
+    StaticBlockObject &blockObj = objbox->object->asStaticBlock();
     PushBlockScope(bce, &stmtInfo, blockObj, bce->offset());
 
     /*
      * For compound statements (i.e. { stmt-list }), the decompiler does not
      * emit curlies by default. However, if this stmt-list contains a let
      * declaration, this is semantically invalid so we need to add a srcnote to
      * enterblock to tell the decompiler to add curlies. This condition
      * shouldn't be so complicated; try to find a simpler condition.
@@ -5541,17 +5526,17 @@ EmitLexicalScope(JSContext *cx, Bytecode
     if (!EmitTree(cx, bce, pn->pn_expr))
         return false;
 
     if (noteIndex >= 0) {
         if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, bce->offset() - bodyBegin))
             return false;
     }
 
-    EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, OBJ_BLOCK_COUNT(cx, blockObj));
+    EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount());
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
 EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     StmtInfo stmtInfo;
@@ -5613,18 +5598,18 @@ EmitForIn(JSContext *cx, BytecodeEmitter
 
     ParseNode *forHead = pn->pn_left;
     ParseNode *forBody = pn->pn_right;
 
     ParseNode *pn1 = forHead->pn_kid1;
     bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
     JS_ASSERT_IF(letDecl, pn1->isLet());
 
-    JSObject *blockObj = letDecl ? pn1->pn_objbox->object : NULL;
-    uint32_t blockObjCount = blockObj ? OBJ_BLOCK_COUNT(cx, blockObj) : 0;
+    StaticBlockObject *blockObj = letDecl ? &pn1->pn_objbox->object->asStaticBlock() : NULL;
+    uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
 
     if (letDecl) {
         /*
          * The let's slot(s) will be under the iterator, but the block must not
          * be entered (i.e. fp->blockChain set) until after evaluating the rhs.
          * Thus, push to reserve space and enterblock after. The same argument
          * applies when leaving the loop. Thus, a for-let-in loop looks like:
          *
@@ -5671,17 +5656,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter
      */
     JS_ASSERT(pn->isOp(JSOP_ITER));
     if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
         return false;
 
     /* Enter the block before the loop body, after evaluating the obj. */
     StmtInfo letStmt;
     if (letDecl) {
-        PushBlockScope(bce, &letStmt, blockObj, bce->offset());
+        PushBlockScope(bce, &letStmt, *blockObj, bce->offset());
         letStmt.flags |= SIF_FOR_BLOCK;
         if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET1))
             return false;
     }
 
     /* Annotate so the decompiler can find the loop-closing jump. */
     intN noteIndex = NewSrcNote(cx, bce, SRC_FOR_IN);
     if (noteIndex < 0)
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -133,17 +133,17 @@ struct StmtInfo {
     uint16_t        type;           /* statement type */
     uint16_t        flags;          /* flags, see below */
     uint32_t        blockid;        /* for simplified dominance computation */
     ptrdiff_t       update;         /* loop update offset (top if none) */
     ptrdiff_t       breaks;         /* offset of last break in loop */
     ptrdiff_t       continues;      /* offset of last continue in loop */
     union {
         JSAtom      *label;         /* name of LABEL */
-        JSObject    *blockObj;      /* block scope object */
+        StaticBlockObject *blockObj;/* block scope object */
     };
     StmtInfo        *down;          /* info for enclosing statement */
     StmtInfo        *downScope;     /* next enclosing lexical scope */
 };
 
 #define SIF_SCOPE        0x0001     /* statement has its own lexical scope */
 #define SIF_BODY_BLOCK   0x0002     /* STMT_BLOCK type is a function body */
 #define SIF_FOR_BLOCK    0x0004     /* for (let ...) induced block scope */
@@ -294,17 +294,17 @@ struct TreeContext {                /* t
     uint32_t        parenDepth;     /* nesting depth of parens that might turn out
                                        to be generator expressions */
     uint32_t        yieldCount;     /* number of |yield| tokens encountered at
                                        non-zero depth in current paren tree */
     uint32_t        argumentsCount; /* number of |arguments| references encountered
                                        at non-zero depth in current paren tree */
     StmtInfo        *topStmt;       /* top of statement info stack */
     StmtInfo        *topScopeStmt;  /* top lexical scope statement */
-    JSObject        *blockChain;    /* compile time block scope chain (NB: one
+    StaticBlockObject *blockChain;  /* compile block scope chain (NB: one
                                        deeper than the topScopeStmt/downScope
                                        chain when in head of let block/expr) */
     ParseNode       *blockNode;     /* parse node for a block with let declarations
                                        (block with its own lexical scope)  */
     AtomDecls       decls;          /* function, const, and var declarations */
     Parser          *parser;        /* ptr to common parsing and lexing data */
     ParseNode       *yieldNode;     /* parse node for a yield expression that might
                                        be an error if we turn out to be inside a
@@ -829,17 +829,17 @@ void
 PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top);
 
 /*
  * Push a block scope statement and link blockObj into tc->blockChain. To pop
  * this statement info record, use PopStatementTC as usual, or if appropriate
  * (if generating code), PopStatementBCE.
  */
 void
-PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top);
+PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top);
 
 /*
  * Pop tc->topStmt. If the top StmtInfo struct is not stack-allocated, it
  * is up to the caller to free it.
  */
 void
 PopStatementTC(TreeContext *tc);
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -174,18 +174,16 @@ Parser::setPrincipals(JSPrincipals *prin
     originPrincipals = originPrin;
     if (originPrincipals)
         JSPRINCIPALS_HOLD(context, originPrincipals);
 }
 
 ObjectBox *
 Parser::newObjectBox(JSObject *obj)
 {
-    JS_ASSERT(obj);
-
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>();
@@ -848,28 +846,28 @@ struct BindData {
 
     ParseNode       *pn;        /* name node for definition processing and
                                    error source coordinates */
     JSOp            op;         /* prolog bytecode or nop */
     Binder          binder;     /* binder, discriminates u */
     union {
         struct {
             VarContext varContext;
-            JSObject *blockObj;
+            StaticBlockObject *blockObj;
             uintN   overflow;
         } let;
     };
     bool fresh;
 
-    void initLet(VarContext varContext, JSObject *blockObj, uintN overflow) {
+    void initLet(VarContext varContext, StaticBlockObject &blockObj, uintN overflow) {
         this->pn = NULL;
         this->op = JSOP_NOP;
         this->binder = BindLet;
         this->let.varContext = varContext;
-        this->let.blockObj = blockObj;
+        this->let.blockObj = &blockObj;
         this->let.overflow = overflow;
     }
 
     void initVarOrConst(JSOp op) {
         this->op = op;
         this->binder = BindVarOrConst;
     }
 };
@@ -1925,18 +1923,18 @@ ReportRedeclaration(JSContext *cx, TreeC
  */
 static JSBool
 BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc)
 {
     ParseNode *pn = data->pn;
     if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn))
         return false;
 
-    JSObject *blockObj = data->let.blockObj;
-    uintN blockCount = OBJ_BLOCK_COUNT(cx, blockObj);
+    StaticBlockObject &blockObj = *data->let.blockObj;
+    uintN blockCount = blockObj.slotCount();
     if (blockCount == JS_BIT(16)) {
         ReportCompileErrorNumber(cx, TS(tc->parser), pn,
                                  JSREPORT_ERROR, data->let.overflow);
         return false;
     }
 
     /*
      * For bindings that are hoisted to the beginning of the block/function,
@@ -1963,62 +1961,59 @@ BindLet(JSContext *cx, BindData *data, J
     pn->pn_dflags |= PND_LET | PND_BOUND;
 
     /*
      * Define the let binding's property before storing pn in the the binding's
      * slot indexed by blockCount off the class-reserved slot base.
      */
     bool redeclared;
     jsid id = ATOM_TO_JSID(atom);
-    const Shape *shape = blockObj->defineBlockVariable(cx, id, blockCount, &redeclared);
+    const Shape *shape = blockObj.addVar(cx, id, blockCount, &redeclared);
     if (!shape) {
         if (redeclared)
             ReportRedeclaration(cx, tc, pn, false, atom);
         return false;
     }
 
-    /*
-     * Store pn temporarily in the shape-mapped slots in the static block
-     * object. This value is clobbered in EmitEnterBlock.
-     */
-    blockObj->setSlot(shape->slot(), PrivateValue(pn));
+    /* Store pn in the static block object. */
+    blockObj.setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
     return true;
 }
 
 template <class Op>
 static inline bool
-ForEachLetDef(TreeContext *tc, JSObject *blockObj, Op op)
+ForEachLetDef(TreeContext *tc, StaticBlockObject &blockObj, Op op)
 {
-    for (Shape::Range r = blockObj->lastProperty()->all(); !r.empty(); r.popFront()) {
+    for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
 
         /* Beware the destructuring dummy slots. */
         if (JSID_IS_INT(shape.propid()))
             continue;
 
         if (!op(tc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
             return false;
     }
     return true;
 }
 
 struct RemoveDecl {
-    bool operator()(TreeContext *tc, JSObject *, const Shape &, JSAtom *atom) {
+    bool operator()(TreeContext *tc, StaticBlockObject &, const Shape &, JSAtom *atom) {
         tc->decls.remove(atom);
         return true;
     }
 };
 
 static void
 PopStatement(TreeContext *tc)
 {
     if (tc->topStmt->flags & SIF_SCOPE) {
-        JSObject *obj = tc->topStmt->blockObj;
-        JS_ASSERT(!obj->inDictionaryMode());
-        ForEachLetDef(tc, obj, RemoveDecl());
+        StaticBlockObject &blockObj = *tc->topStmt->blockObj;
+        JS_ASSERT(!blockObj.inDictionaryMode());
+        ForEachLetDef(tc, blockObj, RemoveDecl());
     }
     PopStatementTC(tc);
 }
 
 static inline bool
 OuterLet(TreeContext *tc, StmtInfo *stmt, JSAtom *atom)
 {
     while (stmt->downScope) {
@@ -2582,18 +2577,18 @@ CheckDestructuring(JSContext *cx, BindDa
     bool ok;
 
     if (left->isKind(PNK_ARRAYCOMP)) {
         ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
                                  JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
-    JSObject *blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL;
-    uint32_t blockCountBefore = blockObj ? OBJ_BLOCK_COUNT(cx, blockObj) : 0;
+    StaticBlockObject *blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL;
+    uint32_t blockCountBefore = blockObj ? blockObj->slotCount() : 0;
 
     if (left->isKind(PNK_RB)) {
         for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) {
             /* Nullary comma is an elision; binary comma is an expression.*/
             if (!pn->isArrayHole()) {
                 if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) {
                     ok = CheckDestructuring(cx, data, pn, tc, false);
                 } else {
@@ -2653,25 +2648,25 @@ CheckDestructuring(JSContext *cx, BindDa
      *   let (x = 1, [[]] = b, y = 3, {a:[]} = c) { ... }
      *
      * four slots are needed.
      *
      * To satisfy both constraints, we push a dummy slot (and add a
      * corresponding dummy property to the block object) for each initializer
      * that doesn't introduce at least one binding.
      */
-    if (toplevel && blockObj && blockCountBefore == OBJ_BLOCK_COUNT(cx, blockObj)) {
+    if (toplevel && blockObj && blockCountBefore == blockObj->slotCount()) {
         if (!DefineNativeProperty(cx, blockObj,
                                   INT_TO_JSID(blockCountBefore),
                                   UndefinedValue(), NULL, NULL,
                                   JSPROP_ENUMERATE | JSPROP_PERMANENT,
                                   Shape::HAS_SHORTID, blockCountBefore)) {
             return false;
         }
-        JS_ASSERT(OBJ_BLOCK_COUNT(cx, blockObj) == blockCountBefore + 1);
+        JS_ASSERT(blockObj->slotCount() == blockCountBefore + 1);
     }
 
     return true;
 }
 
 /*
  * Extend the pn_pos.end source coordinate of each name in a destructuring
  * binding such as
@@ -2806,23 +2801,23 @@ Parser::returnOrYield(bool useAssignExpr
                          JSMSG_ANON_NO_RETURN_VALUE)) {
         return NULL;
     }
 
     return pn;
 }
 
 static ParseNode *
-PushLexicalScope(JSContext *cx, TreeContext *tc, JSObject *obj, StmtInfo *stmt)
+PushLexicalScope(JSContext *cx, TreeContext *tc, StaticBlockObject &obj, StmtInfo *stmt)
 {
     ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, tc);
     if (!pn)
         return NULL;
 
-    ObjectBox *blockbox = tc->parser->newObjectBox(obj);
+    ObjectBox *blockbox = tc->parser->newObjectBox(&obj);
     if (!blockbox)
         return NULL;
 
     PushBlockScope(tc, stmt, obj, -1);
     pn->setOp(JSOP_LEAVEBLOCK);
     pn->pn_objbox = blockbox;
     pn->pn_cookie.makeFree();
     pn->pn_dflags = 0;
@@ -2830,41 +2825,41 @@ PushLexicalScope(JSContext *cx, TreeCont
         return NULL;
     pn->pn_blockid = stmt->blockid;
     return pn;
 }
 
 static ParseNode *
 PushLexicalScope(JSContext *cx, TreeContext *tc, StmtInfo *stmt)
 {
-    JSObject *obj = js_NewBlockObject(cx);
-    if (!obj)
+    StaticBlockObject *blockObj = StaticBlockObject::create(cx);
+    if (!blockObj)
         return NULL;
 
-    return PushLexicalScope(cx, tc, obj, stmt);
+    return PushLexicalScope(cx, tc, *blockObj, stmt);
 }
 
 #if JS_HAS_BLOCK_SCOPE
 
 struct AddDecl
 {
     uint32_t blockid;
 
     AddDecl(uint32_t blockid) : blockid(blockid) {}
 
-    bool operator()(TreeContext *tc, JSObject *blockObj, const Shape &shape, JSAtom *atom)
+    bool operator()(TreeContext *tc, StaticBlockObject &blockObj, const Shape &shape, JSAtom *atom)
     {
-        ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate();
+        ParseNode *def = (ParseNode *) blockObj.getSlot(shape.slot()).toPrivate();
         def->pn_blockid = blockid;
         return Define(def, atom, tc, true);
     }
 };
 
 static ParseNode *
-PushLetScope(JSContext *cx, TreeContext *tc, JSObject *blockObj, StmtInfo *stmt)
+PushLetScope(JSContext *cx, TreeContext *tc, StaticBlockObject &blockObj, StmtInfo *stmt)
 {
     ParseNode *pn = PushLexicalScope(cx, tc, blockObj, stmt);
     if (!pn)
         return NULL;
 
     /* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */
     pn->pn_dflags |= PND_LET;
 
@@ -2884,30 +2879,30 @@ ParseNode *
 Parser::letBlock(LetContext letContext)
 {
     JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
 
     ParseNode *pnlet = BinaryNode::create(PNK_LET, tc);
     if (!pnlet)
         return NULL;
 
-    JSObject *blockObj = js_NewBlockObject(context);
+    StaticBlockObject *blockObj = StaticBlockObject::create(context);
     if (!blockObj)
         return NULL;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
 
     ParseNode *vars = variables(PNK_LET, blockObj, DontHoistVars);
     if (!vars)
         return NULL;
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
 
     StmtInfo stmtInfo;
-    ParseNode *block = PushLetScope(context, tc, blockObj, &stmtInfo);
+    ParseNode *block = PushLetScope(context, tc, *blockObj, &stmtInfo);
     if (!block)
         return NULL;
 
     pnlet->pn_left = vars;
     pnlet->pn_right = block;
 
     ParseNode *ret;
     if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
@@ -2964,17 +2959,17 @@ Parser::letBlock(LetContext letContext)
 static bool
 PushBlocklikeStatement(StmtInfo *stmt, StmtType type, TreeContext *tc)
 {
     PushStatement(tc, stmt, type, -1);
     return GenerateBlockId(tc, stmt->blockid);
 }
 
 static ParseNode *
-NewBindingNode(JSAtom *atom, TreeContext *tc, JSObject *blockObj = NULL,
+NewBindingNode(JSAtom *atom, TreeContext *tc, StaticBlockObject *blockObj = NULL,
                VarContext varContext = HoistVars)
 {
     /*
      * If this name is being injected into an existing block/function, see if
      * it has already been declared or if it resolves an outstanding lexdep.
      * Otherwise, this is a let block/expr that introduces a new scope and thus
      * shadows existing decls and doesn't resolve existing lexdeps. Duplicate
      * names are caught by BindLet.
@@ -3164,17 +3159,17 @@ Parser::forStatement()
 
     /*
      * True if we have 'for (var/let/const ...)', except in the oddball case
      * where 'let' begins a let-expression in 'for (let (...) ...)'.
      */
     bool forDecl = false;
 
     /* Non-null when forDecl is true for a 'for (let ...)' statement. */
-    JSObject *blockObj = NULL;
+    StaticBlockObject *blockObj = NULL;
 
     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
     ParseNode *pn1;
 
     {
         TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt == TOK_SEMI) {
             if (pn->pn_iflags & JSITER_FOREACH) {
@@ -3205,17 +3200,17 @@ Parser::forStatement()
             }
 #if JS_HAS_BLOCK_SCOPE
             else if (tt == TOK_LET) {
                 (void) tokenStream.getToken();
                 if (tokenStream.peekToken() == TOK_LP) {
                     pn1 = letBlock(LetExpresion);
                 } else {
                     forDecl = true;
-                    blockObj = js_NewBlockObject(context);
+                    blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
                         return NULL;
                     pn1 = variables(PNK_LET, blockObj, DontHoistVars);
                 }
             }
 #endif
             else {
                 pn1 = expr();
@@ -3366,17 +3361,17 @@ Parser::forStatement()
 
         if (blockObj) {
             /*
              * Now that the pn3 has been parsed, push the let scope. To hold
              * the blockObj for the emitter, wrap the TOK_LEXICALSCOPE node
              * created by PushLetScope around the for's initializer. This also
              * serves to indicate the let-decl to the emitter.
              */
-            ParseNode *block = PushLetScope(context, tc, blockObj, &letStmt);
+            ParseNode *block = PushLetScope(context, tc, *blockObj, &letStmt);
             if (!block)
                 return NULL;
             letStmt.flags |= SIF_FOR_BLOCK;
             block->pn_expr = pn1;
             pn1 = block;
         }
 
         if (forDecl) {
@@ -3423,17 +3418,17 @@ Parser::forStatement()
         if (!forHead)
             return NULL;
     } else {
         if (blockObj) {
             /*
              * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }'
              * to induce the correct scoping for A.
              */
-            ParseNode *block = PushLetScope(context, tc, blockObj, &letStmt);
+            ParseNode *block = PushLetScope(context, tc, *blockObj, &letStmt);
             if (!block)
                 return NULL;
             letStmt.flags |= SIF_FOR_BLOCK;
 
             ParseNode *let = new_<BinaryNode>(PNK_LET, JSOP_NOP, pos, pn1, block);
             if (!let)
                 return NULL;
 
@@ -3583,17 +3578,17 @@ Parser::tryStatement()
             pnblock->pn_expr = pn2;
             MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
 
             /*
              * Contrary to ECMA Ed. 3, the catch variable is lexically
              * scoped, not a property of a new Object instance.  This is
              * an intentional change that anticipates ECMA Ed. 4.
              */
-            data.initLet(HoistVars, tc->blockChain, JSMSG_TOO_MANY_CATCH_VARS);
+            data.initLet(HoistVars, *tc->blockChain, JSMSG_TOO_MANY_CATCH_VARS);
             JS_ASSERT(data.let.blockObj && data.let.blockObj == pnblock->pn_objbox->object);
 
             tt = tokenStream.getToken();
             ParseNode *pn3;
             switch (tt) {
 #if JS_HAS_DESTRUCTURING
               case TOK_LB:
               case TOK_LC:
@@ -3787,37 +3782,37 @@ Parser::letStatement()
             JS_ASSERT(stmt != tc->topScopeStmt);
             JS_ASSERT(stmt->type == STMT_BLOCK ||
                       stmt->type == STMT_SWITCH ||
                       stmt->type == STMT_TRY ||
                       stmt->type == STMT_FINALLY);
             JS_ASSERT(!stmt->downScope);
 
             /* Convert the block statement into a scope statement. */
-            JSObject *obj = js_NewBlockObject(tc->parser->context);
-            if (!obj)
+            StaticBlockObject *blockObj = StaticBlockObject::create(tc->parser->context);
+            if (!blockObj)
                 return NULL;
 
-            ObjectBox *blockbox = tc->parser->newObjectBox(obj);
+            ObjectBox *blockbox = tc->parser->newObjectBox(blockObj);
             if (!blockbox)
                 return NULL;
 
             /*
              * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
              * list stack, if it isn't already there.  If it is there, but it
              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
              * block.
              */
             stmt->flags |= SIF_SCOPE;
             stmt->downScope = tc->topScopeStmt;
             tc->topScopeStmt = stmt;
 
-            obj->setStaticBlockScopeChain(tc->blockChain);
-            tc->blockChain = obj;
-            stmt->blockObj = obj;
+            blockObj->setEnclosingBlock(tc->blockChain);
+            tc->blockChain = blockObj;
+            stmt->blockObj = blockObj;
 
 #ifdef DEBUG
             ParseNode *tmp = tc->blockNode;
             JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
 #endif
 
             /* Create a new lexical scope node for these statements. */
             ParseNode *pn1 = LexicalScopeNode::create(PNK_LEXICALSCOPE, tc);
@@ -4278,17 +4273,17 @@ Parser::statement()
 }
 
 /*
  * The 'blockObj' parameter is non-null when parsing the 'vars' in a let
  * expression, block statement, non-top-level let declaration in statement
  * context, and the let-initializer of a for-statement.
  */
 ParseNode *
-Parser::variables(ParseNodeKind kind, JSObject *blockObj, VarContext varContext)
+Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext varContext)
 {
     /*
      * The four options here are:
      * - PNK_VAR:   We're parsing var declarations.
      * - PNK_CONST: We're parsing const declarations.
      * - PNK_LET:   We are parsing a let declaration.
      * - PNK_LP:    We are parsing the head of a let block.
      */
@@ -4303,17 +4298,17 @@ Parser::variables(ParseNodeKind kind, JS
 
     /*
      * SpiderMonkey const is really "write once per initialization evaluation"
      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
      * this code will change soon.
      */
     BindData data;
     if (blockObj)
-        data.initLet(varContext, blockObj, JSMSG_TOO_MANY_LOCALS);
+        data.initLet(varContext, *blockObj, JSMSG_TOO_MANY_LOCALS);
     else
         data.initVarOrConst(pn->getOp());
 
     ParseNode *pn2;
     do {
         TokenKind tt = tokenStream.getToken();
 #if JS_HAS_DESTRUCTURING
         if (tt == TOK_LB || tt == TOK_LC) {
@@ -5364,17 +5359,17 @@ Parser::comprehensionTail(ParseNode *kid
     }
 
     pnp = &pn->pn_expr;
 
     CompExprTransplanter transplanter(kid, tc, kind == PNK_SEMI, adjust);
     transplanter.transplant(kid);
 
     JS_ASSERT(tc->blockChain && tc->blockChain == pn->pn_objbox->object);
-    data.initLet(HoistVars, tc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG);
+    data.initLet(HoistVars, *tc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG);
 
     do {
         /*
          * FOR node is binary, left is loop control and right is body.  Use
          * index to count each block-local let-variable on the left-hand side
          * of the IN.
          */
         pn2 = BinaryNode::create(PNK_FOR, tc);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -55,16 +55,18 @@
 #include "frontend/ParseNode.h"
 
 #define NUM_TEMP_FREELISTS      6U      /* 32 to 2048 byte size classes (32 bit) */
 
 typedef struct BindData BindData;
 
 namespace js {
 
+class StaticBlockObject;
+
 enum FunctionSyntaxKind { Expression, Statement };
 enum LetContext { LetExpresion, LetStatement };
 enum VarContext { HoistVars, DontHoistVars };
 
 struct Parser : private AutoGCRooter
 {
     JSContext           *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
     void                *tempFreeList[NUM_TEMP_FREELISTS];
@@ -193,17 +195,18 @@ struct Parser : private AutoGCRooter
     ParseNode *switchStatement();
     ParseNode *forStatement();
     ParseNode *tryStatement();
     ParseNode *withStatement();
 #if JS_HAS_BLOCK_SCOPE
     ParseNode *letStatement();
 #endif
     ParseNode *expressionStatement();
-    ParseNode *variables(ParseNodeKind kind, JSObject *blockObj = NULL, VarContext varContext = HoistVars);
+    ParseNode *variables(ParseNodeKind kind, StaticBlockObject *blockObj = NULL,
+                         VarContext varContext = HoistVars);
     ParseNode *expr();
     ParseNode *assignExpr();
     ParseNode *condExpr1();
     ParseNode *orExpr1();
     ParseNode *andExpr1i();
     ParseNode *andExpr1n();
     ParseNode *bitOrExpr1i();
     ParseNode *bitOrExpr1n();
--- a/js/src/jsapi-tests/testArgumentsObject.cpp
+++ b/js/src/jsapi-tests/testArgumentsObject.cpp
@@ -81,24 +81,24 @@ BEGIN_TEST(testArgumentsObject)
 
 template<size_t ArgCount> bool
 ExhaustiveTest(const char funcode[])
 {
     jsval v;
     EVAL(funcode, &v);
 
     EVAL(CALL_CODES[ArgCount], &v);
-    ArgumentsObject *argsobj = JSVAL_TO_OBJECT(v)->asArguments();
+    ArgumentsObject &argsobj = JSVAL_TO_OBJECT(v)->asArguments();
 
     Value elems[MAX_ELEMS];
 
     for (size_t i = 0; i <= ArgCount; i++) {
         for (size_t j = 0; j <= ArgCount - i; j++) {
             ClearElements(elems);
-            CHECK(argsobj->getElements(i, j, elems));
+            CHECK(argsobj.getElements(i, j, elems));
             for (size_t k = 0; k < j; k++)
                 CHECK_SAME(elems[k], INT_TO_JSVAL(i + k));
             for (size_t k = j; k < MAX_ELEMS - 1; k++)
                 CHECK_SAME(elems[k], JSVAL_NULL);
             CHECK_SAME(elems[MAX_ELEMS - 1], INT_TO_JSVAL(42));
         }
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1398,17 +1398,17 @@ JS_EnterCrossCompartmentCallScript(JSCon
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallStackFrame(JSContext *cx, JSStackFrame *target)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
-    return JS_EnterCrossCompartmentCall(cx, Valueify(target)->scopeChain().getGlobal());
+    return JS_EnterCrossCompartmentCall(cx, &Valueify(target)->scopeChain().global());
 }
 
 JS_PUBLIC_API(void)
 JS_LeaveCrossCompartmentCall(JSCrossCompartmentCall *call)
 {
     AutoCompartment *realcall = reinterpret_cast<AutoCompartment *>(call);
     AssertNoGC(realcall->context);
     CHECK_REQUEST(realcall->context);
@@ -1738,17 +1738,17 @@ JS_InitStandardClasses(JSContext *cx, JS
      * it before assertSameCompartment. (The API contract is that *after* this,
      * cx and obj must be in the same compartment.)
      */
     if (!cx->globalObject)
         JS_SetGlobalObject(cx, obj);
 
     assertSameCompartment(cx, obj);
 
-    return obj->asGlobal()->initStandardClasses(cx);
+    return obj->global().initStandardClasses(cx);
 }
 
 #define CLASP(name)                 (&name##Class)
 #define TYPED_ARRAY_CLASP(type)     (&TypedArray::fastClasses[TypedArray::type])
 #define EAGER_ATOM(name)            ATOM_OFFSET(name), NULL
 #define EAGER_CLASS_ATOM(name)      CLASS_ATOM_OFFSET(name), NULL
 #define EAGER_ATOM_AND_CLASP(name)  EAGER_CLASS_ATOM(name), CLASP(name)
 #define LAZY_ATOM(name)             ATOM_OFFSET(lazy.name), js_##name##_str
@@ -2163,17 +2163,17 @@ JS_GetClassObject(JSContext *cx, JSObjec
     return js_GetClassObject(cx, obj, key, objp);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     assertSameCompartment(cx, obj);
-    return obj->getGlobal();
+    return &obj->global();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalForScopeChain(JSContext *cx)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     return GetGlobalForScopeChain(cx);
@@ -3218,27 +3218,27 @@ JS_SetPrototype(JSContext *cx, JSObject 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
     return SetProto(cx, obj, proto, JS_FALSE);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetParent(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(!obj->isInternalScope());
+    JS_ASSERT(!obj->isScope());
     assertSameCompartment(cx, obj);
     return obj->getParent();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
-    JS_ASSERT(!obj->isInternalScope());
+    JS_ASSERT(!obj->isScope());
     JS_ASSERT(parent || !obj->getParent());
     assertSameCompartment(cx, obj, parent);
     return obj->setParent(cx, parent);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetConstructor(JSContext *cx, JSObject *proto)
 {
@@ -3722,24 +3722,24 @@ DefineProperty(JSContext *cx, JSObject *
     }
 
     if (attrs & JSPROP_NATIVE_ACCESSORS) {
         RootId idRoot(cx, &id);
 
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         attrs &= ~JSPROP_NATIVE_ACCESSORS;
         if (getter) {
-            JSObject *getobj = JS_NewFunction(cx, (Native) getter, 0, 0, obj->getGlobal(), NULL);
+            JSObject *getobj = JS_NewFunction(cx, (Native) getter, 0, 0, &obj->global(), NULL);
             if (!getobj)
                 return false;
             getter = JS_DATA_TO_FUNC_PTR(PropertyOp, getobj);
             attrs |= JSPROP_GETTER;
         }
         if (setter) {
-            JSObject *setobj = JS_NewFunction(cx, (Native) setter, 1, 0, obj->getGlobal(), NULL);
+            JSObject *setobj = JS_NewFunction(cx, (Native) setter, 1, 0, &obj->global(), NULL);
             if (!setobj)
                 return false;
             setter = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setobj);
             attrs |= JSPROP_SETTER;
         }
     }
 
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid);
@@ -4209,17 +4209,17 @@ JS_ClearScope(JSContext *cx, JSObject *o
     if (clearOp)
         clearOp(cx, obj);
 
     if (obj->isNative())
         js_ClearNative(cx, obj);
 
     /* Clear cached class objects on the global object. */
     if (obj->isGlobal())
-        obj->asGlobal()->clear(cx);
+        obj->asGlobal().clear(cx);
 
     js_InitRandom(cx);
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
@@ -4598,17 +4598,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
         JSObject *obj = parent;
         int skip = uva->vector[i].level();
         while (--skip > 0) {
             if (!obj) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
                 return NULL;
             }
-            obj = obj->scopeChain();
+            obj = obj->enclosingScope();
         }
 
         Value v;
         if (!obj->getGeneric(cx, r.front().propid(), &v))
             return NULL;
         clone->toFunction()->setFlatClosureUpvar(i, v);
     }
 
@@ -6284,60 +6284,60 @@ JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObject(JSContext *cx, JSObject *obj, char *bytes, size_t length, uintN flags)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     jschar *chars = InflateString(cx, bytes, &length);
     if (!chars)
         return NULL;
 
-    RegExpStatics *res = obj->asGlobal()->getRegExpStatics();
+    RegExpStatics *res = obj->asGlobal().getRegExpStatics();
     RegExpObject *reobj = RegExpObject::create(cx, res, chars, length, RegExpFlag(flags), NULL);
     cx->free_(chars);
     return reobj;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewUCRegExpObject(JSContext *cx, JSObject *obj, jschar *chars, size_t length, uintN flags)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
-    RegExpStatics *res = obj->asGlobal()->getRegExpStatics();
+    RegExpStatics *res = obj->asGlobal().getRegExpStatics();
     return RegExpObject::create(cx, res, chars, length, RegExpFlag(flags), NULL);
 }
 
 JS_PUBLIC_API(void)
 JS_SetRegExpInput(JSContext *cx, JSObject *obj, JSString *input, JSBool multiline)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, input);
 
-    obj->asGlobal()->getRegExpStatics()->reset(cx, input, !!multiline);
+    obj->asGlobal().getRegExpStatics()->reset(cx, input, !!multiline);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearRegExpStatics(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     JS_ASSERT(obj);
 
-    obj->asGlobal()->getRegExpStatics()->clear();
+    obj->asGlobal().getRegExpStatics()->clear();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, size_t length,
                  size_t *indexp, JSBool test, jsval *rval)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
-    RegExpStatics *res = obj->asGlobal()->getRegExpStatics();
-    return ExecuteRegExp(cx, res, reobj->asRegExp(), NULL, chars, length,
+    RegExpStatics *res = obj->asGlobal().getRegExpStatics();
+    return ExecuteRegExp(cx, res, &reobj->asRegExp(), NULL, chars, length,
                          indexp, test ? RegExpTest : RegExpExec, rval);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewRegExpObjectNoStatics(JSContext *cx, char *bytes, size_t length, uintN flags)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
@@ -6359,17 +6359,17 @@ JS_NewUCRegExpObjectNoStatics(JSContext 
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t length,
                           size_t *indexp, JSBool test, jsval *rval)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
-    return ExecuteRegExp(cx, NULL, obj->asRegExp(), NULL, chars, length, indexp,
+    return ExecuteRegExp(cx, NULL, &obj->asRegExp(), NULL, chars, length, indexp,
                          test ? RegExpTest : RegExpExec, rval);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ObjectIsRegExp(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     JS_ASSERT(obj);
@@ -6377,26 +6377,26 @@ JS_ObjectIsRegExp(JSContext *cx, JSObjec
 }
 
 JS_PUBLIC_API(uintN)
 JS_GetRegExpFlags(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
-    return obj->asRegExp()->getFlags();
+    return obj->asRegExp().getFlags();
 }
 
 JS_PUBLIC_API(JSString *)
 JS_GetRegExpSource(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
 
-    return obj->asRegExp()->getSource();
+    return obj->asRegExp().getSource();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
 {
     AssertNoGC(cx);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -151,19 +151,19 @@ JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
 {
     if (obj->isArray()) {
         *lengthp = obj->getArrayLength();
         return true;
     }
 
     if (obj->isArguments()) {
-        ArgumentsObject *argsobj = obj->asArguments();
-        if (!argsobj->hasOverriddenLength()) {
-            *lengthp = argsobj->initialLength();
+        ArgumentsObject &argsobj = obj->asArguments();
+        if (!argsobj.hasOverriddenLength()) {
+            *lengthp = argsobj.initialLength();
             return true;
         }
     }
 
     AutoValueRooter tvr(cx);
     if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, tvr.addr()))
         return false;
 
@@ -410,17 +410,17 @@ GetElement(JSContext *cx, JSObject *obj,
 {
     JS_ASSERT(index >= 0);
     if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() &&
         !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) {
         *hole = JS_FALSE;
         return JS_TRUE;
     }
     if (obj->isArguments()) {
-        if (obj->asArguments()->getElement(uint32_t(index), vp)) {
+        if (obj->asArguments().getElement(uint32_t(index), vp)) {
             *hole = JS_FALSE;
             return true;
         }
     }
 
     return DoGetElement(cx, obj, index, hole, vp);
 }
 
@@ -447,19 +447,19 @@ GetElements(JSContext *cx, JSObject *aob
         const Value *srcend = srcbeg + length;
         const Value *src = srcbeg;
         for (Value *dst = vp; src < srcend; ++dst, ++src)
             *dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
         return true;
     }
 
     if (aobj->isArguments()) {
-        ArgumentsObject *argsobj = aobj->asArguments();
-        if (!argsobj->hasOverriddenLength()) {
-            if (argsobj->getElements(0, length, vp))
+        ArgumentsObject &argsobj = aobj->asArguments();
+        if (!argsobj.hasOverriddenLength()) {
+            if (argsobj.getElements(0, length, vp))
                 return true;
         }
     }
 
     return GetElementsSlow(cx, aobj, length, vp);
 }
 
 }
@@ -3581,17 +3581,17 @@ js_Array(JSContext *cx, uintN argc, Valu
 }
 
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
     RootedVar<GlobalObject*> global(cx);
-    global = obj->asGlobal();
+    global = &obj->asGlobal();
 
     RootedVarObject arrayProto(cx);
     arrayProto = global->createBlankPrototype(cx, &SlowArrayClass);
     if (!arrayProto || !AddLengthProperty(cx, arrayProto))
         return NULL;
     arrayProto->setArrayLength(cx, 0);
 
     RootedVarFunction ctor(cx);
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -148,17 +148,17 @@ Boolean(JSContext *cx, uintN argc, Value
     return true;
 }
 
 JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *booleanProto = global->createBlankPrototype(cx, &BooleanClass);
     if (!booleanProto)
         return NULL;
     booleanProto->setPrimitiveThis(BooleanValue(false));
 
     JSFunction *ctor = global->createConstructor(cx, Boolean, &BooleanClass,
                                                  CLASS_ATOM(cx, Boolean), 1);
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -518,19 +518,19 @@ JSStructuredCloneWriter::startWrite(cons
         return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
     } else if (v.isNull()) {
         return out.writePair(SCTAG_NULL, 0);
     } else if (v.isUndefined()) {
         return out.writePair(SCTAG_UNDEFINED, 0);
     } else if (v.isObject()) {
         JSObject *obj = &v.toObject();
         if (obj->isRegExp()) {
-            RegExpObject *reobj = obj->asRegExp();
-            return out.writePair(SCTAG_REGEXP_OBJECT, reobj->getFlags()) &&
-                   writeString(SCTAG_STRING, reobj->getSource());
+            RegExpObject &reobj = obj->asRegExp();
+            return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
+                   writeString(SCTAG_STRING, reobj.getSource());
         } else if (obj->isDate()) {
             jsdouble d = js_DateGetMsecSinceEpoch(context(), obj);
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
         } else if (obj->isObject() || obj->isArray()) {
             return startObject(obj);
         } else if (js_IsTypedArray(obj)) {
             return writeTypedArray(obj);
         } else if (js_IsArrayBuffer(obj)) {
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -71,22 +71,22 @@ struct PreserveRegsGuard
     JSContext *cx;
     FrameRegs &regs_;
 };
 
 static inline GlobalObject *
 GetGlobalForScopeChain(JSContext *cx)
 {
     if (cx->hasfp())
-        return cx->fp()->scopeChain().getGlobal();
+        return &cx->fp()->scopeChain().global();
 
     JSObject *scope = JS_ObjectToInnerObject(cx, cx->globalObject);
     if (!scope)
         return NULL;
-    return scope->asGlobal();
+    return &scope->asGlobal();
 }
 
 inline GSNCache *
 GetGSNCache(JSContext *cx)
 {
     return &JS_THREAD_DATA(cx)->gsnCache;
 }
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -195,17 +195,17 @@ JSCompartment::wrap(JSContext *cx, Value
      * Wrappers should really be parented to the wrapped parent of the wrapped
      * object, but in that case a wrapped global object would have a NULL
      * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
      * we parent all wrappers to the global object in their home compartment.
      * This loses us some transparency, and is generally very cheesy.
      */
     JSObject *global;
     if (cx->hasfp()) {
-        global = cx->fp()->scopeChain().getGlobal();
+        global = &cx->fp()->scopeChain().global();
     } else {
         global = JS_ObjectToInnerObject(cx, cx->globalObject);
         if (!global)
             return false;
     }
 
     /* Unwrap incoming objects. */
     if (vp->isObject()) {
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2642,17 +2642,17 @@ js_Date(JSContext *cx, uintN argc, Value
 JSObject *
 js_InitDateClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
     /* Set the static LocalTZA. */
     LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *dateProto = global->createBlankPrototype(cx, &DateClass);
     if (!dateProto)
         return NULL;
     SetDateToNaN(cx, dateProto);
 
     JSFunction *ctor = global->createConstructor(cx, js_Date, &DateClass,
                                                  CLASS_ATOM(cx, Date), MAXARGS);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -642,17 +642,17 @@ JS_PUBLIC_API(JSFunction *)
 JS_GetScriptFunction(JSContext *cx, JSScript *script)
 {
     return script->function();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
 {
-    return obj->scopeChain();
+    return obj->enclosingScope();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
 {
     return Valueify(fp)->isConstructing();
 }
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -1025,17 +1025,17 @@ InitErrorClass(JSContext *cx, GlobalObje
 }
 
 JSObject *
 js_InitExceptionClasses(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isGlobal());
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *objectProto;
     if (!js_GetClassPrototype(cx, global, JSProto_Object, &objectProto))
         return NULL;
 
     /* Initialize the base Error class first. */
     JSObject *errorProto = InitErrorClass(cx, global, JSEXN_ERR, *objectProto);
     if (!errorProto)
@@ -1328,15 +1328,15 @@ js_CopyErrorObject(JSContext *cx, JSObje
         return NULL;
     JS::Anchor<JSString *> filenameAnchor(copy->filename);
     copy->lineno = priv->lineno;
     copy->stackDepth = 0;
     copy->exnType = priv->exnType;
 
     // Create the Error object.
     JSObject *proto;
-    if (!js_GetClassPrototype(cx, scope->getGlobal(), GetExceptionProtoKey(copy->exnType), &proto))
+    if (!js_GetClassPrototype(cx, &scope->global(), GetExceptionProtoKey(copy->exnType), &proto))
         return NULL;
     JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL);
     SetExnPrivate(cx, copyobj, copy);
     autoFree.p = NULL;
     return copyobj;
 }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -87,17 +87,17 @@ JS_GetObjectFunction(JSObject *obj)
     if (obj->isFunction())
         return obj->toFunction();
     return NULL;
 }
 
 JS_FRIEND_API(JSObject *)
 JS_GetGlobalForFrame(JSStackFrame *fp)
 {
-    return Valueify(fp)->scopeChain().getGlobal();
+    return &Valueify(fp)->scopeChain().global();
 }
 
 JS_FRIEND_API(JSBool)
 JS_SplicePrototype(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     /*
      * Change the prototype of an object which hasn't been used anywhere
      * and does not share its type with another object. Unlike JS_SetPrototype,
@@ -192,35 +192,35 @@ AutoSwitchCompartment::~AutoSwitchCompar
 
 JS_FRIEND_API(bool)
 js::IsSystemCompartment(const JSCompartment *c)
 {
     return c->isSystemCompartment;
 }
 
 JS_FRIEND_API(bool)
-js::IsScopeObject(const JSObject *obj)
+js::IsScopeObject(JSObject *obj)
 {
-    return obj->isInternalScope();
+    return obj->isScope();
 }
 
 JS_FRIEND_API(JSObject *)
-js::GetObjectParentMaybeScope(const JSObject *obj)
+js::GetObjectParentMaybeScope(JSObject *obj)
 {
-    return obj->scopeChain();
+    return obj->enclosingScope();
 }
 
 JS_FRIEND_API(JSObject *)
 js::GetGlobalForObjectCrossCompartment(JSObject *obj)
 {
-    return obj->getGlobal();
+    return &obj->global();
 }
 
 JS_FRIEND_API(uint32_t)
-js::GetObjectSlotSpan(const JSObject *obj)
+js::GetObjectSlotSpan(JSObject *obj)
 {
     return obj->slotSpan();
 }
 
 JS_FRIEND_API(bool)
 js::IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx)
 {
     return obj->compartment() == cx->compartment;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -280,44 +280,43 @@ extern JS_FRIEND_DATA(js::Class) Attribu
 extern JS_FRIEND_DATA(js::Class) CallClass;
 extern JS_FRIEND_DATA(js::Class) DeclEnvClass;
 extern JS_FRIEND_DATA(js::Class) FunctionClass;
 extern JS_FRIEND_DATA(js::Class) FunctionProxyClass;
 extern JS_FRIEND_DATA(js::Class) NamespaceClass;
 extern JS_FRIEND_DATA(js::Class) OuterWindowProxyClass;
 extern JS_FRIEND_DATA(js::Class) ObjectProxyClass;
 extern JS_FRIEND_DATA(js::Class) QNameClass;
-extern JS_FRIEND_DATA(js::Class) ScriptClass;
 extern JS_FRIEND_DATA(js::Class) XMLClass;
 extern JS_FRIEND_DATA(js::Class) ObjectClass;
 
 inline js::Class *
 GetObjectClass(const JSObject *obj)
 {
     return reinterpret_cast<const shadow::Object*>(obj)->shape->base->clasp;
 }
 
 inline JSClass *
 GetObjectJSClass(const JSObject *obj)
 {
     return js::Jsvalify(GetObjectClass(obj));
 }
 
 JS_FRIEND_API(bool)
-IsScopeObject(const JSObject *obj);
+IsScopeObject(JSObject *obj);
 
 inline JSObject *
-GetObjectParent(const JSObject *obj)
+GetObjectParent(JSObject *obj)
 {
     JS_ASSERT(!IsScopeObject(obj));
-    return reinterpret_cast<const shadow::Object*>(obj)->shape->base->parent;
+    return reinterpret_cast<shadow::Object*>(obj)->shape->base->parent;
 }
 
 JS_FRIEND_API(JSObject *)
-GetObjectParentMaybeScope(const JSObject *obj);
+GetObjectParentMaybeScope(JSObject *obj);
 
 JS_FRIEND_API(JSObject *)
 GetGlobalForObjectCrossCompartment(JSObject *obj);
 
 JS_FRIEND_API(bool)
 IsOriginalScriptFunction(JSFunction *fun);
 
 JS_FRIEND_API(JSFunction *)
@@ -340,23 +339,23 @@ InitClassWithReserved(JSContext *cx, JSO
 
 JS_FRIEND_API(const Value &)
 GetFunctionNativeReserved(JSObject *fun, size_t which);
 
 JS_FRIEND_API(void)
 SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val);
 
 inline JSObject *
-GetObjectProto(const JSObject *obj)
+GetObjectProto(JSObject *obj)
 {
     return reinterpret_cast<const shadow::Object*>(obj)->type->proto;
 }
 
 inline void *
-GetObjectPrivate(const JSObject *obj)
+GetObjectPrivate(JSObject *obj)
 {
     const shadow::Object *nobj = reinterpret_cast<const shadow::Object*>(obj);
     void **addr = reinterpret_cast<void**>(&nobj->fixedSlots()[nobj->numFixedSlots()]);
     return *addr;
 }
 
 /*
  * Get a slot that is both reserved for object's clasp *and* is fixed (fits
@@ -372,27 +371,27 @@ GetReservedSlot(const JSObject *obj, siz
 inline void
 SetReservedSlot(JSObject *obj, size_t slot, const Value &value)
 {
     JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
     reinterpret_cast<shadow::Object *>(obj)->slotRef(slot) = value;
 }
 
 JS_FRIEND_API(uint32_t)
-GetObjectSlotSpan(const JSObject *obj);
+GetObjectSlotSpan(JSObject *obj);
 
 inline const Value &
-GetObjectSlot(const JSObject *obj, size_t slot)
+GetObjectSlot(JSObject *obj, size_t slot)
 {
     JS_ASSERT(slot < GetObjectSlotSpan(obj));
     return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
 }
 
 inline Shape *
-GetObjectShape(const JSObject *obj)
+GetObjectShape(JSObject *obj)
 {
     shadow::Shape *shape = reinterpret_cast<const shadow::Object*>(obj)->shape;
     return reinterpret_cast<Shape *>(shape);
 }
 
 static inline js::PropertyOp
 CastAsJSPropertyOp(JSObject *object)
 {
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -67,17 +67,17 @@
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsexn.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/TokenStream.h"
-#include "vm/CallObject.h"
+#include "vm/ScopeObject.h"
 #include "vm/Debugger.h"
 
 #if JS_HAS_GENERATORS
 # include "jsiter.h"
 #endif
 
 #if JS_HAS_XDR
 # include "jsxdrapi.h"
@@ -87,29 +87,29 @@
 #include "methodjit/MethodJIT.h"
 #endif
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
-#include "vm/CallObject-inl.h"
+#include "vm/ScopeObject-inl.h"
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 inline JSObject *
 JSObject::getThrowTypeError() const
 {
-    return getGlobal()->getThrowTypeError();
+    return global().getThrowTypeError();
 }
 
 JSBool
 js_GetArgsValue(JSContext *cx, StackFrame *fp, Value *vp)
 {
     JSObject *argsobj;
     if (fp->hasOverriddenArgs()) {
         JS_ASSERT(fp->hasCallObj());
@@ -122,17 +122,17 @@ js_GetArgsValue(JSContext *cx, StackFram
     return JS_TRUE;
 }
 
 js::ArgumentsObject *
 ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee, StackFrame *fp)
 {
     JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
 
-    JSObject *proto = callee.getGlobal()->getOrCreateObjectPrototype(cx);
+    JSObject *proto = callee.global().getOrCreateObjectPrototype(cx);
     if (!proto)
         return NULL;
 
     RootedVarTypeObject type(cx);
 
     type = proto->getNewType(cx);
     if (!type)
         return NULL;
@@ -156,27 +156,27 @@ ArgumentsObject::create(JSContext *cx, u
     data->callee.init(ObjectValue(callee));
     InitValueRange(data->slots, argc, false);
 
     /* We have everything needed to fill in the object, so make the object. */
     JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL);
     if (!obj)
         return NULL;
 
-    ArgumentsObject *argsobj = obj->asArguments();
+    ArgumentsObject &argsobj = obj->asArguments();
 
     JS_ASSERT(UINT32_MAX > (uint64_t(argc) << PACKED_BITS_COUNT));
-    argsobj->initInitialLength(argc);
-    argsobj->initData(data);
-    argsobj->setStackFrame(strict ? NULL : fp);
+    argsobj.initInitialLength(argc);
+    argsobj.initData(data);
+    argsobj.setStackFrame(strict ? NULL : fp);
 
-    JS_ASSERT(argsobj->numFixedSlots() >= NormalArgumentsObject::RESERVED_SLOTS);
-    JS_ASSERT(argsobj->numFixedSlots() >= StrictArgumentsObject::RESERVED_SLOTS);
+    JS_ASSERT(argsobj.numFixedSlots() >= NormalArgumentsObject::RESERVED_SLOTS);
+    JS_ASSERT(argsobj.numFixedSlots() >= StrictArgumentsObject::RESERVED_SLOTS);
 
-    return argsobj;
+    return &argsobj;
 }
 
 struct STATIC_SKIP_INFERENCE PutArg
 {
     PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {}
     HeapValue *dst;
     JSCompartment *compartment;
     bool operator()(uintN, Value *src) {
@@ -246,73 +246,73 @@ js_PutArgsObject(StackFrame *fp)
     } else {
         JS_ASSERT(!argsobj.maybeStackFrame());
     }
 }
 
 static JSBool
 args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
-    ArgumentsObject *argsobj = obj->asArguments();
+    ArgumentsObject &argsobj = obj->asArguments();
     if (JSID_IS_INT(id)) {
         uintN arg = uintN(JSID_TO_INT(id));
-        if (arg < argsobj->initialLength())
-            argsobj->setElement(arg, MagicValue(JS_ARGS_HOLE));
+        if (arg < argsobj.initialLength())
+            argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE));
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        argsobj->markLengthOverridden();
+        argsobj.markLengthOverridden();
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
-        argsobj->asNormalArguments()->clearCallee();
+        argsobj.asNormalArguments().clearCallee();
     }
     return true;
 }
 
 static JSBool
 ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     if (!obj->isNormalArguments())
         return true;
 
-    NormalArgumentsObject *argsobj = obj->asNormalArguments();
+    NormalArgumentsObject &argsobj = obj->asNormalArguments();
     if (JSID_IS_INT(id)) {
         /*
          * arg can exceed the number of arguments if a script changed the
          * prototype to point to another Arguments object with a bigger argc.
          */
         uintN arg = uintN(JSID_TO_INT(id));
-        if (arg < argsobj->initialLength()) {
-            JS_ASSERT(!argsobj->element(arg).isMagic(JS_ARGS_HOLE));
-            if (StackFrame *fp = argsobj->maybeStackFrame())
+        if (arg < argsobj.initialLength()) {
+            JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE));
+            if (StackFrame *fp = argsobj.maybeStackFrame())
                 *vp = fp->canonicalActualArg(arg);
             else
-                *vp = argsobj->element(arg);
+                *vp = argsobj.element(arg);
         }
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        if (!argsobj->hasOverriddenLength())
-            vp->setInt32(argsobj->initialLength());
+        if (!argsobj.hasOverriddenLength())
+            vp->setInt32(argsobj.initialLength());
     } else {
         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
-        const Value &v = argsobj->callee();
+        const Value &v = argsobj.callee();
         if (!v.isMagic(JS_ARGS_HOLE))
             *vp = v;
     }
     return true;
 }
 
 static JSBool
 ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
     if (!obj->isNormalArguments())
         return true;
 
-    NormalArgumentsObject *argsobj = obj->asNormalArguments();
+    NormalArgumentsObject &argsobj = obj->asNormalArguments();
 
     if (JSID_IS_INT(id)) {
         uintN arg = uintN(JSID_TO_INT(id));
-        if (arg < argsobj->initialLength()) {
-            if (StackFrame *fp = argsobj->maybeStackFrame()) {
+        if (arg < argsobj.initialLength()) {
+            if (StackFrame *fp = argsobj.maybeStackFrame()) {
                 JSScript *script = fp->functionScript();
                 if (script->usesArguments) {
                     if (arg < fp->numFormalArgs())
                         TypeScript::SetArgument(cx, script, arg, *vp);
                     fp->canonicalActualArg(arg) = *vp;
                 }
                 return true;
             }
@@ -326,178 +326,178 @@ ArgSetter(JSContext *cx, JSObject *obj, 
      * For simplicity we use delete/define to replace the property with one
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value. Note also that we must define the property instead
      * of setting it in case the user has changed the prototype to an object
      * that has a setter for this id.
      */
     AutoValueRooter tvr(cx);
-    return js_DeleteProperty(cx, argsobj, id, tvr.addr(), false) &&
-           js_DefineProperty(cx, argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
+    return js_DeleteProperty(cx, &argsobj, id, tvr.addr(), false) &&
+           js_DefineProperty(cx, &argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
 }
 
 static JSBool
 args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
              JSObject **objp)
 {
     *objp = NULL;
 
-    NormalArgumentsObject *argsobj = obj->asNormalArguments();
+    NormalArgumentsObject &argsobj = obj->asNormalArguments();
 
     uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
-        if (arg >= argsobj->initialLength() || argsobj->element(arg).isMagic(JS_ARGS_HOLE))
+        if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
             return true;
 
         attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        if (argsobj->hasOverriddenLength())
+        if (argsobj.hasOverriddenLength())
             return true;
     } else {
         if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
             return true;
 
-        if (argsobj->callee().isMagic(JS_ARGS_HOLE))
+        if (argsobj.callee().isMagic(JS_ARGS_HOLE))
             return true;
     }
 
     Value undef = UndefinedValue();
-    if (!js_DefineProperty(cx, argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
+    if (!js_DefineProperty(cx, &argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
         return JS_FALSE;
 
-    *objp = argsobj;
+    *objp = &argsobj;
     return true;
 }
 
 static JSBool
 args_enumerate(JSContext *cx, JSObject *obj)
 {
-    NormalArgumentsObject *argsobj = obj->asNormalArguments();
+    NormalArgumentsObject &argsobj = obj->asNormalArguments();
 
     /*
      * Trigger reflection in args_resolve using a series of js_LookupProperty
      * calls.
      */
-    int argc = int(argsobj->initialLength());
+    int argc = int(argsobj.initialLength());
     for (int i = -2; i != argc; i++) {
         jsid id = (i == -2)
                   ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
                   : (i == -1)
                   ? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
                   : INT_TO_JSID(i);
 
         JSObject *pobj;
         JSProperty *prop;
-        if (!js_LookupProperty(cx, argsobj, id, &pobj, &prop))
+        if (!js_LookupProperty(cx, &argsobj, id, &pobj, &prop))
             return false;
     }
     return true;
 }
 
 static JSBool
 StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     if (!obj->isStrictArguments())
         return true;
 
-    StrictArgumentsObject *argsobj = obj->asStrictArguments();
+    StrictArgumentsObject &argsobj = obj->asStrictArguments();
 
     if (JSID_IS_INT(id)) {
         /*
          * arg can exceed the number of arguments if a script changed the
          * prototype to point to another Arguments object with a bigger argc.
          */
         uintN arg = uintN(JSID_TO_INT(id));
-        if (arg < argsobj->initialLength()) {
-            const Value &v = argsobj->element(arg);
+        if (arg < argsobj.initialLength()) {
+            const Value &v = argsobj.element(arg);
             if (!v.isMagic(JS_ARGS_HOLE))
                 *vp = v;
         }
     } else {
         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
-        if (!argsobj->hasOverriddenLength())
-            vp->setInt32(argsobj->initialLength());
+        if (!argsobj.hasOverriddenLength())
+            vp->setInt32(argsobj.initialLength());
     }
     return true;
 }
 
 static JSBool
 StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
     if (!obj->isStrictArguments())
         return true;
 
-    StrictArgumentsObject *argsobj = obj->asStrictArguments();
+    StrictArgumentsObject &argsobj = obj->asStrictArguments();
 
     if (JSID_IS_INT(id)) {
         uintN arg = uintN(JSID_TO_INT(id));
-        if (arg < argsobj->initialLength()) {
-            argsobj->setElement(arg, *vp);
+        if (arg < argsobj.initialLength()) {
+            argsobj.setElement(arg, *vp);
             return true;
         }
     } else {
         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
     }
 
     /*
      * For simplicity we use delete/set to replace the property with one
      * backed by the default Object getter and setter. Note that we rely on
      * args_delProperty to clear the corresponding reserved slot so the GC can
      * collect its value.
      */
     AutoValueRooter tvr(cx);
-    return js_DeleteProperty(cx, argsobj, id, tvr.addr(), strict) &&
-           js_SetPropertyHelper(cx, argsobj, id, 0, vp, strict);
+    return js_DeleteProperty(cx, &argsobj, id, tvr.addr(), strict) &&
+           js_SetPropertyHelper(cx, &argsobj, id, 0, vp, strict);
 }
 
 static JSBool
 strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
     *objp = NULL;
 
-    StrictArgumentsObject *argsobj = obj->asStrictArguments();
+    StrictArgumentsObject &argsobj = obj->asStrictArguments();
 
     uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
     PropertyOp getter = StrictArgGetter;
     StrictPropertyOp setter = StrictArgSetter;
 
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
-        if (arg >= argsobj->initialLength() || argsobj->element(arg).isMagic(JS_ARGS_HOLE))
+        if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
             return true;
 
         attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
-        if (argsobj->hasOverriddenLength())
+        if (argsobj.hasOverriddenLength())
             return true;
     } else {
         if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
             !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
             return true;
         }
 
         attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
-        getter = CastAsPropertyOp(argsobj->getThrowTypeError());
-        setter = CastAsStrictPropertyOp(argsobj->getThrowTypeError());
+        getter = CastAsPropertyOp(argsobj.getThrowTypeError());
+        setter = CastAsStrictPropertyOp(argsobj.getThrowTypeError());
     }
 
     Value undef = UndefinedValue();
-    if (!js_DefineProperty(cx, argsobj, id, &undef, getter, setter, attrs))
+    if (!js_DefineProperty(cx, &argsobj, id, &undef, getter, setter, attrs))
         return false;
 
-    *objp = argsobj;
+    *objp = &argsobj;
     return true;
 }
 
 static JSBool
 strictargs_enumerate(JSContext *cx, JSObject *obj)
 {
-    StrictArgumentsObject *argsobj = obj->asStrictArguments();
+    StrictArgumentsObject *argsobj = &obj->asStrictArguments();
 
     /*
      * Trigger reflection in strictargs_resolve using a series of
      * js_LookupProperty calls.
      */
     JSObject *pobj;
     JSProperty *prop;
 
@@ -519,38 +519,38 @@ strictargs_enumerate(JSContext *cx, JSOb
     }
 
     return true;
 }
 
 static void
 args_finalize(JSContext *cx, JSObject *obj)
 {
-    cx->free_(reinterpret_cast<void *>(obj->asArguments()->data()));
+    cx->free_(reinterpret_cast<void *>(obj->asArguments().data()));
 }
 
 static void
 args_trace(JSTracer *trc, JSObject *obj)
 {
-    ArgumentsObject *argsobj = obj->asArguments();
-    ArgumentsData *data = argsobj->data();
+    ArgumentsObject &argsobj = obj->asArguments();
+    ArgumentsData *data = argsobj.data();
     MarkValue(trc, data->callee, js_callee_str);
-    MarkValueRange(trc, argsobj->initialLength(), data->slots, js_arguments_str);
+    MarkValueRange(trc, argsobj.initialLength(), data->slots, js_arguments_str);
 
     /*
      * If a generator's arguments or call object escapes, and the generator
      * frame is not executing, the generator object needs to be marked because
      * it is not otherwise reachable. An executing generator is rooted by its
      * invocation.  To distinguish the two cases (which imply different access
      * paths to the generator object), we use the JSFRAME_FLOATING_GENERATOR
      * flag, which is only set on the StackFrame kept in the generator object's
      * JSGenerator.
      */
 #if JS_HAS_GENERATORS
-    StackFrame *fp = argsobj->maybeStackFrame();
+    StackFrame *fp = argsobj.maybeStackFrame();
     if (fp && fp->isFloatingGenerator())
         MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object");
 #endif
 }
 
 /*
  * The classes below collaborate to lazily reflect and synchronize actual
  * argument values, argument count, and callee function object stored in a
@@ -601,63 +601,16 @@ Class js::StrictArgumentsObjectClass = {
     NULL,                    /* checkAccess */
     NULL,                    /* call        */
     NULL,                    /* construct   */
     NULL,                    /* xdrObject   */
     NULL,                    /* hasInstance */
     args_trace
 };
 
-/*
- * A Declarative Environment object stores its active StackFrame pointer in
- * its private slot, just as Call and Arguments objects do.
- */
-Class js::DeclEnvClass = {
-    js_Object_str,
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(CallObject::DECL_ENV_RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    JS_PropertyStub,         /* addProperty */
-    JS_PropertyStub,         /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub
-};
-
-static inline JSObject *
-NewDeclEnvObject(JSContext *cx, StackFrame *fp)
-{
-    RootedVarTypeObject type(cx);
-    type = cx->compartment->getEmptyType(cx);
-    if (!type)
-        return NULL;
-
-    JSObject *parent = fp->scopeChain().getGlobal();
-
-    RootedVarShape emptyDeclEnvShape(cx);
-    emptyDeclEnvShape =
-        EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
-                                    parent, CallObject::DECL_ENV_FINALIZE_KIND);
-    if (!emptyDeclEnvShape)
-        return NULL;
-
-    JSObject *envobj = JSObject::create(cx, CallObject::DECL_ENV_FINALIZE_KIND,
-                                        emptyDeclEnvShape, type, NULL);
-    if (!envobj)
-        return NULL;
-    envobj->setPrivate(fp);
-
-    if (!envobj->setInternalScopeChain(cx, &fp->scopeChain()))
-        return NULL;
-
-    return envobj;
-}
-
 namespace js {
 
 CallObject *
 CreateFunCallObject(JSContext *cx, StackFrame *fp)
 {
     JS_ASSERT(fp->isNonEvalFunctionFrame());
     JS_ASSERT(!fp->hasCallObj());
 
@@ -665,17 +618,17 @@ CreateFunCallObject(JSContext *cx, Stack
     JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
                  scopeChain->getPrivate() != fp);
 
     /*
      * For a named function expression Call's parent points to an environment
      * object holding function's name.
      */
     if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
-        scopeChain = NewDeclEnvObject(cx, fp);
+        scopeChain = DeclEnvObject::create(cx, fp);
         if (!scopeChain)
             return NULL;
 
         if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
                                   ObjectValue(fp->callee()), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return NULL;
         }
@@ -784,21 +737,19 @@ js_PutCallObject(StackFrame *fp)
             if (nesting && script->isOuterFunction) {
                 nesting->argArray = callobj.argArray();
                 nesting->varArray = callobj.varArray();
             }
         }
 
         /* Clear private pointers to fp, which is about to go away. */
         if (js_IsNamedLambda(fun)) {
-            JSObject *env = callobj.internalScopeChain();
-
-            JS_ASSERT(env->isDeclEnv());
-            JS_ASSERT(env->getPrivate() == fp);
-            env->setPrivate(NULL);
+            JSObject &env = callobj.enclosingScope();
+            JS_ASSERT(env.asDeclEnv().maybeStackFrame() == fp);
+            env.setPrivate(NULL);
         }
     }
 
     callobj.setStackFrame(NULL);
 }
 
 namespace js {
 
@@ -1950,17 +1901,17 @@ JSFunctionSpec function_methods[] = {
 
 JSBool
 Function(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Block this call if security callbacks forbid it. */
     RootedVar<GlobalObject*> global(cx);
-    global = args.callee().getGlobal();
+    global = &args.callee().global();
     if (!global->isRuntimeCodeGenEnabled(cx)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
         return false;
     }
 
     Bindings bindings(cx);
 
     const char *filename;
@@ -2245,17 +2196,17 @@ js_CloneFunctionObject(JSContext *cx, JS
             JS_ASSERT(script->compartment() == fun->compartment());
             JS_ASSERT(script->compartment() != cx->compartment);
 
             clone->script().init(NULL);
             JSScript *cscript = js_CloneScript(cx, script);
             if (!cscript)
                 return NULL;
 
-            cscript->globalObject = clone->getGlobal();
+            cscript->globalObject = &clone->global();
             clone->setScript(cscript);
             if (!cscript->typeSetFunction(cx, clone))
                 return NULL;
 
             js_CallNewScriptHook(cx, clone->script(), clone);
             Debugger::onNewScript(cx, clone->script(), NULL);
         }
     }
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -40,16 +40,18 @@
 #ifndef jsfuninlines_h___
 #define jsfuninlines_h___
 
 #include "jsfun.h"
 #include "jsscript.h"
 
 #include "vm/GlobalObject.h"
 
+#include "vm/ScopeObject-inl.h"
+
 inline bool
 JSFunction::inStrictMode() const
 {
     return script()->strictModeCode;
 }
 
 inline JSObject *
 JSFunction::environment() const
@@ -346,27 +348,27 @@ IsBuiltinFunctionConstructor(JSFunction 
 const Shape *
 LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj);
 
 static inline JSObject *
 SkipScopeParent(JSObject *parent)
 {
     if (!parent)
         return NULL;
-    while (parent->isInternalScope())
-        parent = parent->scopeChain();
+    while (parent->isScope())
+        parent = &parent->asScope().enclosingScope();
     return parent;
 }
 
 inline JSFunction *
 CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
                     gc::AllocKind kind = JSFunction::FinalizeKind)
 {
     JS_ASSERT(parent);
-    JSObject *proto = parent->getGlobal()->getOrCreateFunctionPrototype(cx);
+    JSObject *proto = parent->global().getOrCreateFunctionPrototype(cx);
     if (!proto)
         return NULL;
 
     return js_CloneFunctionObject(cx, fun, parent, proto, kind);
 }
 
 inline JSFunction *
 CloneFunctionObjectIfNotSingleton(JSContext *cx, JSFunction *fun, JSObject *parent)
@@ -390,17 +392,17 @@ CloneFunctionObjectIfNotSingleton(JSCont
 
 inline JSFunction *
 CloneFunctionObject(JSContext *cx, JSFunction *fun)
 {
     /*
      * Variant which makes an exact clone of fun, preserving parent and proto.
      * Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
      * is not equivalent: API clients, including XPConnect, can reparent
-     * objects so that fun->getGlobal() != fun->getProto()->getGlobal().
+     * objects so that fun->global() != fun->getProto()->global().
      * See ReparentWrapperIfFound.
      */
     JS_ASSERT(fun->getParent() && fun->getProto());
 
     if (fun->hasSingletonType())
         return fun;
 
     return js_CloneFunctionObject(cx, fun, fun->environment(), fun->getProto(),
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -91,17 +91,17 @@
 #include "frontend/Parser.h"
 #include "methodjit/MethodJIT.h"
 #include "vm/Debugger.h"
 #include "vm/String.h"
 
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 
-#include "vm/CallObject-inl.h"
+#include "vm/ScopeObject-inl.h"
 #include "vm/String-inl.h"
 
 #ifdef MOZ_VALGRIND
 # define JS_VALGRIND
 #endif
 #ifdef JS_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1886,17 +1886,17 @@ TypeSet::getSingleton(JSContext *cx, boo
 
 static inline bool
 TypeHasGlobal(Type type, JSObject *global)
 {
     if (type.isUnknown() || type.isAnyObject())
         return false;
 
     if (type.isSingleObject())
-        return type.singleObject()->getGlobal() == global;
+        return &type.singleObject()->global() == global;
 
     if (type.isTypeObject())
         return type.typeObject()->getGlobal() == global;
 
     JS_ASSERT(type.isPrimitive());
     return true;
 }
 
@@ -5194,18 +5194,18 @@ TypeScript::SetScope(JSContext *cx, JSSc
     JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
                  scope->asCall().getCalleeFunction() != fun);
 
     if (!script->compileAndGo) {
         script->types->global = NULL;
         return true;
     }
 
-    JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal());
-    script->types->global = fun ? fun->getGlobal() : scope->getGlobal();
+    JS_ASSERT_IF(fun && scope, fun->global() == scope->global());
+    script->types->global = fun ? &fun->global() : &scope->global();
 
     /*
      * Update the parent in the script's bindings. The bindings are created
      * with a NULL parent, and fixing the parent now avoids the need to reshape
      * every time a call object is created from the bindings.
      */
     if (!script->bindings.setParent(cx, script->types->global))
         return false;
@@ -5226,17 +5226,17 @@ TypeScript::SetScope(JSContext *cx, JSSc
         return true;
     }
 
     /*
      * Walk the scope chain to the next call object, which will be the function
      * the script is nested inside.
      */
     while (!scope->isCall())
-        scope = scope->internalScopeChain();
+        scope = &scope->asScope().enclosingScope();
 
     CallObject &call = scope->asCall();
 
     /* The isInnerFunction test ensures there is no intervening strict eval call object. */
     JS_ASSERT(!call.isForEval());
 
     /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
     JSFunction *parentFun = call.getCalleeFunction();
@@ -5259,19 +5259,19 @@ TypeScript::SetScope(JSContext *cx, JSSc
      * compartment and there may be inner function objects parented to an
      * activation of the outer function sticking around. In such cases, treat
      * the parent's call object as the most recent one, so that it is not
      * marked as reentrant.
      */
     if (!parent->ensureHasTypes(cx))
         return false;
     if (!parent->types->hasScope()) {
-        if (!SetScope(cx, parent, scope->internalScopeChain()))
+        if (!SetScope(cx, parent, &call.enclosingScope()))
             return false;
-        parent->nesting()->activeCall = scope;
+        parent->nesting()->activeCall = &call;
         parent->nesting()->argArray = Valueify(call.argArray());
         parent->nesting()->varArray = Valueify(call.varArray());
     }
 
     JS_ASSERT(!script->types->nesting);
 
     /* Construct and link nesting information for the two functions. */
 
@@ -5354,32 +5354,32 @@ ClearActiveNesting(JSScript *start)
 static void
 CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
 {
   restart:
     JSScript *parent = script->nesting()->parent;
     JS_ASSERT(parent);
 
     while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
-        scope = scope->internalScopeChain();
+        scope = &scope->asScope().enclosingScope();
 
     if (scope != parent->nesting()->activeCall) {
         parent->reentrantOuterFunction = true;
         MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
 
         /*
          * Continue checking parents to see if this is reentrant for them too.
          * We don't need to check this in for non-reentrant calls on the outer
          * function: when we entered any outer function to the immediate parent
          * we cleared the active call for its transitive children, so a
          * non-reentrant call on a child is also a non-reentrant call on the
          * parent.
          */
         if (parent->nesting()->parent) {
-            scope = scope->internalScopeChain();
+            scope = &scope->asScope().enclosingScope();
             script = parent;
             goto restart;
         }
     }
 }
 
 void
 NestingPrologue(JSContext *cx, StackFrame *fp)
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1245,19 +1245,19 @@ TypeObject::setFlagsFromKey(JSContext *c
     if (!hasAllFlags(flags))
         setFlags(cx, flags);
 }
 
 inline JSObject *
 TypeObject::getGlobal()
 {
     if (singleton)
-        return singleton->getGlobal();
+        return &singleton->global();
     if (interpretedFunction && interpretedFunction->script()->compileAndGo)
-        return interpretedFunction->getGlobal();
+        return &interpretedFunction->global();
     return NULL;
 }
 
 inline void
 TypeObject::writeBarrierPre(TypeObject *type)
 {
 #ifdef JSGC_INCREMENTAL
     if (!type)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -134,17 +134,17 @@ using namespace js::types;
  * place a mutable clone of it on scopeChain.
  *
  * This lazy cloning is implemented in GetScopeChain, which is also used in
  * some other cases --- entering 'with' blocks, for example.
  */
 JSObject *
 js::GetScopeChain(JSContext *cx, StackFrame *fp)
 {
-    JSObject *sharedBlock = fp->maybeBlockChain();
+    StaticBlockObject *sharedBlock = fp->maybeBlockChain();
 
     if (!sharedBlock) {
         /*
          * Don't force a call object for a lightweight function call, but do
          * insist that there is a call object for a heavyweight function call.
          */
         JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(),
                      fp->hasCallObj());
@@ -170,17 +170,17 @@ js::GetScopeChain(JSContext *cx, StackFr
         /*
          * scopeChain includes all blocks whose static scope we're within that
          * have already been cloned.  Find the innermost such block.  Its
          * prototype should appear on blockChain; we'll clone blockChain up
          * to, but not including, that prototype.
          */
         limitClone = &fp->scopeChain();
         while (limitClone->isWith())
-            limitClone = limitClone->internalScopeChain();
+            limitClone = &limitClone->asWith().enclosingScope();
         JS_ASSERT(limitClone);
 
         /*
          * It may seem like we don't know enough about limitClone to be able
          * to just grab its prototype as we do here, but it's actually okay.
          *
          * If limitClone is a block object belonging to this frame, then its
          * prototype is the innermost entry in blockChain that we have already
@@ -202,55 +202,54 @@ js::GetScopeChain(JSContext *cx, StackFr
         if (limitBlock == sharedBlock)
             return &fp->scopeChain();
     }
 
     /*
      * Special-case cloning the innermost block; this doesn't have enough in
      * common with subsequent steps to include in the loop.
      *
-     * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
-     * populate it below.
+     * create() leaves the clone's enclosingScope unset. We set it below.
      */
-    JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
+    ClonedBlockObject *innermostNewChild = ClonedBlockObject::create(cx, *sharedBlock, fp);
     if (!innermostNewChild)
         return NULL;
 
     /*
      * Clone our way towards outer scopes until we reach the innermost
      * enclosing function, or the innermost block we've already cloned.
      */
-    JSObject *newChild = innermostNewChild;
+    ClonedBlockObject *newChild = innermostNewChild;
     for (;;) {
         JS_ASSERT(newChild->getProto() == sharedBlock);
-        sharedBlock = sharedBlock->staticBlockScopeChain();
+        sharedBlock = sharedBlock->enclosingBlock();
 
         /* Sometimes limitBlock will be NULL, so check that first.  */
         if (sharedBlock == limitBlock || !sharedBlock)
             break;
 
         /* As in the call above, we don't know the real parent yet.  */
-        JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp);
+        ClonedBlockObject *clone = ClonedBlockObject::create(cx, *sharedBlock, fp);
         if (!clone)
             return NULL;
 
-        if (!newChild->setInternalScopeChain(cx, clone))
+        if (!newChild->setEnclosingScope(cx, *clone))
             return NULL;
         newChild = clone;
     }
-    if (!newChild->setInternalScopeChain(cx, &fp->scopeChain()))
+    if (!newChild->setEnclosingScope(cx, fp->scopeChain()))
         return NULL;
 
 
     /*
      * If we found a limit block belonging to this frame, then we should have
      * found it in blockChain.
      */
     JS_ASSERT_IF(limitBlock &&
-                 limitBlock->isBlock() &&
+                 limitBlock->isClonedBlock() &&
                  limitClone->getPrivate() == js_FloatingFrameIfGenerator(cx, fp),
                  sharedBlock);
 
     /* Place our newly cloned blocks at the head of the scope chain.  */
     fp->setScopeChainNoCallObj(*innermostNewChild);
     return innermostNewChild;
 }
 
@@ -322,17 +321,17 @@ js::BoxNonStrictThis(JSContext *cx, cons
     JS_ASSERT(!thisv.isMagic());
 
 #ifdef DEBUG
     JSFunction *fun = call.callee().isFunction() ? call.callee().toFunction() : NULL;
     JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->inStrictMode());
 #endif
 
     if (thisv.isNullOrUndefined()) {
-        JSObject *thisp = call.callee().getGlobal()->thisObject(cx);
+        JSObject *thisp = call.callee().global().thisObject(cx);
         if (!thisp)
             return false;
         call.thisv().setObject(*thisp);
         return true;
     }
 
     if (!thisv.isObject())
         return !!js_PrimitiveToObject(cx, &thisv);
@@ -454,17 +453,17 @@ js::RunScript(JSContext *cx, JSScript *s
     JS_ASSERT(fp == cx->fp());
     JS_ASSERT(fp->script() == script);
 #ifdef JS_METHODJIT_SPEW
     JMCheckLogging();
 #endif
 
     /* FIXME: Once bug 470510 is fixed, make this an assert. */
     if (script->compileAndGo) {
-        if (fp->scopeChain().getGlobal()->isCleared()) {
+        if (fp->scopeChain().global().isCleared()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
             return false;
         }
     }
 
 #ifdef JS_METHODJIT
     mjit::CompileStatus status;
     status = mjit::CanMethodJIT(cx, script, fp->isConstructing(),
@@ -1083,73 +1082,68 @@ EnterWith(JSContext *cx, jsint stackInde
     JSObject *parent = GetScopeChain(cx, fp);
     if (!parent)
         return JS_FALSE;
 
     OBJ_TO_INNER_OBJECT(cx, obj);
     if (!obj)
         return JS_FALSE;
 
-    JSObject *withobj = js_NewWithObject(cx, obj, parent,
-                                         sp + stackIndex - fp->base());
+    JSObject *withobj = WithObject::create(cx, fp, *obj, *parent,
+                                           sp + stackIndex - fp->base());
     if (!withobj)
         return JS_FALSE;
 
     fp->setScopeChainNoCallObj(*withobj);
     return JS_TRUE;
 }
 
 static void
 LeaveWith(JSContext *cx)
 {
-    JSObject *withobj;
-
-    withobj = &cx->fp()->scopeChain();
-    JS_ASSERT(withobj->getClass() == &WithClass);
-    JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
-    JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
-    withobj->setPrivate(NULL);
-    cx->fp()->setScopeChainNoCallObj(*withobj->internalScopeChain());
+    WithObject &withobj = cx->fp()->scopeChain().asWith();
+    JS_ASSERT(withobj.maybeStackFrame() == js_FloatingFrameIfGenerator(cx, cx->fp()));
+    JS_ASSERT(withobj.stackDepth() >= 0);
+    withobj.setStackFrame(NULL);
+    cx->fp()->setScopeChainNoCallObj(withobj.enclosingScope());
 }
 
 bool
-js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, int stackDepth)
+js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth)
 {
     return (obj.isWith() || obj.isBlock()) &&
            obj.getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) &&
-           OBJ_BLOCK_DEPTH(cx, &obj) >= stackDepth;
+           obj.asNestedScope().stackDepth() >= stackDepth;
 }
 
 /*
  * Unwind block and scope chains to match the given depth. The function sets
  * fp->sp on return to stackDepth.
  */
 bool
-js::UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind)
+js::UnwindScope(JSContext *cx, uint32_t stackDepth, JSBool normalUnwind)
 {
-    JS_ASSERT(stackDepth >= 0);
     JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp);
 
     StackFrame *fp = cx->fp();
-    JSObject *obj = fp->maybeBlockChain();
-    while (obj) {
-        JS_ASSERT(obj->isStaticBlock());
-        if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
+    StaticBlockObject *block = fp->maybeBlockChain();
+    while (block) {
+        if (block->stackDepth() < stackDepth)
             break;
-        obj = obj->staticBlockScopeChain();
+        block = block->enclosingBlock();
     }
-    fp->setBlockChain(obj);
+    fp->setBlockChain(block);
 
     for (;;) {
         JSObject &scopeChain = fp->scopeChain();
         if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth))
             break;
-        if (scopeChain.isBlock()) {
+        if (scopeChain.isClonedBlock()) {
             /* Don't fail until after we've updated all stacks. */
-            normalUnwind &= js_PutBlockObject(cx, normalUnwind);
+            normalUnwind &= scopeChain.asClonedBlock().put(cx, normalUnwind);
         } else {
             LeaveWith(cx);
         }
     }
 
     cx->regs().sp = fp->base() + stackDepth;
     return normalUnwind;
 }
@@ -1917,27 +1911,27 @@ BEGIN_CASE(JSOP_POP)
     regs.sp--;
 END_CASE(JSOP_POP)
 
 BEGIN_CASE(JSOP_POPN)
 {
     regs.sp -= GET_UINT16(regs.pc);
 #ifdef DEBUG
     JS_ASSERT(regs.fp()->base() <= regs.sp);
-    JSObject *obj = regs.fp()->maybeBlockChain();
-    JS_ASSERT_IF(obj,
-                 OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
+    StaticBlockObject *block = regs.fp()->maybeBlockChain();
+    JS_ASSERT_IF(block,
+                 block->stackDepth() + block->slotCount()
                  <= (size_t) (regs.sp - regs.fp()->base()));
-    for (obj = &regs.fp()->scopeChain(); obj; obj = obj->scopeChain()) {
+    for (JSObject *obj = &regs.fp()->scopeChain(); obj; obj = obj->enclosingScope()) {
         if (!obj->isBlock() || !obj->isWith())
             continue;
         if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp()))
             break;
-        JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj)
-                  + (obj->isBlock() ? OBJ_BLOCK_COUNT(cx, obj) : 1)
+        JS_ASSERT(regs.fp()->base() + obj->asBlock().stackDepth()
+                  + (obj->isBlock() ? obj->asBlock().slotCount() : 1)
                   <= regs.sp);
     }
 #endif
 }
 END_CASE(JSOP_POPN)
 
 BEGIN_CASE(JSOP_SETRVAL)
 BEGIN_CASE(JSOP_POPV)
@@ -2340,17 +2334,17 @@ BEGIN_CASE(JSOP_ENUMCONSTELEM)
         goto error;
     }
     regs.sp -= 3;
 }
 END_CASE(JSOP_ENUMCONSTELEM)
 #endif
 
 BEGIN_CASE(JSOP_BINDGNAME)
-    PUSH_OBJECT(*regs.fp()->scopeChain().getGlobal());
+    PUSH_OBJECT(regs.fp()->scopeChain().global());
 END_CASE(JSOP_BINDGNAME)
 
 BEGIN_CASE(JSOP_BINDNAME)
 {
     JSObject *obj;
     do {
         /*
          * We can skip the property lookup for the global object. If the
@@ -2974,19 +2968,19 @@ BEGIN_CASE(JSOP_LENGTH)
                 JSObject *obj = &vp->toObject();
                 if (obj->isArray()) {
                     jsuint length = obj->getArrayLength();
                     rval = NumberValue(length);
                     break;
                 }
 
                 if (obj->isArguments()) {
-                    ArgumentsObject *argsobj = obj->asArguments();
-                    if (!argsobj->hasOverriddenLength()) {
-                        uint32_t length = argsobj->initialLength();
+                    ArgumentsObject &argsobj = obj->asArguments();
+                    if (!argsobj.hasOverriddenLength()) {
+                        uint32_t length = argsobj.initialLength();
                         JS_ASSERT(length < INT32_MAX);
                         rval = Int32Value(int32_t(length));
                         break;
                     }
                 }
 
                 if (js_IsTypedArray(obj)) {
                     JSObject *tarray = TypedArray::getTypedArray(obj);
@@ -3033,24 +3027,24 @@ END_CASE(JSOP_GETPROP)
 BEGIN_CASE(JSOP_CALLPROP)
 {
     Value lval = regs.sp[-1];
 
     Value objv;
     if (lval.isObject()) {
         objv = lval;
     } else {
-        GlobalObject *global = regs.fp()->scopeChain().getGlobal();
+        GlobalObject &global = regs.fp()->scopeChain().global();
         JSObject *pobj;
         if (lval.isString()) {
-            pobj = global->getOrCreateStringPrototype(cx);
+            pobj = global.getOrCreateStringPrototype(cx);
         } else if (lval.isNumber()) {
-            pobj = global->getOrCreateNumberPrototype(cx);
+            pobj = global.getOrCreateNumberPrototype(cx);
         } else if (lval.isBoolean()) {
-            pobj = global->getOrCreateBooleanPrototype(cx);
+            pobj = global.getOrCreateBooleanPrototype(cx);
         } else {
             JS_ASSERT(lval.isNull() || lval.isUndefined());
             js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
             goto error;
         }
         if (!pobj)
             goto error;
         objv.setObject(*pobj);
@@ -3120,17 +3114,17 @@ BEGIN_CASE(JSOP_SETMETHOD)
 {
     Value rval = regs.sp[-1];
     JS_ASSERT_IF(op == JSOP_SETMETHOD, IsFunctionObject(rval));
     Value &lref = regs.sp[-2];
     JS_ASSERT_IF(op == JSOP_SETNAME, lref.isObject());
     JSObject *obj;
     VALUE_TO_OBJECT(cx, &lref, obj);
 
-    JS_ASSERT_IF(op == JSOP_SETGNAME, obj == regs.fp()->scopeChain().getGlobal());
+    JS_ASSERT_IF(op == JSOP_SETGNAME, obj == &regs.fp()->scopeChain().global());
 
     do {
         PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
 
         /*
          * Probe the property cache, specializing for two important
          * set-property cases. First:
          *
@@ -3253,17 +3247,17 @@ BEGIN_CASE(JSOP_GETELEM)
     if (IsDefinitelyIndex(rref, &index)) {
         if (obj->isDenseArray()) {
             if (index < obj->getDenseArrayInitializedLength()) {
                 rval = obj->getDenseArrayElement(index);
                 if (!rval.isMagic())
                     goto end_getelem;
             }
         } else if (obj->isArguments()) {
-            if (obj->asArguments()->getElement(index, &rval))
+            if (obj->asArguments().getElement(index, &rval))
                 goto end_getelem;
         }
 
         if (!obj->getElement(cx, index, &rval))
             goto error;
     } else {
         if (script->hasAnalysis())
             script->analysis()->getCode(regs.pc).getStringElement = true;
@@ -3501,17 +3495,17 @@ BEGIN_CASE(JSOP_GETGNAME)
 BEGIN_CASE(JSOP_CALLGNAME)
 BEGIN_CASE(JSOP_NAME)
 BEGIN_CASE(JSOP_CALLNAME)
 {
     JSObject *obj = &regs.fp()->scopeChain();
 
     bool global = js_CodeSpec[op].format & JOF_GNAME;
     if (global)
-        obj = obj->getGlobal();
+        obj = &obj->global();
 
     Value rval;
 
     PropertyCacheEntry *entry;
     JSObject *obj2;
     JSAtom *atom;
     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
     if (!atom) {
@@ -3547,18 +3541,18 @@ BEGIN_CASE(JSOP_CALLNAME)
 
     /* Take the slow path if prop was not found in a native object. */
     if (!obj->isNative() || !obj2->isNative()) {
         if (!obj->getGeneric(cx, id, &rval))
             goto error;
     } else {
         Shape *shape = (Shape *)prop;
         JSObject *normalized = obj;
-        if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
-            normalized = js_UnwrapWithObject(cx, normalized);
+        if (normalized->isWith() && !shape->hasDefaultGetter())
+            normalized = &normalized->asWith().object();
         NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval);
     }
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, regs.pc, rval);
 
     /* obj must be on the scope chain, thus not a function. */
     if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
@@ -3627,17 +3621,17 @@ END_CASE(JSOP_OBJECT)
 
 BEGIN_CASE(JSOP_REGEXP)
 {
     /*
      * Push a regexp object cloned from the regexp literal object mapped by the
      * bytecode at pc.
      */
     jsatomid index = GET_FULL_INDEX(0);
-    JSObject *proto = regs.fp()->scopeChain().getGlobal()->getOrCreateRegExpPrototype(cx);
+    JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx);
     if (!proto)
         goto error;
     JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
     if (!obj)
         goto error;
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_REGEXP)
@@ -5097,106 +5091,101 @@ BEGIN_CASE(JSOP_XMLPI)
 }
 END_CASE(JSOP_XMLPI)
 
 BEGIN_CASE(JSOP_GETFUNNS)
 {
     JS_ASSERT(!script->strictModeCode);
 
     Value rval;
-    if (!cx->fp()->scopeChain().getGlobal()->getFunctionNamespace(cx, &rval))
+    if (!cx->fp()->scopeChain().global().getFunctionNamespace(cx, &rval))
         goto error;
     PUSH_COPY(rval);
 }
 END_CASE(JSOP_GETFUNNS)
 #endif /* JS_HAS_XML_SUPPORT */
 
 BEGIN_CASE(JSOP_ENTERBLOCK)
 BEGIN_CASE(JSOP_ENTERLET0)
 BEGIN_CASE(JSOP_ENTERLET1)
 {
     JSObject *obj;
     LOAD_OBJECT(0, obj);
-    JS_ASSERT(obj->isStaticBlock());
-    JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain());
+    StaticBlockObject &blockObj = obj->asStaticBlock();
+    JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock());
 
     if (op == JSOP_ENTERBLOCK) {
-        JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
-        Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
+        JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp);
+        Value *vp = regs.sp + blockObj.slotCount();
         JS_ASSERT(regs.sp < vp);
         JS_ASSERT(vp <= regs.fp()->slots() + script->nslots);
         SetValueRangeToUndefined(regs.sp, vp);
         regs.sp = vp;
     } else if (op == JSOP_ENTERLET0) {
-        JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
+        JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() + blockObj.slotCount()
                   == regs.sp);
     } else if (op == JSOP_ENTERLET1) {
-        JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
+        JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() + blockObj.slotCount()
                   == regs.sp - 1);
     }
 
 #ifdef DEBUG
-    JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain());
+    JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock());
 
     /*
      * The young end of fp->scopeChain may omit blocks if we haven't closed
      * over them, but if there are any closure blocks on fp->scopeChain, they'd
      * better be (clones of) ancestors of the block we're entering now;
      * anything else we should have popped off fp->scopeChain when we left its
      * static scope.
      */
     JSObject *obj2 = &regs.fp()->scopeChain();
     while (obj2->isWith())
-        obj2 = obj2->internalScopeChain();
+        obj2 = &obj2->asWith().enclosingScope();
     if (obj2->isBlock() &&
         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp()))
     {
-        JSObject *youngestProto = obj2->getProto();
-        JS_ASSERT(youngestProto->isStaticBlock());
-        JSObject *parent = obj;
-        while ((parent = parent->scopeChain()) != youngestProto)
+        StaticBlockObject &youngestProto = obj2->asClonedBlock().staticBlock();
+        StaticBlockObject *parent = &blockObj;
+        while ((parent = parent->enclosingBlock()) != &youngestProto)
             JS_ASSERT(parent);
     }
 #endif
 
-    regs.fp()->setBlockChain(obj);
+    regs.fp()->setBlockChain(&blockObj);
 }
 END_CASE(JSOP_ENTERBLOCK)
 
 BEGIN_CASE(JSOP_LEAVEBLOCK)
 BEGIN_CASE(JSOP_LEAVEFORLETIN)
 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
 {
-    JS_ASSERT(regs.fp()->blockChain().isStaticBlock());
-    DebugOnly<uintN> blockDepth = OBJ_BLOCK_DEPTH(cx, &regs.fp()->blockChain());
-    JS_ASSERT(blockDepth <= StackDepth(script));
+    StaticBlockObject &blockObj = regs.fp()->blockChain();
+    JS_ASSERT(blockObj.stackDepth() <= StackDepth(script));
 
     /*
      * If we're about to leave the dynamic scope of a block that has been
      * cloned onto fp->scopeChain, clear its private data, move its locals from
      * the stack into the clone, and pop it off the chain.
      */
-    JSObject &obj = regs.fp()->scopeChain();
-    if (obj.getProto() == &regs.fp()->blockChain()) {
-        JS_ASSERT(obj.isClonedBlock());
-        if (!js_PutBlockObject(cx, JS_TRUE))
-            goto error;
-    }
-
-    regs.fp()->setBlockChain(regs.fp()->blockChain().staticBlockScopeChain());
+    JSObject &scope = regs.fp()->scopeChain();
+    if (scope.getProto() == &blockObj && !scope.asClonedBlock().put(cx, JS_TRUE))
+        goto error;
+
+    regs.fp()->setBlockChain(blockObj.enclosingBlock());
 
     if (op == JSOP_LEAVEBLOCK) {
         /* Pop the block's slots. */
         regs.sp -= GET_UINT16(regs.pc);
-        JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp);
+        JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp);
     } else if (op == JSOP_LEAVEBLOCKEXPR) {
         /* Pop the block's slots maintaining the topmost expr. */
         Value *vp = &regs.sp[-1];
         regs.sp -= GET_UINT16(regs.pc);
-        JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp - 1);
+        JS_ASSERT(regs.fp()->base() + blockObj.stackDepth() == regs.sp - 1);
         regs.sp[-1] = *vp;
     } else {
         /* Another op will pop; nothing to do here. */
         len = JSOP_LEAVEFORLETIN_LENGTH;
         DO_NEXT_OP(len);
     }
 }
 END_CASE(JSOP_LEAVEBLOCK)
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -319,23 +319,23 @@ class InterpreterFrames {
     const InterruptEnablerBase &enabler;
 };
 
 /*
  * Unwind block and scope chains to match the given depth. The function sets
  * fp->sp on return to stackDepth.
  */
 extern bool
-UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind);
+UnwindScope(JSContext *cx, uint32_t stackDepth, JSBool normalUnwind);
 
 extern bool
 OnUnknownMethod(JSContext *cx, js::Value *vp);
 
 extern bool
-IsActiveWithOrBlock(JSContext *cx, JSObject &obj, int stackDepth);
+IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth);
 
 /************************************************************************/
 
 /*
  * To really poison a set of values, using 'magic' or 'undefined' isn't good
  * enough since often these will just be ignored by buggy code (see bug 629974)
  * in debug builds and crash in release builds. Instead, we use a safe-for-crash
  * pointer.
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1273,17 +1273,17 @@ Class js::GeneratorClass = {
 JS_REQUIRES_STACK JSObject *
 js_NewGenerator(JSContext *cx)
 {
     FrameRegs &stackRegs = cx->regs();
     StackFrame *stackfp = stackRegs.fp();
     JS_ASSERT(stackfp->base() == cx->regs().sp);
     JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
 
-    GlobalObject *global = stackfp->scopeChain().getGlobal();
+    GlobalObject *global = &stackfp->scopeChain().global();
     JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
     if (!proto)
         return NULL;
     JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global);
     if (!obj)
         return NULL;
 
     /* Load and compute stack slot counts. */
@@ -1617,17 +1617,17 @@ InitStopIterationClass(JSContext *cx, Gl
     return proto;
 }
 
 JSObject *
 js_InitIteratorClasses(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     /*
      * Bail if Iterator has already been initialized.  We test for Iterator
      * rather than for StopIteration because if js_InitIteratorClasses recurs,
      * as happens when the StopIteration object is frozen, initializing the
      * Iterator class a second time will assert.
      */
     JSObject *iter;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1007,22 +1007,22 @@ FinishRuntimeNumberState(JSRuntime *rt)
 JSObject *
 js_InitNumberClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
     /* XXX must do at least once per new thread, so do it per JSContext... */
     FIX_FPU();
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *numberProto = global->createBlankPrototype(cx, &NumberClass);
     if (!numberProto)
         return NULL;
-    numberProto->asNumber()->setPrimitiveValue(0);
+    numberProto->asNumber().setPrimitiveValue(0);
 
     JSFunction *ctor = global->createConstructor(cx, Number, &NumberClass,
                                                  CLASS_ATOM(cx, Number), 1);
     if (!ctor)
         return NULL;
 
     if (!LinkConstructorAndPrototype(cx, ctor, numberProto))
         return NULL;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -943,17 +943,17 @@ obj_valueOf(JSContext *cx, uintN argc, V
     return true;
 }
 
 /* We should be able to assert this for *any* fp->scopeChain(). */
 static void
 AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
 {
 #ifdef DEBUG
-    for (JSObject *o = &scopeobj; o; o = o->scopeChain()) {
+    for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) {
         if (JSObjectOp op = o->getClass()->ext.innerObject)
             JS_ASSERT(op(cx, o) == o);
     }
 #endif
 }
 
 #ifndef EVAL_CACHE_CHAIN_LIMIT
 # define EVAL_CACHE_CHAIN_LIMIT 4
@@ -1128,17 +1128,17 @@ enum EvalType { DIRECT_EVAL = EXECUTE_DI
  */
 static bool
 EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
            JSObject &scopeobj)
 {
     JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
     AssertInnerizedScopeChain(cx, scopeobj);
 
-    if (!scopeobj.getGlobal()->isRuntimeCodeGenEnabled(cx)) {
+    if (!scopeobj.global().isRuntimeCodeGenEnabled(cx)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
         return false;
     }
 
     /* ES5 15.1.2.1 step 1. */
     if (args.length() < 1) {
         args.rval().setUndefined();
         return true;
@@ -1170,17 +1170,17 @@ EvalKernel(JSContext *cx, const CallArgs
             return false;
         thisv = caller->thisValue();
 
 #ifdef DEBUG
         jsbytecode *callerPC = caller->pcQuadratic(cx);
         JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
 #endif
     } else {
-        JS_ASSERT(args.callee().getGlobal() == &scopeobj);
+        JS_ASSERT(args.callee().global() == scopeobj);
         staticLevel = 0;
 
         /* Use the global as 'this', modulo outerization. */
         JSObject *thisobj = scopeobj.thisObject(cx);
         if (!thisobj)
             return false;
         thisv = ObjectValue(*thisobj);
     }
@@ -1300,17 +1300,17 @@ namespace js {
  *
  * NB: This method handles only indirect eval.
  */
 JSBool
 eval(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return WarnOnTooManyArgs(cx, args) &&
-           EvalKernel(cx, args, INDIRECT_EVAL, NULL, *args.callee().getGlobal());
+           EvalKernel(cx, args, INDIRECT_EVAL, NULL, args.callee().global());
 }
 
 bool
 DirectEval(JSContext *cx, const CallArgs &args)
 {
     /* Direct eval can assume it was called from an interpreted frame. */
     StackFrame *caller = cx->fp();
     JS_ASSERT(caller->isScriptFrame());
@@ -1327,17 +1327,17 @@ DirectEval(JSContext *cx, const CallArgs
         return false;
 
     return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
 }
 
 bool
 IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
 {
-    return scopeChain->getGlobal()->getOriginalEval() == v;
+    return scopeChain->global().getOriginalEval() == v;
 }
 
 bool
 IsAnyBuiltinEval(JSFunction *fun)
 {
     return fun->maybeNative() == eval;
 }
 
@@ -2607,17 +2607,17 @@ obj_create(JSContext *cx, uintN argc, Va
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
         return false;
     }
 
     /*
      * Use the callee's global as the parent of the new object to avoid dynamic
      * scoping (i.e., using the caller's global).
      */
-    JSObject *obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, vp->toObject().getGlobal());
+    JSObject *obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, &vp->toObject().global());
     if (!obj)
         return JS_FALSE;
     vp->setObject(*obj); /* Root and prepare for eventual return. */
 
     /* Don't track types or array-ness for objects created here. */
     MarkTypeObjectUnknownProperties(cx, obj->type());
 
     /* 15.2.3.5 step 4. */
@@ -2998,17 +2998,17 @@ js::NewObjectWithClassProto(JSContext *c
      * will flush the new object cache).
      */
     JSProtoKey protoKey = GetClassProtoKey(clasp);
 
     NewObjectCache &cache = cx->compartment->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (parent->isGlobal() && protoKey != JSProto_Null) {
-        if (cache.lookupGlobal(clasp, parent->asGlobal(), kind, &entry))
+        if (cache.lookupGlobal(clasp, &parent->asGlobal(), kind, &entry))
             return cache.newObjectFromHit(cx, entry);
     }
 
     RootObject parentRoot(cx, &parent);
 
     if (!FindProto(cx, clasp, parentRoot, &proto))
         return NULL;
 
@@ -3016,17 +3016,17 @@ js::NewObjectWithClassProto(JSContext *c
     if (!type)
         return NULL;
 
     JSObject *obj = NewObject(cx, clasp, type, parent, kind);
     if (!obj)
         return NULL;
 
     if (entry != -1 && !obj->hasDynamicSlots())
-        cache.fillGlobal(entry, clasp, parent->asGlobal(), kind, obj);
+        cache.fillGlobal(entry, clasp, &parent->asGlobal(), kind, obj);
 
     return obj;
 }
 
 JSObject *
 js::NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind)
 {
     JS_ASSERT(type->proto->hasNewType(type));
@@ -3271,467 +3271,16 @@ js_InferFlags(JSContext *cx, uintN defau
         if (pc < script->code + script->length && Detecting(cx, pc))
             flags |= JSRESOLVE_DETECTING;
     }
     if (format & JOF_DECLARING)
         flags |= JSRESOLVE_DECLARING;
     return flags;
 }
 
-/*
- * ObjectOps and Class for with-statement stack objects.
- */
-static JSBool
-with_LookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp)
-{
-    /* Fixes bug 463997 */
-    uintN flags = cx->resolveFlags;
-    if (flags == RESOLVE_INFER)
-        flags = js_InferFlags(cx, flags);
-    flags |= JSRESOLVE_WITH;
-    JSAutoResolveFlags rf(cx, flags);
-    return obj->getProto()->lookupGeneric(cx, id, objp, propp);
-}
-
-static JSBool
-with_LookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, JSProperty **propp)
-{
-    return with_LookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
-}
-
-static JSBool
-with_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
-                   JSProperty **propp)
-{
-    jsid id;
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return with_LookupGeneric(cx, obj, id, objp, propp);
-}
-
-static JSBool
-with_LookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp)
-{
-    return with_LookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
-}
-
-static JSBool
-with_GetGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
-{
-    return obj->getProto()->getGeneric(cx, id, vp);
-}
-
-static JSBool
-with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
-{
-    return with_GetGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp);
-}
-
-static JSBool
-with_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
-{
-    jsid id;
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return with_GetGeneric(cx, obj, receiver, id, vp);
-}
-
-static JSBool
-with_GetSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
-{
-    return with_GetGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
-}
-
-static JSBool
-with_SetGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
-{
-    return obj->getProto()->setGeneric(cx, id, vp, strict);
-}
-
-static JSBool
-with_SetProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
-{
-    return obj->getProto()->setProperty(cx, name, vp, strict);
-}
-
-static JSBool
-with_SetElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
-{
-    return obj->getProto()->setElement(cx, index, vp, strict);
-}
-
-static JSBool
-with_SetSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
-{
-    return obj->getProto()->setSpecial(cx, sid, vp, strict);
-}
-
-static JSBool
-with_GetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
-{
-    return obj->getProto()->getGenericAttributes(cx, id, attrsp);
-}
-
-static JSBool
-with_GetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp)
-{
-    return obj->getProto()->getPropertyAttributes(cx, name, attrsp);
-}
-
-static JSBool
-with_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp)
-{
-    return obj->getProto()->getElementAttributes(cx, index, attrsp);
-}
-
-static JSBool
-with_GetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp)
-{
-    return obj->getProto()->getSpecialAttributes(cx, sid, attrsp);
-}
-
-static JSBool
-with_SetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
-{
-    return obj->getProto()->setGenericAttributes(cx, id, attrsp);
-}
-
-static JSBool
-with_SetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp)
-{
-    return obj->getProto()->setPropertyAttributes(cx, name, attrsp);
-}
-
-static JSBool
-with_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp)
-{
-    return obj->getProto()->setElementAttributes(cx, index, attrsp);
-}
-
-static JSBool
-with_SetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp)
-{
-    return obj->getProto()->setSpecialAttributes(cx, sid, attrsp);
-}
-
-static JSBool
-with_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
-{
-    return obj->getProto()->deleteGeneric(cx, id, rval, strict);
-}
-
-static JSBool
-with_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
-{
-    return obj->getProto()->deleteProperty(cx, name, rval, strict);
-}
-
-static JSBool
-with_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
-{
-    return obj->getProto()->deleteElement(cx, index, rval, strict);
-}
-
-static JSBool
-with_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
-{
-    return obj->getProto()->deleteSpecial(cx, sid, rval, strict);
-}
-
-static JSBool
-with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-               Value *statep, jsid *idp)
-{
-    return obj->getProto()->enumerate(cx, enum_op, statep, idp);
-}
-
-static JSType
-with_TypeOf(JSContext *cx, JSObject *obj)
-{
-    return JSTYPE_OBJECT;
-}
-
-static JSObject *
-with_ThisObject(JSContext *cx, JSObject *obj)
-{
-    return obj->getWithThis();
-}
-
-Class js::WithClass = {
-    "With",
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(3) | JSCLASS_IS_ANONYMOUS,
-    JS_PropertyStub,         /* addProperty */
-    JS_PropertyStub,         /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    NULL,                    /* finalize */
-    NULL,                    /* reserved    */
-    NULL,                    /* checkAccess */
-    NULL,                    /* call        */
-    NULL,                    /* construct   */
-    NULL,                    /* xdrObject   */
-    NULL,                    /* hasInstance */
-    NULL,                    /* trace       */
-    JS_NULL_CLASS_EXT,
-    {
-        with_LookupGeneric,
-        with_LookupProperty,
-        with_LookupElement,
-        with_LookupSpecial,
-        NULL,             /* defineGeneric */
-        NULL,             /* defineProperty */
-        NULL,             /* defineElement */
-        NULL,             /* defineSpecial */
-        with_GetGeneric,
-        with_GetProperty,
-        with_GetElement,
-        NULL,             /* getElementIfPresent */
-        with_GetSpecial,
-        with_SetGeneric,
-        with_SetProperty,
-        with_SetElement,
-        with_SetSpecial,
-        with_GetGenericAttributes,
-        with_GetPropertyAttributes,
-        with_GetElementAttributes,
-        with_GetSpecialAttributes,
-        with_SetGenericAttributes,
-        with_SetPropertyAttributes,
-        with_SetElementAttributes,
-        with_SetSpecialAttributes,
-        with_DeleteGeneric,
-        with_DeleteProperty,
-        with_DeleteElement,
-        with_DeleteSpecial,
-        with_Enumerate,
-        with_TypeOf,
-        NULL,             /* fix   */
-        with_ThisObject,
-        NULL,             /* clear */
-    }
-};
-
-static const gc::AllocKind WITH_FINALIZE_KIND = gc::FINALIZE_OBJECT4;
-
-JS_REQUIRES_STACK JSObject *
-js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
-{
-    JSObject *obj;
-
-    RootedVarTypeObject type(cx);
-    type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-
-    StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
-
-    RootedVarShape emptyWithShape(cx);
-    emptyWithShape = EmptyShape::getInitialShape(cx, &WithClass, proto,
-                                                 parent->getGlobal(),
-                                                 WITH_FINALIZE_KIND);
-    if (!emptyWithShape)
-        return NULL;
-
-    obj = JSObject::create(cx, WITH_FINALIZE_KIND, emptyWithShape, type, NULL);
-    if (!obj)
-        return NULL;
-    OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
-
-    if (!obj->setInternalScopeChain(cx, parent))
-        return NULL;
-    obj->setPrivate(priv);
-
-    JSObject *thisp = proto->thisObject(cx);
-    if (!thisp)
-        return NULL;
-
-    assertSameCompartment(cx, obj, thisp);
-
-    obj->setWithThis(thisp);
-    return obj;
-}
-
-static const uint32_t BLOCK_RESERVED_SLOTS = 2;
-static const gc::AllocKind BLOCK_FINALIZE_KIND = gc::FINALIZE_OBJECT4;
-
-JSObject *
-js_NewBlockObject(JSContext *cx)
-{
-    RootedVarTypeObject type(cx);
-    type = cx->compartment->getEmptyType(cx);
-    if (!type)
-        return NULL;
-
-    RootedVarShape emptyBlockShape(cx);
-    emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL,
-                                                  BLOCK_FINALIZE_KIND);
-    if (!emptyBlockShape)
-        return NULL;
-
-    return JSObject::create(cx, FINALIZE_OBJECT4, emptyBlockShape, type, NULL);
-}
-
-JSObject *
-js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp)
-{
-    JS_ASSERT(proto->isStaticBlock());
-
-    RootedVarTypeObject type(cx);
-    type = proto->getNewType(cx);
-    if (!type)
-        return NULL;
-
-    HeapValue *slots;
-    if (!PreallocateObjectDynamicSlots(cx, proto->lastProperty(), &slots))
-        return NULL;
-
-    RootedVarShape shape(cx);
-    shape = proto->lastProperty();
-
-    JSObject *clone = JSObject::create(cx, BLOCK_FINALIZE_KIND,
-                                       shape, type, slots);
-    if (!clone)
-        return NULL;
-
-    /* Set the parent if necessary, as for call objects. */
-    JSObject *global = fp->scopeChain().getGlobal();
-    if (global != clone->getParent()) {
-        JS_ASSERT(clone->getParent() == NULL);
-        if (!clone->setParent(cx, global))
-            return NULL;
-    }
-
-    JS_ASSERT(!clone->inDictionaryMode());
-    JS_ASSERT(clone->isClonedBlock());
-    JS_ASSERT(clone->slotSpan() >= OBJ_BLOCK_COUNT(cx, proto) + BLOCK_RESERVED_SLOTS);
-
-    clone->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
-    clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
-
-    if (clone->lastProperty()->extensibleParents() && !clone->generateOwnShape(cx))
-        return NULL;
-
-    return clone;
-}
-
-JS_REQUIRES_STACK JSBool
-js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
-{
-    StackFrame *const fp = cx->fp();
-    JSObject *obj = &fp->scopeChain();
-    JS_ASSERT(obj->isClonedBlock());
-    JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
-
-    /* Block objects should have all reserved slots allocated early. */
-    uintN count = OBJ_BLOCK_COUNT(cx, obj);
-    JS_ASSERT(obj->slotSpan() >= JSSLOT_BLOCK_DEPTH + 1 + count);
-
-    /* The block and its locals must be on the current stack for GC safety. */
-    uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
-    JS_ASSERT(depth <= size_t(cx->regs().sp - fp->base()));
-    JS_ASSERT(count <= size_t(cx->regs().sp - fp->base() - depth));
-
-    /* See comments in CheckDestructuring in frontend/Parser.cpp. */
-    JS_ASSERT(count >= 1);
-
-    if (normalUnwind) {
-        uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
-        depth += fp->numFixed();
-        obj->copySlotRange(slot, fp->slots() + depth, count);
-    }
-
-    /* We must clear the private slot even with errors. */
-    obj->setPrivate(NULL);
-    fp->setScopeChainNoCallObj(*obj->internalScopeChain());
-    return normalUnwind;
-}
-
-static JSBool
-block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
-{
-    /*
-     * Block objects are never exposed to script, and the engine handles them
-     * with care. So unlike other getters, this one can assert (rather than
-     * check) certain invariants about obj.
-     */
-    JS_ASSERT(obj->isClonedBlock());
-    uintN index = (uintN) JSID_TO_INT(id);
-    JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
-
-    StackFrame *fp = (StackFrame *) obj->getPrivate();
-    if (fp) {
-        fp = js_LiveFrameIfGenerator(fp);
-        index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
-        JS_ASSERT(index < fp->numSlots());
-        *vp = fp->slots()[index];
-        return true;
-    }
-
-    /* Values are in slots immediately following the class-reserved ones. */
-    JS_ASSERT(obj->getSlot(JSSLOT_FREE(&BlockClass) + index) == *vp);
-    return true;
-}
-
-static JSBool
-block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
-{
-    JS_ASSERT(obj->isClonedBlock());
-    uintN index = (uintN) JSID_TO_INT(id);
-    JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
-
-    StackFrame *fp = (StackFrame *) obj->getPrivate();
-    if (fp) {
-        fp = js_LiveFrameIfGenerator(fp);
-        index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
-        JS_ASSERT(index < fp->numSlots());
-        fp->slots()[index] = *vp;
-        return true;
-    }
-
-    /*
-     * The value in *vp will be written back to the slot in obj that was
-     * allocated when this let binding was defined.
-     */
-    return true;
-}
-
-const Shape *
-JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared)
-{
-    JS_ASSERT(isStaticBlock());
-
-    *redeclared = false;
-
-    RootedVarObject self(cx, this);
-
-    /* Inline JSObject::addProperty in order to trap the redefinition case. */
-    Shape **spp;
-    Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true);
-    if (shape) {
-        *redeclared = true;
-        return NULL;
-    }
-
-    /*
-     * Don't convert this object to dictionary mode so that we can clone the
-     * block's shape later.
-     */
-    uint32_t slot = JSSLOT_FREE(&BlockClass) + index;
-    shape = self->addPropertyInternal(cx, id, block_getProperty, block_setProperty,
-                                      slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
-                                      Shape::HAS_SHORTID, index, spp,
-                                      /* allowDictionary = */ false);
-    if (!shape)
-        return NULL;
-    return shape;
-}
-
 JSBool
 JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict)
 {
     if (JS_UNLIKELY(watched())) {
         id = js_CheckForStringIndex(id);
         WatchpointMap *wpmap = cx->compartment->watchpointMap;
         if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
             return false;
@@ -4123,163 +3672,16 @@ JSObject::swap(JSContext *cx, JSObject *
     }
 
     TradeGuts(cx, this, otherClone, reservedThis);
     TradeGuts(cx, other, thisClone, reservedOther);
 
     return true;
 }
 
-#if JS_HAS_XDR
-
-#define NO_PARENT_INDEX UINT32_MAX
-
-uint32_t
-FindObjectIndex(JSObjectArray *array, JSObject *obj)
-{
-    size_t i;
-
-    if (array) {
-        i = array->length;
-        do {
-
-            if (array->vector[--i] == obj)
-                return i;
-        } while (i != 0);
-    }
-
-    return NO_PARENT_INDEX;
-}
-
-JSBool
-js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
-{
-    JSContext *cx;
-    uint32_t parentId;
-    JSObject *obj, *parent;
-    uintN depth, count;
-    uint32_t depthAndCount;
-    const Shape *shape;
-
-    cx = xdr->cx;
-#ifdef __GNUC__
-    obj = NULL;         /* quell GCC overwarning */
-#endif
-
-    if (xdr->mode == JSXDR_ENCODE) {
-        obj = *objp;
-        parent = obj->staticBlockScopeChain();
-        parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
-                   ? FindObjectIndex(xdr->script->objects(), parent)
-                   : NO_PARENT_INDEX;
-        depth = uint16_t(OBJ_BLOCK_DEPTH(cx, obj));
-        count = uint16_t(OBJ_BLOCK_COUNT(cx, obj));
-        depthAndCount = uint32_t(depth << 16) | count;
-    }
-#ifdef __GNUC__ /* suppress bogus gcc warnings */
-    else count = 0;
-#endif
-
-    /* First, XDR the parent atomid. */
-    if (!JS_XDRUint32(xdr, &parentId))
-        return JS_FALSE;
-
-    if (xdr->mode == JSXDR_DECODE) {
-        obj = js_NewBlockObject(cx);
-        if (!obj)
-            return JS_FALSE;
-        *objp = obj;
-
-        /*
-         * If there's a parent id, then get the parent out of our script's
-         * object array. We know that we XDR block object in outer-to-inner
-         * order, which means that getting the parent now will work.
-         */
-        if (parentId == NO_PARENT_INDEX)
-            parent = NULL;
-        else
-            parent = xdr->script->getObject(parentId);
-        obj->setStaticBlockScopeChain(parent);
-    }
-
-    if (!JS_XDRUint32(xdr, &depthAndCount))
-        return false;
-
-    if (xdr->mode == JSXDR_DECODE) {
-        depth = uint16_t(depthAndCount >> 16);
-        count = uint16_t(depthAndCount);
-        obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)));
-
-        /*
-         * XDR the block object's properties. We know that there are 'count'
-         * properties to XDR, stored as id/shortid pairs.
-         */
-        for (uintN i = 0; i < count; i++) {
-            JSAtom *atom;
-
-            /* XDR the real id. */
-            if (!js_XDRAtom(xdr, &atom))
-                return false;
-
-            bool redeclared;
-            if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i, &redeclared)) {
-                JS_ASSERT(!redeclared);
-                return false;
-            }
-        }
-    } else {
-        AutoShapeVector shapes(cx);
-        shapes.growBy(count);
-
-        for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
-            shape = &r.front();
-            shapes[shape->shortid()] = shape;
-        }
-
-        /*
-         * XDR the block object's properties. We know that there are 'count'
-         * properties to XDR, stored as id/shortid pairs.
-         */
-        for (uintN i = 0; i < count; i++) {
-            shape = shapes[i];
-            JS_ASSERT(shape->getter() == block_getProperty);
-
-            jsid propid = shape->propid();
-            JS_ASSERT(JSID_IS_ATOM(propid));
-            JSAtom *atom = JSID_TO_ATOM(propid);
-
-#ifdef DEBUG
-            uint16_t shortid = uint16_t(shape->shortid());
-            JS_ASSERT(shortid == i);
-#endif
-
-            /* XDR the real id. */
-            if (!js_XDRAtom(xdr, &atom))
-                return false;
-        }
-    }
-    return true;
-}
-
-#endif
-
-Class js::BlockClass = {
-    "Block",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(BLOCK_RESERVED_SLOTS) |
-    JSCLASS_IS_ANONYMOUS,
-    JS_PropertyStub,         /* addProperty */
-    JS_PropertyStub,         /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub
-};
-
 static bool
 DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
                    const Value &v, uint32_t attrs, bool &named)
 {
     jsid id = ATOM_TO_JSID(atom);
 
     if (key != JSProto_Null) {
         /*
@@ -5025,17 +4427,17 @@ SetProto(JSContext *cx, JSObject *obj, J
 }
 
 JSBool
 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
                   JSObject **objp)
 {
     RootObject objRoot(cx, &obj);
 
-    obj = obj->getGlobal();
+    obj = &obj->global();
     if (!obj->isGlobal()) {
         *objp = NULL;
         return true;
     }
 
     Value v = obj->getReservedSlot(key);
     if (v.isObject()) {
         *objp = &v.toObject();
@@ -5069,17 +4471,17 @@ js_FindClassObject(JSContext *cx, JSObje
     JSObject *cobj, *pobj;
     jsid id;
     JSProperty *prop;
     const Shape *shape;
 
     RootedVarObject obj(cx);
 
     if (start) {
-        obj = start->getGlobal();
+        obj = &start->global();
         OBJ_TO_INNER_OBJECT(cx, *obj.address());
     } else {
         obj = GetGlobalForScopeChain(cx);
     }
     if (!obj)
         return false;
 
     if (protoKey != JSProto_Null) {
@@ -5281,17 +4683,17 @@ js_PurgeScopeChainHelper(JSContext *cx, 
 
     /*
      * We must purge the scope chain only for Call objects as they are the only
      * kind of cacheable non-global object that can gain properties after outer
      * properties with the same names have been cached or traced. Call objects
      * may gain such properties via eval introducing new vars; see bug 490364.
      */
     if (obj->isCall()) {
-        while ((obj = obj->scopeChain()) != NULL) {
+        while ((obj = obj->enclosingScope()) != NULL) {
             if (!PurgeProtoChain(cx, obj, id))
                 return false;
         }
     }
 
     return true;
 }
 
@@ -5719,23 +5121,23 @@ js_FindPropertyHelper(JSContext *cx, jsi
          * Skip along the scope chain to the enclosing global object. This is
          * used for GNAME opcodes where the bytecode emitter has determined a
          * name access must be on the global. It also insulates us from bugs
          * in the emitter: type inference will assume that GNAME opcodes are
          * accessing the global object, and the inferred behavior should match
          * the actual behavior even if the id could be found on the scope chain
          * before the global object.
          */
-        scopeChain = scopeChain->getGlobal();
+        scopeChain = &scopeChain->global();
     }
 
     /* Scan entries on the scope chain that we can cache across. */
     entry = JS_NO_PROP_CACHE_FILL;
     obj = scopeChain;
-    parent = obj->scopeChain();
+    parent = obj->enclosingScope();
     for (scopeIndex = 0;
          parent
          ? IsCacheableNonGlobalScope(obj)
          : !obj->getOps()->lookupProperty;
          ++scopeIndex) {
         if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
             return NULL;
 
@@ -5771,32 +5173,32 @@ js_FindPropertyHelper(JSContext *cx, jsi
             goto out;
         }
 
         if (!parent) {
             pobj = NULL;
             goto out;
         }
         obj = parent;
-        parent = obj->scopeChain();
+        parent = obj->enclosingScope();
     }
 
     for (;;) {
         if (!obj->lookupGeneric(cx, id, &pobj, &prop))
             return NULL;
         if (prop) {
             PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
             goto out;
         }
 
         /*
          * We conservatively assume that a resolve hook could mutate the scope
          * chain during JSObject::lookupGeneric. So we read parent here again.
          */
-        parent = obj->scopeChain();
+        parent = obj->enclosingScope();
         if (!parent) {
             pobj = NULL;
             break;
         }
         obj = parent;
     }
 
   out:
@@ -5820,17 +5222,17 @@ js_FindProperty(JSContext *cx, jsid id, 
 
 JSObject *
 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
 {
     /*
      * This function should not be called for a global object or from the
      * trace and should have a valid cache entry for native scopeChain.
      */
-    JS_ASSERT(scopeChain->scopeChain() != NULL);
+    JS_ASSERT(scopeChain->enclosingScope() != NULL);
 
     JSObject *obj = scopeChain;
 
     /*
      * Loop over cacheable objects on the scope chain until we find a
      * property. We also stop when we reach the global object skipping any
      * farther checks or lookups. For details see the JSOP_BINDNAME case of
      * js_Interpret.
@@ -5845,24 +5247,24 @@ js_FindIdentifierBase(JSContext *cx, JSO
         JSProperty *prop;
         if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
             return NULL;
         if (prop) {
             if (!pobj->isNative()) {
                 JS_ASSERT(obj->isGlobal());
                 return obj;
             }
-            JS_ASSERT_IF(obj->isInternalScope(), pobj->getClass() == obj->getClass());
+            JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass());
             DebugOnly<PropertyCacheEntry*> entry =
                 JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop);
             JS_ASSERT(entry);
             return obj;
         }
 
-        JSObject *parent = obj->scopeChain();
+        JSObject *parent = obj->enclosingScope();
         if (!parent)
             return obj;
         obj = parent;
     }
 
     /* Loop until we find a property or reach the global object. */
     do {
         JSObject *pobj;
@@ -5872,17 +5274,17 @@ js_FindIdentifierBase(JSContext *cx, JSO
         if (prop)
             break;
 
         /*
          * We conservatively assume that a resolve hook could mutate the scope
          * chain during JSObject::lookupGeneric. So we must check if parent is
          * not null here even if it wasn't before the lookup.
          */
-        JSObject *parent = obj->scopeChain();
+        JSObject *parent = obj->enclosingScope();
         if (!parent)
             break;
         obj = parent;
     } while (!obj->isGlobal());
     return obj;
 }
 
 static JS_ALWAYS_INLINE JSBool
@@ -6858,17 +6260,17 @@ js_GetClassPrototype(JSContext *cx, JSOb
                      JSObject **protop, Class *clasp)
 {
     JS_ASSERT(JSProto_Null <= protoKey);
     JS_ASSERT(protoKey < JSProto_LIMIT);
 
     if (protoKey != JSProto_Null) {
         GlobalObject *global;
         if (scopeobj) {
-            global = scopeobj->getGlobal();
+            global = &scopeobj->global();
         } else {
             global = GetCurrentGlobal(cx);
             if (!global) {
                 *protop = NULL;
                 return true;
             }
         }
         const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -338,23 +338,30 @@ extern Class SlowArrayClass;
 extern Class StopIterationClass;
 extern Class StringClass;
 extern Class StrictArgumentsObjectClass;
 extern Class WeakMapClass;
 extern Class WithClass;
 extern Class XMLFilterClass;
 
 class ArgumentsObject;
+class BlockObject;
 class BooleanObject;
+class ClonedBlockObject;
+class DeclEnvObject;
 class GlobalObject;
+class NestedScopeObject;
 class NormalArgumentsObject;
 class NumberObject;
+class ScopeObject;
+class StaticBlockObject;
 class StrictArgumentsObject;
 class StringObject;
 class RegExpObject;
+class WithObject;
 
 /*
  * Header structure for object element arrays. This structure is immediately
  * followed by an array of elements, with the elements member in an object
  * pointing to the beginning of that array (the end of this structure).
  * See below for usage of this structure.
  */
 class ObjectElements
@@ -770,20 +777,18 @@ struct JSObject : js::gc::Cell
 
     inline void setSlot(uintN slot, const js::Value &value);
     inline void initSlot(uintN slot, const js::Value &value);
     inline void initSlotUnchecked(uintN slot, const js::Value &value);
 
     inline void nativeSetSlot(uintN slot, const js::Value &value);
     inline void nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value);
 
-    inline js::Value getReservedSlot(uintN index) const;
+    inline const js::Value &getReservedSlot(uintN index) const;
     inline js::HeapValue &getReservedSlotRef(uintN index);
-
-    /* Call this only after the appropriate ensure{Class,Instance}ReservedSlots call. */
     inline void setReservedSlot(uintN index, const js::Value &v);
 
     /* For slots which are known to always be fixed, due to the way they are allocated. */
 
     js::HeapValue &getFixedSlotRef(uintN slot) {
         JS_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
@@ -868,60 +873,44 @@ struct JSObject : js::gc::Cell
      *
      * All script-accessible objects with a NULL parent are global objects,
      * and all global objects have a NULL parent. Some builtin objects which
      * are not script-accessible also have a NULL parent, such as parser
      * created functions for non-compileAndGo scripts.
      *
      * Except for the non-script-accessible builtins, the global with which an
      * object is associated can be reached by following parent links to that
-     * global (see getGlobal()).
+     * global (see global()).
      *
      * The scope chain of an object is the link in the search path when a
      * script does a name lookup on a scope object. For JS internal scope
-     * objects --- Call, Block, DeclEnv and With --- the chain is stored in
+     * objects --- Call, DeclEnv and block --- the chain is stored in
      * the first fixed slot of the object, and the object's parent is the
      * associated global. For other scope objects, the chain is stored in the
      * object's parent.
      *
      * In compileAndGo code, scope chains can contain only internal scope
      * objects with a global object at the root as the scope of the outermost
      * non-function script. In non-compileAndGo code, the scope of the
      * outermost non-function script might not be a global object, and can have
      * a mix of other objects above it before the global object is reached.
      */
 
     /* Access the parent link of an object. */
     inline JSObject *getParent() const;
     bool setParent(JSContext *cx, JSObject *newParent);
 
-    /* Get the scope chain of an arbitrary scope object. */
-    inline JSObject *scopeChain() const;
-
-    inline bool isGlobal() const;
-    inline js::GlobalObject *asGlobal();
-    inline js::GlobalObject *getGlobal() const;
-
-    inline bool isInternalScope() const;
+    /*
+     * Get the enclosing scope of an object. When called on non-scope object,
+     * this will just be the global (the name "enclosing scope" still applies
+     * in this situation because non-scope objects can be on the scope chain).
+     */
+    inline JSObject *enclosingScope();
 
-    /* Access the scope chain of an internal scope object. */
-    inline JSObject *internalScopeChain() const;
-    inline bool setInternalScopeChain(JSContext *cx, JSObject *obj);
-    static inline size_t offsetOfInternalScopeChain();
-
-    /*
-     * Access the scope chain of a static block object. These do not appear
-     * on scope chains but mirror their structure, and can have a NULL
-     * scope chain.
-     */
-    inline JSObject *staticBlockScopeChain() const;
-    inline void setStaticBlockScopeChain(JSObject *obj);
-
-    /* Common fixed slot for the scope chain of internal scope objects. */
-    static const uint32_t SCOPE_CHAIN_SLOT = 0;
+    inline js::GlobalObject &global() const;
 
     /* Private data accessors. */
 
     inline bool hasPrivate() const;
     inline void *getPrivate() const;
     inline void setPrivate(void *data);
 
     /* Access private data for an object with a known number of fixed slots. */
@@ -976,22 +965,16 @@ struct JSObject : js::gc::Cell
     inline const js::Value &getPrimitiveThis() const;
     inline void setPrimitiveThis(const js::Value &pthis);
 
     static size_t getPrimitiveThisOffset() {
         /* All primitive objects have their value in a fixed slot. */
         return getFixedSlotOffset(JSSLOT_PRIMITIVE_THIS);
     }
 
-  public:
-    inline js::BooleanObject *asBoolean();
-    inline js::NumberObject *asNumber();
-    inline js::StringObject *asString();
-    inline js::RegExpObject *asRegExp();
-
     /* Accessors for elements. */
 
     js::ObjectElements *getElementsHeader() const {
         return js::ObjectElements::fromElements(elements);
     }
 
     inline bool ensureElements(JSContext *cx, uintN cap);
     bool growElements(JSContext *cx, uintN cap);
@@ -1075,24 +1058,16 @@ struct JSObject : js::gc::Cell
     bool arrayGetOwnDataElement(JSContext *cx, size_t i, js::Value *vp);
 
   public:
     bool allocateArrayBufferSlots(JSContext *cx, uint32_t size);
     inline uint32_t arrayBufferByteLength();
     inline uint8_t * arrayBufferDataOffset();
 
   public:
-    inline js::ArgumentsObject *asArguments();
-    inline js::NormalArgumentsObject *asNormalArguments();
-    inline js::StrictArgumentsObject *asStrictArguments();
-
-  public:
-    inline js::CallObject &asCall();
-
-  public:
     /*
      * Date-specific getters and setters.
      */
 
     static const uint32_t JSSLOT_DATE_UTC_TIME = 0;
 
     /*
      * Cached slots holding local properties of the date.
@@ -1174,22 +1149,16 @@ struct JSObject : js::gc::Cell
     inline void setQNameLocalName(JSAtom *name);
 
     /*
      * Proxy-specific getters and setters.
      */
     inline js::Wrapper *getWrapperHandler() const;
 
     /*
-     * With object-specific getters and setters.
-     */
-    inline JSObject *getWithThis() const;
-    inline void setWithThis(JSObject *thisp);
-
-    /*
      * Back to generic stuff.
      */
     inline bool isCallable();
 
     inline void finish(JSContext *cx);
     JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
 
     inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0);
@@ -1215,29 +1184,31 @@ struct JSObject : js::gc::Cell
      * callable a TypeError will be thrown. On success the value returned by
      * the call is stored in *vp.
      */
     bool callMethod(JSContext *cx, jsid id, uintN argc, js::Value *argv, js::Value *vp);
 
   private:
     js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::StackShape &child);
 
+  protected:
     /*
      * Internal helper that adds a shape not yet mapped by this object.
      *
      * Notes:
      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
      * 2. !isExtensible() checking must be done by callers.
      */
     js::Shape *addPropertyInternal(JSContext *cx, jsid id,
                                    JSPropertyOp getter, JSStrictPropertyOp setter,
                                    uint32_t slot, uintN attrs,
                                    uintN flags, intN shortid, js::Shape **spp,
                                    bool allowDictionary);
 
+  private:
     bool toDictionaryMode(JSContext *cx);
 
     struct TradeGutsReserved;
     static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                                     TradeGutsReserved &reserved);
 
     static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                           TradeGutsReserved &reserved);
@@ -1340,63 +1311,116 @@ struct JSObject : js::gc::Cell
     inline JSObject *thisObject(JSContext *cx);
 
     static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp);
 
     inline JSObject *getThrowTypeError() const;
 
     bool swap(JSContext *cx, JSObject *other);
 
-    const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared);
-
-    inline bool isArguments() const;
-    inline bool isArrayBuffer() const;
-    inline bool isNormalArguments() const;
-    inline bool isStrictArguments() const;
-    inline bool isArray() const;
-    inline bool isDenseArray() const;
-    inline bool isSlowArray() const;
-    inline bool isNumber() const;
-    inline bool isBoolean() const;
-    inline bool isString() const;
-    inline bool isPrimitive() const;
-    inline bool isDate() const;
-    inline bool isFunction() const;
-    inline bool isObject() const;
-    inline bool isWith() const;
-    inline bool isBlock() const;
-    inline bool isStaticBlock() const;
-    inline bool isClonedBlock() const;
-    inline bool isCall() const;
-    inline bool isDeclEnv() const;
-    inline bool isRegExp() const;
-    inline bool isScript() const;
-    inline bool isGenerator() const;
-    inline bool isIterator() const;
-    inline bool isStopIteration() const;
-    inline bool isError() const;
-    inline bool isXML() const;
-    inline bool isNamespace() const;
-    inline bool isWeakMap() const;
-    inline bool isFunctionProxy() const;
-    inline bool isProxy() const;
-
-    inline bool isXMLId() const;
-    inline bool isQName() const;
-
-    inline bool isWrapper() const;
-    inline bool isCrossCompartmentWrapper() const;
-
     inline void initArrayClass();
 
     static inline void writeBarrierPre(JSObject *obj);
     static inline void writeBarrierPost(JSObject *obj, void *addr);
     inline void privateWriteBarrierPre(void **oldval);
     inline void privateWriteBarrierPost(void **oldval);
 
+    /*
+     * In addition to the generic object interface provided by JSObject,
+     * specific types of objects may provide additional operations. To access,
+     * these addition operations, callers should use the pattern:
+     *
+     *   if (obj.isX()) {
+     *     XObject &x = obj.asX();
+     *     x.foo();
+     *   }
+     *
+     * These XObject classes form a hierarchy. For example, for a cloned block
+     * object, the following predicates are true: isClonedBlock, isBlock,
+     * isNestedScope and isScope. Each of these has a respective class that
+     * derives and adds operations.
+     *
+     * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file
+     * triplet (along with any class YObject that derives XObject).
+     *
+     * Note that X represents a low-level representation and does not query the
+     * [[Class]] property of object defined by the spec (for this, see
+     * js::ObjectClassIs).
+     *
+     * SpiderMonkey has not been completely switched to the isX/asX/XObject
+     * pattern so in some cases there is no XObject class and the engine
+     * instead pokes directly at reserved slots and getPrivate. In such cases,
+     * consider adding the missing XObject class.
+     */
+
+    /* Direct subtypes of JSObject: */
+    inline bool isArguments() const;
+    inline bool isArrayBuffer() const;
+    inline bool isArray() const;
+    inline bool isDate() const;
+    inline bool isDenseArray() const;
+    inline bool isError() const;
+    inline bool isFunction() const;
+    inline bool isGenerator() const;
+    inline bool isGlobal() const;
+    inline bool isIterator() const;
+    inline bool isNamespace() const;
+    inline bool isObject() const;
+    inline bool isQName() const;
+    inline bool isPrimitive() const;
+    inline bool isProxy() const;
+    inline bool isRegExp() const;
+    inline bool isScope() const;
+    inline bool isScript() const;
+    inline bool isSlowArray() const;
+    inline bool isStopIteration() const;
+    inline bool isWeakMap() const;
+    inline bool isXML() const;
+    inline bool isXMLId() const;
+
+    /* Subtypes of ScopeObject. */
+    inline bool isBlock() const;
+    inline bool isCall() const;
+    inline bool isDeclEnv() const;
+    inline bool isNestedScope() const;
+    inline bool isWith() const;
+    inline bool isClonedBlock() const;
+    inline bool isStaticBlock() const;
+
+    /* Subtypes of PrimitiveObject. */
+    inline bool isBoolean() const;
+    inline bool isNumber() const;
+    inline bool isString() const;
+
+    /* Subtypes of ArgumentsObject. */
+    inline bool isNormalArguments() const;
+    inline bool isStrictArguments() const;
+
+    /* Subtypes of Proxy. */
+    inline bool isWrapper() const;
+    inline bool isFunctionProxy() const;
+    inline bool isCrossCompartmentWrapper() const;
+
+    inline js::ArgumentsObject &asArguments();
+    inline js::BlockObject &asBlock();
+    inline js::BooleanObject &asBoolean();
+    inline js::CallObject &asCall();
+    inline js::ClonedBlockObject &asClonedBlock();
+    inline js::DeclEnvObject &asDeclEnv();
+    inline js::GlobalObject &asGlobal();
+    inline js::NestedScopeObject &asNestedScope();
+    inline js::NormalArgumentsObject &asNormalArguments();
+    inline js::NumberObject &asNumber();
+    inline js::RegExpObject &asRegExp();
+    inline js::ScopeObject &asScope();
+    inline js::StrictArgumentsObject &asStrictArguments();
+    inline js::StaticBlockObject &asStaticBlock();
+    inline js::StringObject &asString();
+    inline js::WithObject &asWith();
+
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
 
   private:
     static void staticAsserts() {
         /* Check alignment for any fixed slots allocated after the object. */
         JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0);
 
         JS_STATIC_ASSERT(offsetof(JSObject, shape_) == offsetof(js::shadow::Object, shape));
@@ -1479,76 +1503,16 @@ class JSValueArray {
 class ValueArray {
   public:
     js::Value *array;
     size_t length;
 
     ValueArray(js::Value *v, size_t c) : array(v), length(c) {}
 };
 
-/*
- * Block scope object macros.  The slots reserved by BlockClass are:
- *
- *   private              StackFrame *      active frame pointer or null
- *   JSSLOT_SCOPE_CHAIN   JSObject *        scope chain, as for other scopes
- *   JSSLOT_BLOCK_DEPTH   int               depth of block slots in frame
- *
- * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals.
- *
- * A With object is like a Block object, in that both have a reserved slot
- * telling the stack depth of the relevant slots (the slot whose value is the
- * object named in the with statement, the slots containing the block's local
- * variables); and both have a private slot referring to the StackFrame in
- * whose activation they were created (or null if the with or block object
- * outlives the frame).
- */
-static const uint32_t JSSLOT_BLOCK_DEPTH = 1;
-static const uint32_t JSSLOT_BLOCK_FIRST_FREE_SLOT = JSSLOT_BLOCK_DEPTH + 1;
-
-static const uint32_t JSSLOT_WITH_THIS = 2;
-
-#define OBJ_BLOCK_COUNT(cx,obj)                                               \
-    (obj)->propertyCount()
-#define OBJ_BLOCK_DEPTH(cx,obj)                                               \
-    (obj)->getFixedSlot(JSSLOT_BLOCK_DEPTH).toInt32()
-#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth)                                     \
-    (obj)->setFixedSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)))
-
-/*
- * To make sure this slot is well-defined, always call js_NewWithObject to
- * create a With object, don't call js_NewObject directly.  When creating a
- * With object that does not correspond to a stack slot, pass -1 for depth.
- *
- * When popping the stack across this object's "with" statement, client code
- * must call withobj->setPrivate(NULL).
- */
-extern JS_REQUIRES_STACK JSObject *
-js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth);
-
-inline JSObject *
-js_UnwrapWithObject(JSContext *cx, JSObject *withobj);
-
-/*
- * Create a new block scope object not linked to any proto or parent object.
- * Blocks are created by the compiler to reify let blocks and comprehensions.
- * Only when dynamic scope is captured do they need to be cloned and spliced
- * into an active scope chain.
- */
-extern JSObject *
-js_NewBlockObject(JSContext *cx);
-
-extern JSObject *
-js_CloneBlockObject(JSContext *cx, JSObject *proto, js::StackFrame *fp);
-
-extern JS_REQUIRES_STACK JSBool
-js_PutBlockObject(JSContext *cx, JSBool normalUnwind);
-
-JSBool
-js_XDRBlockObject(JSXDRState *xdr, JSObject **objp);
-
 /* For manipulating JSContext::sharpObjectMap. */
 #define SHARP_BIT       ((jsatomid) 1)
 #define BUSY_BIT        ((jsatomid) 2)
 #define SHARP_ID_SHIFT  2
 #define IS_SHARP(he)    (uintptr_t((he)->value) & SHARP_BIT)
 #define MAKE_SHARP(he)  ((he)->value = (void *) (uintptr_t((he)->value)|SHARP_BIT))
 #define IS_BUSY(he)     (uintptr_t((he)->value) & BUSY_BIT)
 #define MAKE_BUSY(he)   ((he)->value = (void *) (uintptr_t((he)->value)|BUSY_BIT))
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -39,47 +39,44 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsobjinlines_h___
 #define jsobjinlines_h___
 
 #include <new>
 
 #include "jsarray.h"
+#include "jsbool.h"
+#include "jscntxt.h"
 #include "jsdate.h"
 #include "jsfun.h"
 #include "jsgcmark.h"
 #include "jsiter.h"
 #include "jslock.h"
+#include "jsnum.h"
 #include "jsobj.h"
 #include "jsprobes.h"
 #include "jspropertytree.h"
 #include "jsproxy.h"
 #include "jsscope.h"
+#include "jsstr.h"
 #include "jstypedarray.h"
 #include "jsxml.h"
 #include "jswrapper.h"
 
-/* Headers included for inline implementations used by this header. */
-#include "jsbool.h"
-#include "jscntxt.h"
-#include "jsnum.h"
-#include "jsinferinlines.h"
-#include "jsscopeinlines.h"
-#include "jsscriptinlines.h"
-#include "jsstr.h"
-
 #include "gc/Barrier.h"
 #include "js/TemplateLib.h"
 #include "vm/GlobalObject.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsgcinlines.h"
+#include "jsinferinlines.h"
 #include "jsscopeinlines.h"
+#include "jsscriptinlines.h"
 
 #include "gc/Barrier-inl.h"
 #include "vm/String-inl.h"
 
 inline bool
 JSObject::hasPrivate() const
 {
     return getClass()->hasPrivate();
@@ -283,63 +280,20 @@ JSObject::finalize(JSContext *cx, bool b
 }
 
 inline JSObject *
 JSObject::getParent() const
 {
     return lastProperty()->getObjectParent();
 }
 
-inline bool
-JSObject::isInternalScope() const
-{
-    return isCall() || isDeclEnv() || isBlock() || isWith();
-}
-
 inline JSObject *
-JSObject::internalScopeChain() const
-{
-    JS_ASSERT(isInternalScope());
-    return &getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
-}
-
-inline bool
-JSObject::setInternalScopeChain(JSContext *cx, JSObject *obj)
+JSObject::enclosingScope()
 {
-    JS_ASSERT(isInternalScope());
-    if (!obj->setDelegate(cx))
-        return false;
-    setFixedSlot(SCOPE_CHAIN_SLOT, JS::ObjectValue(*obj));
-    return true;
-}
-
-/*static*/ inline size_t
-JSObject::offsetOfInternalScopeChain()
-{
-    return getFixedSlotOffset(SCOPE_CHAIN_SLOT);
-}
-
-inline JSObject *
-JSObject::scopeChain() const
-{
-    return isInternalScope() ? internalScopeChain() : getParent();
-}
-
-inline JSObject *
-JSObject::staticBlockScopeChain() const
-{
-    JS_ASSERT(isStaticBlock());
-    return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
-}
-
-inline void
-JSObject::setStaticBlockScopeChain(JSObject *obj)
-{
-    JS_ASSERT(isStaticBlock());
-    setFixedSlot(SCOPE_CHAIN_SLOT, JS::ObjectOrNullValue(obj));
+    return isScope() ? &asScope().enclosingScope() : getParent();
 }
 
 /*
  * Property read barrier for deferred cloning of compiler-created function
  * objects optimized as typically non-escaping, ad-hoc methods in obj.
  */
 inline js::Shape *
 JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp)
@@ -453,17 +407,17 @@ JSObject::canRemoveLastProperty()
 
 inline const js::HeapValue *
 JSObject::getRawSlots()
 {
     JS_ASSERT(isGlobal());
     return slots;
 }
 
-inline js::Value
+inline const js::Value &
 JSObject::getReservedSlot(uintN index) const
 {
     JS_ASSERT(index < JSSLOT_FREE(getClass()));
     return getSlot(index);
 }
 
 inline js::HeapValue &
 JSObject::getReservedSlotRef(uintN index)
@@ -800,28 +754,16 @@ JSObject::getQNameLocalNameVal() const
 
 inline void
 JSObject::setQNameLocalName(JSAtom *name)
 {
     JS_ASSERT(isQName());
     setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue());
 }
 
-inline JSObject *
-JSObject::getWithThis() const
-{
-    return &getFixedSlot(JSSLOT_WITH_THIS).toObject();
-}
-
-inline void
-JSObject::setWithThis(JSObject *thisp)
-{
-    setFixedSlot(JSSLOT_WITH_THIS, js::ObjectValue(*thisp));
-}
-
 inline bool
 JSObject::setSingletonType(JSContext *cx)
 {
     if (!cx->typeInferenceEnabled())
         return true;
 
     JS_ASSERT(!lastProperty()->previous());
     JS_ASSERT(!hasLazyType());
@@ -942,41 +884,42 @@ inline bool JSObject::watched() const
 
 inline bool JSObject::hasSpecialEquality() const
 {
     return !!getClass()->ext.equality;
 }
 
 inline bool JSObject::isArguments() const { return isNormalArguments() || isStrictArguments(); }
 inline bool JSObject::isArrayBuffer() const { return hasClass(&js::ArrayBufferClass); }
-inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); }
-inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); }
-inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); }
+inline bool JSObject::isBlock() const { return hasClass(&js::BlockClass); }
 inline bool JSObject::isBoolean() const { return hasClass(&js::BooleanClass); }
-inline bool JSObject::isString() const { return hasClass(&js::StringClass); }
-inline bool JSObject::isPrimitive() const { return isNumber() || isString() || isBoolean(); }
+inline bool JSObject::isCall() const { return hasClass(&js::CallClass); }
+inline bool JSObject::isClonedBlock() const { return isBlock() && !!getProto(); }
 inline bool JSObject::isDate() const { return hasClass(&js::DateClass); }
+inline bool JSObject::isDeclEnv() const { return hasClass(&js::DeclEnvClass); }
+inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); }
 inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); }
-inline bool JSObject::isObject() const { return hasClass(&js::ObjectClass); }
-inline bool JSObject::isWith() const { return hasClass(&js::WithClass); }
-inline bool JSObject::isBlock() const { return hasClass(&js::BlockClass); }
-inline bool JSObject::isStaticBlock() const { return isBlock() && !getProto(); }
-inline bool JSObject::isClonedBlock() const { return isBlock() && !!getProto(); }
-inline bool JSObject::isCall() const { return hasClass(&js::CallClass); }
-inline bool JSObject::isDeclEnv() const { return hasClass(&js::DeclEnvClass); }
-inline bool JSObject::isRegExp() const { return hasClass(&js::RegExpClass); }
-inline bool JSObject::isScript() const { return hasClass(&js::ScriptClass); }
+inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); }
 inline bool JSObject::isGenerator() const { return hasClass(&js::GeneratorClass); }
 inline bool JSObject::isIterator() const { return hasClass(&js::IteratorClass); }
+inline bool JSObject::isNamespace() const { return hasClass(&js::NamespaceClass); }
+inline bool JSObject::isNestedScope() const { return isBlock() || isWith(); }
+inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); }
+inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); }
+inline bool JSObject::isObject() const { return hasClass(&js::ObjectClass); }
+inline bool JSObject::isPrimitive() const { return isNumber() || isString() || isBoolean(); }
+inline bool JSObject::isRegExp() const { return hasClass(&js::RegExpClass); }
+inline bool JSObject::isScope() const { return isCall() || isDeclEnv() || isNestedScope(); }
+inline bool JSObject::isStaticBlock() const { return isBlock() && !getProto(); }
 inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); }
-inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); }
+inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); }
+inline bool JSObject::isString() const { return hasClass(&js::StringClass); }
+inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); }
+inline bool JSObject::isWith() const { return hasClass(&js::WithClass); }
 inline bool JSObject::isXML() const { return hasClass(&js::XMLClass); }
-inline bool JSObject::isNamespace() const { return hasClass(&js::NamespaceClass); }
-inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); }
-inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); }
 
 inline bool JSObject::isArray() const
 {
     return isSlowArray() || isDenseArray();
 }
 
 inline bool JSObject::isDenseArray() const
 {
@@ -1427,38 +1370,31 @@ JSObject::isCrossCompartmentWrapper() co
 }
 
 inline bool
 JSObject::isWrapper() const
 {
     return js::IsWrapper(this);
 }
 
-inline js::GlobalObject *
-JSObject::getGlobal() const
+inline js::GlobalObject &
+JSObject::global() const
 {
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
     return obj->asGlobal();
 }
 
 static inline bool
 js_IsCallable(const js::Value &v)
 {
     return v.isObject() && v.toObject().isCallable();
 }
 
-inline JSObject *
-js_UnwrapWithObject(JSContext *cx, JSObject *withobj)
-{
-    JS_ASSERT(withobj->isWith());
-    return withobj->getProto();
-}
-
 namespace js {
 
 inline void
 OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj)
 {
     if (JSObjectOp op = obj->getClass()->ext.innerObject)
         obj = op(cx, obj);
 }
@@ -1768,17 +1704,17 @@ NewBuiltinClassInstance(JSContext *cx, C
     gc::AllocKind kind = gc::GetGCObjectKind(clasp);
     return NewBuiltinClassInstance(cx, clasp, kind);
 }
 
 inline GlobalObject *
 GetCurrentGlobal(JSContext *cx)
 {
     JSObject *scopeChain = (cx->hasfp()) ? &cx->fp()->scopeChain() : cx->globalObject;
-    return scopeChain ? scopeChain->getGlobal() : NULL;
+    return scopeChain ? &scopeChain->global() : NULL;
 }
 
 bool
 FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
                    Class *clasp);
 
 /*
  * Create a plain object with the specified type. This bypasses getNewType to
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -223,17 +223,17 @@ static uint32_t
 NumBlockSlots(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
 
     JSObject *obj = NULL;
     GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj);
-    return OBJ_BLOCK_COUNT(NULL, obj);
+    return obj->asStaticBlock().slotCount();
 }
 
 uintN
 js::StackUses(JSScript *script, jsbytecode *pc)
 {
     JSOp op = (JSOp) *pc;
     const JSCodeSpec &cs = js_CodeSpec[op];
     if (cs.nuses >= 0)
@@ -489,20 +489,18 @@ ToDisassemblySource(JSContext *cx, jsval
         if (!source)
             return false;
         bytes->initBytes(source);
         return true;
     }
 
     if (!JSVAL_IS_PRIMITIVE(v)) {
         JSObject *obj = JSVAL_TO_OBJECT(v);
-        Class *clasp = obj->getClass();
-
-        if (clasp == &BlockClass) {
-            char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
+        if (obj->isBlock()) {
+            char *source = JS_sprintf_append(NULL, "depth %d {", obj->asBlock().stackDepth());
             if (!source)
                 return false;
 
             Shape::Range r = obj->lastProperty()->all();
             while (!r.empty()) {
                 const Shape &shape = r.front();
                 JSAtom *atom = JSID_IS_INT(shape.propid())
                                ? cx->runtime->atomState.emptyAtom
@@ -522,25 +520,25 @@ ToDisassemblySource(JSContext *cx, jsval
 
             source = JS_sprintf_append(source, "}");
             if (!source)
                 return false;
             bytes->initBytes(source);
             return true;
         }
 
-        if (clasp == &FunctionClass) {
+        if (obj->isFunction()) {
             JSString *str = JS_DecompileFunction(cx, obj->toFunction(), JS_DONT_PRETTY_PRINT);
             if (!str)
                 return false;
             return bytes->encode(cx, str);
         }
 
-        if (clasp == &RegExpClass) {
-            JSString *source = obj->asRegExp()->toString(cx);
+        if (obj->isRegExp()) {
+            JSString *source = obj->asRegExp().toString(cx);
             if (!source)
                 return false;
             JS::Anchor<JSString *> anchor(source);
             return bytes->encode(cx, source);
         }
     }
 
     return !!js_ValueToPrintable(cx, v, bytes, true);
@@ -1753,33 +1751,31 @@ GetLocal(SprintStack *ss, jsint i)
         JS_ASSERT(ss->printer->script->code <= pc);
         JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length));
 
         if (JSOP_ENTERBLOCK == (JSOp)*pc) {
             jsatomid j = js_GetIndexFromBytecode(ss->printer->script, pc, 0);
             JSObject *obj = script->getObject(j);
 
             if (obj->isBlock()) {
-                jsint depth = OBJ_BLOCK_DEPTH(cx, obj);
-                jsint count = OBJ_BLOCK_COUNT(cx, obj);
-
+                uint32_t depth = obj->asBlock().stackDepth();
+                uint32_t count = obj->asBlock().slotCount();
                 if (jsuint(i - depth) < jsuint(count))
                     return GetLocalInSlot(ss, i, jsint(i - depth), obj);
             }
         }
     }
 
     // Iterate over all objects.
     for (jsatomid j = 0, n = script->objects()->length; j != n; j++) {
         JSObject *obj = script->getObject(j);
 
         if (obj->isBlock()) {
-            jsint depth = OBJ_BLOCK_DEPTH(cx, obj);
-            jsint count = OBJ_BLOCK_COUNT(cx, obj);
-
+            uint32_t depth = obj->asBlock().stackDepth();
+            uint32_t count = obj->asBlock().slotCount();
             if (jsuint(i - depth) < jsuint(count))
                 return GetLocalInSlot(ss, i, jsint(i - depth), obj);
         }
     }
 
     return GetStr(ss, i);
 }
 
@@ -2247,25 +2243,25 @@ DecompileGroupAssignment(SprintStack *ss
 
 /*
  * The names of the vars of a let block/expr are stored as the ids of the
  * shapes of the block object. Shapes are stored in a singly-linked list in
  * reverse order of addition. This function takes care of putting the names
  * back in declaration order.
  */
 static bool
-GetBlockNames(JSContext *cx, JSObject *blockObj, AtomVector *atoms)
+GetBlockNames(JSContext *cx, StaticBlockObject &blockObj, AtomVector *atoms)
 {
-    size_t numAtoms = OBJ_BLOCK_COUNT(cx, blockObj);
+    size_t numAtoms = blockObj.slotCount();
     LOCAL_ASSERT(numAtoms > 0);
     if (!atoms->resize(numAtoms))
         return false;
 
     uintN i = numAtoms;
-    for (Shape::Range r = blockObj->lastProperty()->all(); !r.empty(); r.popFront()) {
+    for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
         LOCAL_ASSERT(shape.hasShortID());
         --i;
         LOCAL_ASSERT((uintN)shape.shortid() == i);
         (*atoms)[i] = JSID_IS_INT(shape.propid())
                       ? cx->runtime->atomState.emptyAtom
                       : JSID_TO_ATOM(shape.propid());
     }
@@ -3183,17 +3179,19 @@ Decompile(SprintStack *ss, jsbytecode *p
                 jp->indent -= 4;
                 js_printf(jp, "\t}\n");
                 break;
 
               case JSOP_ENTERBLOCK:
               {
                 LOAD_OBJECT(0);
                 AtomVector atoms(cx);
-                if (!GetBlockNames(cx, obj, &atoms) || !PushBlockNames(cx, ss, atoms))
+                StaticBlockObject &blockObj = obj->asStaticBlock();
+
+                if (!GetBlockNames(cx, blockObj, &atoms) || !PushBlockNames(cx, ss, atoms))
                     return NULL;
 
                 sn = js_GetSrcNote(jp->script, pc);
                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
 #if JS_HAS_BLOCK_SCOPE
                   case SRC_BRACE:
                     js_printf(jp, "\t{\n");
                     jp->indent += 4;
@@ -3241,17 +3239,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                         LOCAL_ASSERT(*pc == JSOP_POP);
                         pc += JSOP_POP_LENGTH;
                         lval = PopStr(ss, JSOP_NOP);
                         js_puts(jp, lval);
                     } else {
 #endif
                         LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP);
                         pc += JSOP_SETLOCALPOP_LENGTH;
-                        LOCAL_ASSERT(OBJ_BLOCK_COUNT(cx, obj) >= 1);
+                        LOCAL_ASSERT(blockObj.slotCount() >= 1);
                         if (!QuoteString(&jp->sprinter, atoms[0], 0))
                             return NULL;
 #if JS_HAS_DESTRUCTURING
                     }
 #endif
 
                     /*
                      * Pop the exception_cookie (or its dup in the case of a
@@ -3323,28 +3321,29 @@ Decompile(SprintStack *ss, jsbytecode *p
                 if (op == JSOP_LEAVEBLOCKEXPR)
                     todo = SprintCString(&ss->sprinter, rval);
                 break;
               }
 
               case JSOP_ENTERLET0:
               {
                 LOAD_OBJECT(0);
+                StaticBlockObject &blockObj = obj->asStaticBlock();
 
                 AtomVector atoms(cx);
-                if (!GetBlockNames(cx, obj, &atoms))
+                if (!GetBlockNames(cx, blockObj, &atoms))
                     return NULL;
 
                 sn = js_GetSrcNote(jp->script, pc);
                 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DECL);
                 ptrdiff_t letData = js_GetSrcNoteOffset(sn, 0);
                 bool groupAssign = LetDataToGroupAssign(letData);
-                uintN letDepth = OBJ_BLOCK_DEPTH(cx, obj);
-                LOCAL_ASSERT(letDepth == (uintN)ss->top - OBJ_BLOCK_COUNT(cx, obj));
-                LOCAL_ASSERT(atoms.length() == OBJ_BLOCK_COUNT(cx, obj));
+                uintN letDepth = blockObj.stackDepth();
+                LOCAL_ASSERT(letDepth == (uintN)ss->top - blockObj.slotCount());
+                LOCAL_ASSERT(atoms.length() == blockObj.slotCount());
 
                 /*
                  * Build the list of decompiled rhs expressions. Do this before
                  * sprinting the let-head since GetStr can inject stuff on top
                  * of the stack (in case js_DecompileValueGenerator).
                  */
                 Vector<const char *> rhsExprs(cx);
                 if (!rhsExprs.resize(atoms.length()))
@@ -3444,23 +3443,24 @@ Decompile(SprintStack *ss, jsbytecode *p
                *    with switch.
                * Hence, the only thing to do is update the let vars' slots with
                * their names, taking care to preserve the iter/condition value
                * on top of the stack.
                */
               case JSOP_ENTERLET1:
               {
                 LOAD_OBJECT(0);
+                StaticBlockObject &blockObj = obj->asStaticBlock();
 
                 AtomVector atoms(cx);
-                if (!GetBlockNames(cx, obj, &atoms))
+                if (!GetBlockNames(cx, blockObj, &atoms))
                     return NULL;
 
                 LOCAL_ASSERT(js_GetSrcNote(jp->script, pc) == NULL);
-                LOCAL_ASSERT(ss->top - 1 == OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj));
+                LOCAL_ASSERT(ss->top - 1 == blockObj.stackDepth() + blockObj.slotCount());
                 jsbytecode *nextpc = pc + JSOP_ENTERLET1_LENGTH;
                 if (*nextpc == JSOP_GOTO || *nextpc == JSOP_GOTOX) {
                     LOCAL_ASSERT(SN_TYPE(js_GetSrcNote(jp->script, nextpc)) == SRC_FOR_IN);
                 } else {
                     LOCAL_ASSERT(*nextpc == JSOP_CONDSWITCH ||
                                  *nextpc == JSOP_TABLESWITCH || *nextpc == JSOP_TABLESWITCHX ||
                                  *nextpc == JSOP_LOOKUPSWITCH || *nextpc == JSOP_LOOKUPSWITCHX);
                 }
@@ -4121,23 +4121,24 @@ Decompile(SprintStack *ss, jsbytecode *p
                         len = 0;
                     } else {
                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCTLET);
 
                         ptrdiff_t offsetToLet = js_GetSrcNoteOffset(sn, 0);
                         LOCAL_ASSERT(*(pc + offsetToLet) == JSOP_ENTERLET0);
 
                         GET_OBJECT_FROM_BYTECODE(jp->script, pc + offsetToLet, 0, obj);
-
-                        uint32_t blockDepth = OBJ_BLOCK_DEPTH(cx, obj);
+                        StaticBlockObject &blockObj = obj->asStaticBlock();
+
+                        uint32_t blockDepth = blockObj.stackDepth();
                         LOCAL_ASSERT(blockDepth < ss->top);
-                        LOCAL_ASSERT(ss->top <= blockDepth + OBJ_BLOCK_COUNT(cx, obj));
+                        LOCAL_ASSERT(ss->top <= blockDepth + blockObj.slotCount());
 
                         AtomVector atoms(cx);
-                        if (!GetBlockNames(cx, obj, &atoms))
+                        if (!GetBlockNames(cx, blockObj, &atoms))
                             return NULL;
 
                         /*
                          * Skip any initializers preceding this one. E.g., in
                          *   let (w=1, x=2, [y,z] = a) { ... }
                          * skip 'w' and 'x' for the JSOP_DUP of '[y,z] = a'.
                          */
                         AtomRange letNames = atoms.all();
@@ -4818,17 +4819,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 LOAD_OBJECT(0);
                 str = js_ValueToSource(cx, ObjectValue(*obj));
                 if (!str)
                     return NULL;
                 goto sprint_string;
 
               case JSOP_REGEXP:
                 GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj);
-                str = obj->asRegExp()->toString(cx);
+                str = obj->asRegExp().toString(cx);
                 if (!str)
                     return NULL;
                 goto sprint_string;
 
               case JSOP_TABLESWITCH:
               case JSOP_TABLESWITCHX:
               {
                 ptrdiff_t jmplen, off, off2;
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -72,17 +72,17 @@ PropertyCache::fill(JSContext *cx, JSObj
      * before any running script might consult a parent-linked scope chain. If
      * this requirement is not satisfied, the fill in progress will never hit,
      * but scope shape tests ensure nothing malfunctions.
      */
     JS_ASSERT_IF(obj == pobj, scopeIndex == 0);
 
     JSObject *tmp = obj;
     for (uintN i = 0; i < scopeIndex; i++)
-        tmp = tmp->internalScopeChain();
+        tmp = &tmp->asScope().enclosingScope();
 
     uintN protoIndex = 0;
     while (tmp != pobj) {
         /*
          * Don't cache entries across prototype lookups which can mutate in
          * arbitrary ways without a shape change.
          */
         if (tmp->hasUncacheableProto()) {
@@ -226,17 +226,17 @@ PropertyCache::fullTest(JSContext *cx, j
      * PropertyCache::test handles only the direct and immediate-prototype hit
      * cases. All others go here.
      */
     pobj = obj;
 
     if (JOF_MODE(cs.format) == JOF_NAME) {
         uint8_t scopeIndex = entry->scopeIndex;
         while (scopeIndex > 0) {
-            tmp = pobj->scopeChain();
+            tmp = pobj->enclosingScope();
             if (!tmp || !tmp->isNative())
                 break;
             pobj = tmp;
             scopeIndex--;
         }
 
         *objp = pobj;
     }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1534,17 +1534,17 @@ proxy_createFunction(JSContext *cx, uint
                              "createFunction", "1", "");
         return false;
     }
     JSObject *handler = NonNullObject(cx, vp[2]);
     if (!handler)
         return false;
     JSObject *proto, *parent;
     parent = vp[0].toObject().getParent();
-    proto = parent->getGlobal()->getOrCreateFunctionPrototype(cx);
+    proto = parent->global().getOrCreateFunctionPrototype(cx);
     if (!proto)
         return false;
     parent = proto->getParent();
 
     JSObject *call = js_ValueToCallableObject(cx, &vp[3], JSV2F_SEARCH_STACK);
     if (!call)
         return false;
     JSObject *construct = NULL;
@@ -1648,17 +1648,17 @@ callable_Construct(JSContext *cx, uintN 
         Value protov;
         if (!callable->getProperty(cx, ATOM(classPrototype), &protov))
             return false;
 
         JSObject *proto;
         if (protov.isObject()) {
             proto = &protov.toObject();
         } else {
-            proto = callable->getGlobal()->getOrCreateObjectPrototype(cx);
+            proto = callable->global().getOrCreateObjectPrototype(cx);
             if (!proto)
                 return false;
         }
 
         JSObject *newobj = NewObjectWithGivenProto(cx, &ObjectClass, proto, NULL);
         if (!newobj)
             return false;
 
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -311,16 +311,17 @@ class Handle
     T value() { return *ptr; }
 
     template <typename S>
     void testAssign() {
 #ifdef DEBUG
         T a = RootMethods<T>::initial();
         S b = RootMethods<S>::initial();
         a = b;
+        (void)a;
 #endif
     }
 };
 
 typedef Handle<JSObject*>          HandleObject;
 typedef Handle<JSFunction*>        HandleFunction;
 typedef Handle<Shape*>             HandleShape;
 typedef Handle<BaseShape*>         HandleBaseShape;
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1455,17 +1455,17 @@ void
 NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto)
 {
     Class *clasp = shape->getObjectClass();
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     if (CanBeFinalizedInBackground(kind, clasp))
         kind = GetBackgroundAllocKind(kind);
 
-    GlobalObject *global = shape->getObjectParent()->getGlobal();
+    GlobalObject *global = &shape->getObjectParent()->global();
     types::TypeObject *type = proto->getNewType(cx);
 
     EntryIndex entry;
     if (lookupGlobal(clasp, global, kind, &entry))
         PodZero(&entries[entry]);
     if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry))
         PodZero(&entries[entry]);
     if (lookupType(clasp, type, kind, &entry))
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -530,16 +530,17 @@ struct StackBaseShape
 typedef HashSet<ReadBarriered<UnownedBaseShape>,
                 StackBaseShape,
                 SystemAllocPolicy> BaseShapeSet;
 
 struct Shape : public js::gc::Cell
 {
     friend struct ::JSObject;
     friend struct ::JSFunction;
+    friend class js::StaticBlockObject;
     friend class js::PropertyTree;
     friend class js::Bindings;
     friend struct js::StackShape;
     friend struct js::StackBaseShape;
     friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
 
   protected:
     HeapPtrBaseShape    base_;
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -36,33 +36,37 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsscopeinlines_h___
 #define jsscopeinlines_h___
 
 #include <new>
+
 #include "jsarray.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsscope.h"
 #include "jsgc.h"
 #include "jsgcmark.h"
 
 #include "vm/ArgumentsObject.h"
+#include "vm/ScopeObject.h"
 #include "vm/StringObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/ScopeObject-inl.h"
+
 namespace js {
 
 inline
 BaseShape::BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags)
 {
     JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
     PodZero(this);
     this->clasp = clasp;
@@ -260,17 +264,17 @@ Shape::get(JSContext* cx, JSObject *rece
         return pobj->methodReadBarrier(cx, *this, vp);
     }
 
     /*
      * |with (it) color;| ends up here, as do XML filter-expressions.
      * Avoid exposing the With object to native getters.
      */
     if (obj->isWith())
-        obj = js_UnwrapWithObject(cx, obj);
+        obj = &obj->asWith().object();
     return js::CallJSPropertyOp(cx, getterOp(), receiver, getUserId(), vp);
 }
 
 inline bool
 Shape::set(JSContext* cx, JSObject* obj, bool strict, js::Value* vp) const
 {
     JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
 
@@ -279,17 +283,17 @@ Shape::set(JSContext* cx, JSObject* obj,
         return js::InvokeGetterOrSetter(cx, obj, fval, 1, vp, vp);
     }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     /* See the comment in js::Shape::get as to why we check for With. */
     if (obj->isWith())
-        obj = js_UnwrapWithObject(cx, obj);
+        obj = &obj->asWith().object();
     return js::CallJSPropertyOpSetter(cx, setterOp(), obj, getUserId(), strict, vp);
 }
 
 inline void
 Shape::setParent(js::Shape *p)
 {
     JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
                  p->maybeSlot() <= maybeSlot());
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -648,33 +648,34 @@ js_XDRScript(JSXDRState *xdr, JSScript *
      * that block objects from the script->objects array will be written and
      * restored in the outer-to-inner order. js_XDRBlockObject relies on this
      * to restore the parent chain.
      */
     for (i = 0; i != nobjects; ++i) {
         HeapPtr<JSObject> *objp = &script->objects()->vector[i];
         uint32_t isBlock;
         if (xdr->mode == JSXDR_ENCODE) {
-            Class *clasp = (*objp)->getClass();
-            JS_ASSERT(clasp == &FunctionClass ||
-                      clasp == &BlockClass);
-            isBlock = (clasp == &BlockClass) ? 1 : 0;
+            JSObject *obj = *objp;
+            JS_ASSERT(obj->isFunction() || obj->isStaticBlock());
+            isBlock = obj->isBlock() ? 1 : 0;
         }
         if (!JS_XDRUint32(xdr, &isBlock))
             goto error;
-        JSObject *tmp = *objp;
         if (isBlock == 0) {
+            JSObject *tmp = *objp;
             if (!js_XDRFunctionObject(xdr, &tmp))
                 goto error;
+            *objp = tmp;
         } else {
             JS_ASSERT(isBlock == 1);
-            if (!js_XDRBlockObject(xdr, &tmp))
+            StaticBlockObject *tmp = static_cast<StaticBlockObject *>(objp->get());
+            if (!js_XDRStaticBlockObject(xdr, &tmp))
                 goto error;
+            *objp = tmp;
         }
-        *objp = tmp;
     }
     for (i = 0; i != nupvars; ++i) {
         if (!JS_XDRUint32(xdr, reinterpret_cast<uint32_t *>(&script->upvars()->vector[i])))
             goto error;
     }
     for (i = 0; i != nregexps; ++i) {
         JSObject *tmp = script->regexps()->vector[i];
         if (!js_XDRRegExpObject(xdr, &tmp))
@@ -1256,34 +1257,34 @@ JSScript::NewScriptFromEmitter(JSContext
             bce->parent->compiling() &&
             bce->parent->asBytecodeEmitter()->checkSingletonContext() &&
             !fun->isFlatClosure();
 
         if (!script->typeSetFunction(cx, fun, singleton))
             return NULL;
 
         fun->setScript(script);
-        script->globalObject = fun->getParent() ? fun->getParent()->getGlobal() : NULL;
+        script->globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
     } else {
         /*
          * Initialize script->object, if necessary, so that the debugger has a
          * valid holder object.
          */
         if (bce->flags & TCF_NEED_SCRIPT_GLOBAL)
             script->globalObject = GetCurrentGlobal(cx);
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
     if (!bce->parent) {
         GlobalObject *compileAndGoGlobal = NULL;
         if (script->compileAndGo) {
             compileAndGoGlobal = script->globalObject;
             if (!compileAndGoGlobal)
-                compileAndGoGlobal = bce->scopeChain()->getGlobal();
+                compileAndGoGlobal = &bce->scopeChain()->global();
         }
         Debugger::onNewScript(cx, script, compileAndGoGlobal);
     }
 
     if (cx->hasRunOption(JSOPTION_PCCOUNT))
         (void) script->initCounts(cx);
 
     return script;
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -43,17 +43,17 @@
 
 #include "jsautooplen.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jsscope.h"
 
-#include "vm/CallObject.h"
+#include "vm/ScopeObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/RegExpObject.h"
 
 #include "jsscopeinlines.h"
 
 namespace js {
 
 inline
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1398,17 +1398,17 @@ class RegExpGuard
     explicit RegExpGuard(JSContext *cx) : cx(cx), rep(cx) {}
     ~RegExpGuard() {}
 
     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
     bool
     init(uintN argc, Value *vp, bool convertVoid = false)
     {
         if (argc != 0 && ValueIsRegExp(vp[2])) {
-            if (!rep.reset(vp[2].toObject().asRegExp()))
+            if (!rep.reset(&vp[2].toObject().asRegExp()))
                 return false;
         } else {
             if (convertVoid && (argc == 0 || vp[2].isUndefined())) {
                 fm.patstr = cx->runtime->emptyString;
                 return true;
             }
 
             fm.patstr = ArgToRootedString(cx, argc, vp, 0);
@@ -2533,18 +2533,17 @@ js::str_split(JSContext *cx, uintN argc,
     }
 
     /* Step 8. */
     RegExpMatcher matcher(cx);
     JSLinearString *sepstr = NULL;
     bool sepUndefined = (argc == 0 || vp[2].isUndefined());
     if (!sepUndefined) {
         if (ValueIsRegExp(vp[2])) {
-            RegExpObject *reobj = vp[2].toObject().asRegExp();
-            if (!matcher.reset(reobj))
+            if (!matcher.reset(&vp[2].toObject().asRegExp()))
                 return false;
         } else {
             JSString *sep = ToString(cx, vp[2]);
             if (!sep)
                 return false;
             vp[2].setString(sep);
 
             sepstr = sep->ensureLinear(cx);
@@ -3028,20 +3027,20 @@ StringObject::assignInitialShape(JSConte
                            LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 JSObject *
 js_InitStringClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *proto = global->createBlankPrototype(cx, &StringClass);
-    if (!proto || !proto->asString()->init(cx, cx->runtime->emptyString))
+    if (!proto || !proto->asString().init(cx, cx->runtime->emptyString))
         return NULL;
 
     /* Now create the String function. */
     JSFunction *ctor = global->createConstructor(cx, js_String, &StringClass,
                                                  CLASS_ATOM(cx, String), 1);
     if (!ctor)
         return NULL;
 
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -2369,17 +2369,17 @@ InitArrayBufferClass(JSContext *cx, Glob
     return arrayBufferProto;
 }
 
 JS_FRIEND_API(JSObject *)
 js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     /* Idempotency required: we initialize several things, possibly lazily. */
     JSObject *stop;
     if (!js_GetClassObject(cx, global, JSProto_ArrayBuffer, &stop))
         return NULL;
     if (stop)
         return stop;
 
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -325,17 +325,17 @@ static JSFunctionSpec weak_map_methods[]
     JS_FS_END
 };
 
 JSObject *
 js_InitWeakMapClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *weakMapProto = global->createBlankPrototype(cx, &WeakMapClass);
     if (!weakMapProto)
         return NULL;
 
     JSFunction *ctor = global->createConstructor(cx, WeakMap_construct, &WeakMapClass,
                                                  CLASS_ATOM(cx, WeakMap), 0);
     if (!ctor)
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -419,20 +419,20 @@ ForceFrame::enter()
 {
     frame = context->new_<DummyFrameGuard>();
     if (!frame)
        return false;
 
     JS_ASSERT(context->compartment == target->compartment());
     JSCompartment *destination = context->compartment;
 
-    JSObject *scopeChain = target->getGlobal();
-    JS_ASSERT(scopeChain->isNative());
+    JSObject &scopeChain = target->global();
+    JS_ASSERT(scopeChain.isNative());
 
-    return context->stack.pushDummyFrame(context, destination, *scopeChain, frame);
+    return context->stack.pushDummyFrame(context, destination, scopeChain, frame);
 }
 
 AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
     : context(cx),
       origin(cx->compartment),
       target(target),
       destination(target->compartment()),
       entered(false)
@@ -445,21 +445,21 @@ AutoCompartment::~AutoCompartment()
         leave();
 }
 
 bool
 AutoCompartment::enter()
 {
     JS_ASSERT(!entered);
     if (origin != destination) {
-        JSObject *scopeChain = target->getGlobal();
-        JS_ASSERT(scopeChain->isNative());
+        JSObject &scopeChain = target->global();
+        JS_ASSERT(scopeChain.isNative());
 
         frame.construct();
-        if (!context->stack.pushDummyFrame(context, destination, *scopeChain, &frame.ref()))
+        if (!context->stack.pushDummyFrame(context, destination, scopeChain, &frame.ref()))
             return false;
 
         if (context->isExceptionPending())
             context->wrapPendingException();
     }
     entered = true;
     return true;
 }
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -4651,17 +4651,17 @@ HasFunctionProperty(JSContext *cx, JSObj
         return false;
     if (!prop) {
         xml = (JSXML *) obj->getPrivate();
         if (HasSimpleContent(xml)) {
             /*
              * Search in String.prototype to set found whenever
              * GetXMLFunction returns existing function.
              */
-            JSObject *proto = obj->getGlobal()->getOrCreateStringPrototype(cx);
+            JSObject *proto = obj->global().getOrCreateStringPrototype(cx);
             if (!proto)
                 return false;
 
             if (!js_LookupProperty(cx, proto, funid, &pobj, &prop))
                 return false;
         }
     }
     *found = (prop != NULL);
@@ -7399,17 +7399,17 @@ js_GetXMLObject(JSContext *cx, JSXML *xm
     return obj;
 }
 
 JSObject *
 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *namespaceProto = global->createBlankPrototype(cx, &NamespaceClass);
     if (!namespaceProto)
         return NULL;
     JSFlatString *empty = cx->runtime->emptyString;
     namespaceProto->setNamePrefix(empty);
     namespaceProto->setNameURI(empty);
 
@@ -7432,17 +7432,17 @@ js_InitNamespaceClass(JSContext *cx, JSO
     return namespaceProto;
 }
 
 JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *qnameProto = global->createBlankPrototype(cx, &QNameClass);
     if (!qnameProto)
         return NULL;
     JSAtom *empty = cx->runtime->emptyString;
     if (!InitXMLQName(cx, qnameProto, empty, empty, empty))
         return NULL;
 
@@ -7464,17 +7464,17 @@ js_InitQNameClass(JSContext *cx, JSObjec
     return qnameProto;
 }
 
 JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    GlobalObject *global = obj->asGlobal();
+    GlobalObject *global = &obj->asGlobal();
 
     JSObject *xmlProto = global->createBlankPrototype(cx, &XMLClass);
     if (!xmlProto)
         return NULL;
     JSXML *xml = js_NewXML(cx, JSXML_CLASS_TEXT);
     if (!xml)
         return NULL;
     xmlProto->setPrivate(xml);
@@ -7590,19 +7590,18 @@ js_GetDefaultXMLNamespace(JSContext *cx,
     JSObject *ns, *obj, *tmp;
     jsval v;
 
     JSObject *scopeChain = GetCurrentScopeChain(cx);
     if (!scopeChain)
         return false;
 
     obj = NULL;
-    for (tmp = scopeChain; tmp; tmp = tmp->scopeChain()) {
-        Class *clasp = tmp->getClass();
-        if (clasp == &BlockClass || clasp == &WithClass)
+    for (tmp = scopeChain; tmp; tmp = tmp->enclosingScope()) {
+        if (tmp->isBlock() || tmp->isWith())
             continue;
         if (!tmp->getSpecial(cx, SpecialId::defaultXMLNamespace(), &v))
             return JS_FALSE;
         if (!JSVAL_IS_PRIMITIVE(v)) {
             *vp = v;
             return JS_TRUE;
         }
         obj = tmp;
@@ -7703,17 +7702,17 @@ JSString *
 js_ValueToXMLString(JSContext *cx, const Value &v)
 {
     return ToXMLString(cx, v, 0);
 }
 
 JSBool
 js_GetAnyName(JSContext *cx, jsid *idp)
 {
-    JSObject *global = cx->hasfp() ? cx->fp()->scopeChain().getGlobal() : cx->globalObject;
+    JSObject *global = cx->hasfp() ? &cx->fp()->scopeChain().global() : cx->globalObject;
     Value v = global->getReservedSlot(JSProto_AnyName);
     if (v.isUndefined()) {
         JSObject *obj = NewObjectWithGivenProto(cx, &AnyNameClass, NULL, global);
         if (!obj)
             return false;
 
         JS_ASSERT(!obj->getProto());
 
@@ -7788,17 +7787,17 @@ js_FindXMLProperty(JSContext *cx, const 
             if (!target->lookupGeneric(cx, funid, &pobj, &prop))
                 return JS_FALSE;
             if (prop) {
                 *idp = funid;
                 *objp = target;
                 return JS_TRUE;
             }
         }
-    } while ((obj = obj->scopeChain()) != NULL);
+    } while ((obj = obj->enclosingScope()) != NULL);
 
     JSAutoByteString printable;
     JSString *str = ConvertQNameToString(cx, nameobj);
     if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                      JSMSG_UNDEFINED_XML_NAME, printable.ptr());
     }
     return JS_FALSE;
@@ -7824,17 +7823,17 @@ GetXMLFunction(JSContext *cx, JSObject *
             break;
     }
 
     JSXML *xml = (JSXML *) obj->getPrivate();
     if (!HasSimpleContent(xml))
         return true;
 
     /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
-    JSObject *proto = obj->getGlobal()->getOrCreateStringPrototype(cx);
+    JSObject *proto = obj->global().getOrCreateStringPrototype(cx);
     if (!proto)
         return false;
 
     return proto->getGeneric(cx, id, vp);
 }
 
 static JSXML *
 GetPrivate(JSContext *cx, JSObject *obj, const char *method)
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6372,17 +6372,17 @@ mjit::Compiler::jsop_callgname_epilogue(
     if (fval->isNotType(JSVAL_TYPE_OBJECT)) {
         frame.push(UndefinedValue());
         return;
     }
 
     /* Paths for known object callee. */
     if (fval->isConstant()) {
         JSObject *obj = &fval->getValue().toObject();
-        if (obj->getGlobal() == globalObj) {
+        if (&obj->global() == globalObj) {
             frame.push(UndefinedValue());
         } else {
             prepareStubCall(Uses(1));
             INLINE_STUBCALL(stubs::PushImplicitThisForGlobal, REJOIN_NONE);
             frame.pushSynced(JSVAL_TYPE_UNKNOWN);
         }
         return;
     }
@@ -6829,29 +6829,29 @@ mjit::Compiler::jsop_newinit()
 
 bool
 mjit::Compiler::jsop_regexp()
 {
     JSObject *obj = script->getRegExp(fullAtomIndex(PC));
     RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL;
 
     if (!globalObj ||
-        obj->getGlobal() != globalObj ||
+        &obj->global() != globalObj ||
         !cx->typeInferenceEnabled() ||
         analysis->localsAliasStack() ||
         types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx),
                                        types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
         prepareStubCall(Uses(0));
         masm.move(ImmPtr(obj), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
         frame.pushSynced(JSVAL_TYPE_OBJECT);
         return true;
     }
 
-    RegExpObject *reobj = obj->asRegExp();
+    RegExpObject *reobj = &obj->asRegExp();
 
     DebugOnly<uint32_t> origFlags = reobj->getFlags();
     DebugOnly<uint32_t> staticsFlags = res->getFlags();
     JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
 
     /*
      * JS semantics require regular expression literals to create different
      * objects every time they execute. We only need to do this cloning if the
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -634,17 +634,17 @@ mjit::Compiler::compileArrayConcat(types
 {
     /*
      * Require the 'this' types to have a specific type matching the current
      * global, so we can create the result object inline.
      */
     if (thisTypes->getObjectCount() != 1)
         return Compile_InlineAbort;
     types::TypeObject *thisType = thisTypes->getTypeObject(0);
-    if (!thisType || thisType->proto->getGlobal() != globalObj)
+    if (!thisType || &thisType->proto->global() != globalObj)
         return Compile_InlineAbort;
 
     /*
      * Constraints modeling this concat have not been generated by inference,
      * so check that type information already reflects possible side effects of
      * this call.
      */
     thisTypes->addFreeze(cx);
@@ -834,17 +834,17 @@ mjit::Compiler::inlineNativeFunction(uin
     JSObject *callee = &origCallee->getValue().toObject();
     if (!callee->isFunction())
         return Compile_InlineAbort;
 
     /*
      * The callee must have the same parent as the script's global, otherwise
      * inference may not have accounted for any side effects correctly.
      */
-    if (!globalObj || globalObj != callee->getGlobal())
+    if (!globalObj || globalObj != &callee->global())
         return Compile_InlineAbort;
 
     Native native = callee->toFunction()->maybeNative();
 
     if (!native)
         return Compile_InlineAbort;
 
     JSValueType type = knownPushedType(0);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -625,26 +625,26 @@ js_InternalThrow(VMFrame &f)
 
     /*
      * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go
      * back into the interpreter with a pending exception. This will cause
      * it to immediately rethrow.
      */
     if (cx->isExceptionPending()) {
         JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
-        JSObject *obj = script->getObject(GET_SLOTNO(pc));
-        Value *vp = cx->regs().sp + OBJ_BLOCK_COUNT(cx, obj);
+        StaticBlockObject &blockObj = script->getObject(GET_SLOTNO(pc))->asStaticBlock();
+        Value *vp = cx->regs().sp + blockObj.slotCount();
         SetValueRangeToUndefined(cx->regs().sp, vp);
         cx->regs().sp = vp;
         JS_ASSERT(JSOp(pc[JSOP_ENTERBLOCK_LENGTH]) == JSOP_EXCEPTION);
         cx->regs().sp[0] = cx->getPendingException();
         cx->clearPendingException();
         cx->regs().sp++;
         cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH;
-        cx->regs().fp()->setBlockChain(obj);
+        cx->regs().fp()->setBlockChain(&blockObj);
     }
 
     *f.oldregs = f.regs;
 
     return NULL;
 }
 
 void JS_FASTCALL
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -81,23 +81,23 @@ PatchGetFallback(VMFrame &f, ic::GetGlob
     Repatcher repatch(f.jit());
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName));
     repatch.relink(ic->slowPathCall, fptr);
 }
 
 void JS_FASTCALL
 ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
 {
-    JSObject *obj = f.fp()->scopeChain().getGlobal();
+    JSObject &obj = f.fp()->scopeChain().global();
     JSAtom *atom = f.script()->getAtom(GET_INDEX(f.pc()));
     jsid id = ATOM_TO_JSID(atom);
 
     RecompilationMonitor monitor(f.cx);
 
-    const Shape *shape = obj->nativeLookup(f.cx, id);
+    const Shape *shape = obj.nativeLookup(f.cx, id);
 
     if (monitor.recompiled()) {
         stubs::GetGlobalName(f);
         return;
     }
 
     if (!shape ||
         !shape->hasDefaultGetterOrIsMethod() ||
@@ -107,20 +107,20 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlo
             PatchGetFallback(f, ic);
         stubs::GetGlobalName(f);
         return;
     }
     uint32_t slot = shape->slot();
 
     /* Patch shape guard. */
     Repatcher repatcher(f.jit());
-    repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj->lastProperty());
+    repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj.lastProperty());
 
     /* Patch loads. */
-    uint32_t index = obj->dynamicSlotIndex(slot);
+    uint32_t index = obj.dynamicSlotIndex(slot);
     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
     repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
 
     /* Do load anyway... this time. */
     stubs::GetGlobalName(f);
 }
 
 template <JSBool strict>
@@ -203,26 +203,26 @@ UpdateSetGlobalName(VMFrame &f, ic::SetG
                                               ic->vr.isTypeKnown());
 
     return Lookup_Cacheable;
 }
 
 void JS_FASTCALL
 ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
-    JSObject *obj = f.fp()->scopeChain().getGlobal();
+    JSObject &obj = f.fp()->scopeChain().global();
     JSScript *script = f.script();
     JSAtom *atom = script->getAtom(GET_INDEX(f.pc()));
 
     RecompilationMonitor monitor(f.cx);
 
-    const Shape *shape = obj->nativeLookup(f.cx, ATOM_TO_JSID(atom));
+    const Shape *shape = obj.nativeLookup(f.cx, ATOM_TO_JSID(atom));
 
     if (!monitor.recompiled()) {
-        LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape);
+        LookupStatus status = UpdateSetGlobalName(f, ic, &obj, shape);
         if (status == Lookup_Error)
             THROW();
     }
 
     if (ic->usePropertyCache)
         STRICT_VARIANT(stubs::SetGlobalName)(f, atom);
     else
         STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, atom);
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -49,17 +49,17 @@
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jspropertycache.h"
 #include "jspropertycacheinlines.h"
 #include "jsinterpinlines.h"
 #include "jsautooplen.h"
 
-#include "vm/CallObject-inl.h"
+#include "vm/ScopeObject-inl.h"
 
 #if defined JS_POLYIC
 
 using namespace js;
 using namespace js::mjit;
 using namespace js::mjit::ic;
 
 typedef JSC::FunctionPtr FunctionPtr;
@@ -1438,20 +1438,20 @@ class ScopeNameCompiler : public PICStub
             /* Guard on intervening shapes. */
             masm.loadShape(pic.objReg, pic.shapeReg);
             Jump j = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
                                     ImmPtr(tobj->lastProperty()));
             if (!fails.append(j))
                 return error();
 
             /* Load the next link in the scope chain. */
-            Address parent(pic.objReg, JSObject::offsetOfInternalScopeChain());
+            Address parent(pic.objReg, ScopeObject::offsetOfEnclosingScope());
             masm.loadPayload(parent, pic.objReg);
 
-            tobj = tobj->internalScopeChain();
+            tobj = &tobj->asScope().enclosingScope();
         }
 
         if (tobj != getprop.holder)
             return disable("scope chain walk terminated early");
 
         return Lookup_Cacheable;
     }
 
@@ -1496,17 +1496,17 @@ class ScopeNameCompiler : public PICStub
         JumpList fails(cx);
         ScopeNameLabels &labels = pic.scopeNameLabels();
 
         /* For GETXPROP, the object is already in objReg. */
         if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
             masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
 
         JS_ASSERT(obj == getprop.holder);
-        JS_ASSERT(getprop.holder == scopeChain->getGlobal());
+        JS_ASSERT(getprop.holder == &scopeChain->global());
 
         LookupStatus status = walkScopeChain(masm, fails);
         if (status != Lookup_Cacheable)
             return status;
 
         /* If a scope chain walk was required, the final object needs a NULL test. */
         MaybeJump finalNull;
         if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
@@ -1580,17 +1580,17 @@ class ScopeNameCompiler : public PICStub
         Vector<Jump, 8> fails(cx);
         ScopeNameLabels &labels = pic.scopeNameLabels();
 
         /* For GETXPROP, the object is already in objReg. */
         if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
             masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
 
         JS_ASSERT(obj == getprop.holder);
-        JS_ASSERT(getprop.holder != scopeChain->getGlobal());
+        JS_ASSERT(getprop.holder != &scopeChain->global());
 
         CallObjPropKind kind;
         const Shape *shape = getprop.shape;
         if (shape->getterOp() == GetCallArg) {
             kind = ARG;
         } else if (shape->getterOp() == GetCallVar) {
             kind = VAR;
         } else {
@@ -1758,17 +1758,17 @@ class ScopeNameCompiler : public PICStub
             if (thisvp)
                 return ComputeImplicitThis(cx, obj, *vp, thisvp);
             return true;
         }
 
         const Shape *shape = getprop.shape;
         JSObject *normalized = obj;
         if (obj->isWith() && !shape->hasDefaultGetter())
-            normalized = js_UnwrapWithObject(cx, obj);
+            normalized = &obj->asWith().object();
         NATIVE_GET(cx, normalized, holder, shape, JSGET_METHOD_BARRIER, vp, return false);
         if (thisvp)
             return ComputeImplicitThis(cx, normalized, *vp, thisvp);
         return true;
     }
 };
 
 class BindNameCompiler : public PICStubCompiler
@@ -1820,27 +1820,27 @@ class BindNameCompiler : public PICStubC
         /* Guard on the shape of the scope chain. */
         masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
         masm.loadShape(pic.objReg, pic.shapeReg);
         Jump firstShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
                                          ImmPtr(scopeChain->lastProperty()));
 
         /* Walk up the scope chain. */
         JSObject *tobj = scopeChain;
-        Address parent(pic.objReg, JSObject::offsetOfInternalScopeChain());
+        Address parent(pic.objReg, ScopeObject::offsetOfEnclosingScope());
         while (tobj && tobj != obj) {
             if (!IsCacheableNonGlobalScope(tobj))
                 return disable("non-cacheable obj in scope chain");
             masm.loadPayload(parent, pic.objReg);
             masm.loadShape(pic.objReg, pic.shapeReg);
             Jump shapeTest = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
                                             ImmPtr(tobj->lastProperty()));
             if (!fails.append(shapeTest))
                 return error();
-            tobj = tobj->internalScopeChain();
+            tobj = &tobj->asScope().enclosingScope();
         }
         if (tobj != obj)
             return disable("indirect hit");
 
         Jump done = masm.jump();
 
         // All failures flow to here, so there is a common point to patch.
         for (Jump *pj = fails.begin(); pj != fails.end(); ++pj)
@@ -1927,29 +1927,29 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
             f.regs.sp[-1].setInt32(str->length());
             return;
         } else if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
             f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
             return;
         } else if (!f.regs.sp[-1].isPrimitive()) {
             JSObject *obj = &f.regs.sp[-1].toObject();
             if (obj->isArray() ||
-                (obj->isArguments() && !obj->asArguments()->hasOverriddenLength()) ||
+                (obj->isArguments() && !obj->asArguments().hasOverriddenLength()) ||
                 obj->isString()) {
                 GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledGetPropIC);
                 if (obj->isArray()) {
                     LookupStatus status = cc.generateArrayLengthStub();
                     if (status == Lookup_Error)
                         THROW();
                     f.regs.sp[-1].setNumber(obj->getArrayLength());
                 } else if (obj->isArguments()) {
                     LookupStatus status = cc.generateArgsLengthStub();
                     if (status == Lookup_Error)
                         THROW();
-                    f.regs.sp[-1].setInt32(int32_t(obj->asArguments()->initialLength()));
+                    f.regs.sp[-1].setInt32(int32_t(obj->asArguments().initialLength()));
                 } else if (obj->isString()) {
                     LookupStatus status = cc.generateStringObjLengthStub();
                     if (status == Lookup_Error)
                         THROW();
                     JSString *str = obj->getPrimitiveThis().toString();
                     f.regs.sp[-1].setInt32(str->length());
                 }
                 return;
@@ -2064,24 +2064,24 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pi
 
     // Do this first in case js_GetClassPrototype triggers a recompilation.
     jsid id = ATOM_TO_JSID(pic->atom);
 
     Value objv;
     if (lval.isObject()) {
         objv = lval;
     } else {
-        GlobalObject *global = f.fp()->scopeChain().getGlobal();
+        GlobalObject &global = f.fp()->scopeChain().global();
         JSObject *pobj;
         if (lval.isString()) {
-            pobj = global->getOrCreateStringPrototype(cx);
+            pobj = global.getOrCreateStringPrototype(cx);
         } else if (lval.isNumber()) {
-            pobj = global->getOrCreateNumberPrototype(cx);
+            pobj = global.getOrCreateNumberPrototype(cx);
         } else if (lval.isBoolean()) {
-            pobj = global->getOrCreateBooleanPrototype(cx);
+            pobj = global.getOrCreateBooleanPrototype(cx);
         } else {
             JS_ASSERT(lval.isNull() || lval.isUndefined());
             js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
             THROW();
         }
         if (!pobj)
             THROW();
         objv.setObject(*pobj);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -113,17 +113,17 @@ stubs::BindNameNoCache(VMFrame &f, JSAto
     if (!obj)
         THROW();
     f.regs.sp[0].setObject(*obj);
 }
 
 JSObject * JS_FASTCALL
 stubs::BindGlobalName(VMFrame &f)
 {
-    return f.fp()->scopeChain().getGlobal();
+    return &f.fp()->scopeChain().global();
 }
 
 template<JSBool strict>
 void JS_FASTCALL
 stubs::SetName(VMFrame &f, JSAtom *origAtom)
 {
     JSContext *cx = f.cx;
 
@@ -312,17 +312,17 @@ NameOp(VMFrame &f, JSObject *obj, bool c
         /* Take the slow path if prop was not found in a native object. */
         if (!obj->isNative() || !obj2->isNative()) {
             if (!obj->getGeneric(cx, id, &rval))
                 return NULL;
         } else {
             Shape *shape = (Shape *)prop;
             JSObject *normalized = obj;
             if (normalized->isWith() && !shape->hasDefaultGetter())
-                normalized = js_UnwrapWithObject(cx, normalized);
+                normalized = &normalized->asWith().object();
             NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
         }
 
         /*
          * If this is an incop, update the property's types themselves,
          * to capture the type effect on the intermediate value.
          */
         if (rval.isUndefined() && (js_CodeSpec[*f.pc()].format & (JOF_INC|JOF_DEC)))
@@ -342,17 +342,17 @@ stubs::Name(VMFrame &f)
 {
     if (!NameOp(f, &f.fp()->scopeChain(), false))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::GetGlobalName(VMFrame &f)
 {
-    JSObject *globalObj = f.fp()->scopeChain().getGlobal();
+    JSObject *globalObj = &f.fp()->scopeChain().global();
     if (!NameOp(f, globalObj, false))
          THROW();
 }
 
 void JS_FASTCALL
 stubs::GetElem(VMFrame &f)
 {
     JSContext *cx = f.cx;
@@ -390,17 +390,17 @@ stubs::GetElem(VMFrame &f)
     if (IsDefinitelyIndex(rref, &index)) {
         if (obj->isDenseArray()) {
             if (index < obj->getDenseArrayInitializedLength()) {
                 rval = obj->getDenseArrayElement(index);
                 if (!rval.isMagic())
                     return;
             }
         } else if (obj->isArguments()) {
-            if (obj->asArguments()->getElement(index, &rval))
+            if (obj->asArguments().getElement(index, &rval))
                 return;
         }
 
         if (!obj->getElement(cx, index, &rval))
             THROW();
     } else {
         SpecialId special;
         if (ValueIsSpecial(obj, &rref, &special, cx)) {
@@ -551,17 +551,17 @@ stubs::CallName(VMFrame &f)
 /*
  * Push the implicit this value, with the assumption that the callee
  * (which is on top of the stack) was read as a property from the
  * global object.
  */
 void JS_FASTCALL
 stubs::PushImplicitThisForGlobal(VMFrame &f)
 {
-    return PushImplicitThis(f, f.fp()->scopeChain().getGlobal(), f.regs.sp[-1]);
+    return PushImplicitThis(f, &f.fp()->scopeChain().global(), f.regs.sp[-1]);
 }
 
 void JS_FASTCALL
 stubs::BitOr(VMFrame &f)
 {
     int32_t i, j;
 
     if (!ToInt32(f.cx, f.regs.sp[-2], &i) || !ToInt32(f.cx, f.regs.sp[-1], &j))
@@ -686,17 +686,17 @@ stubs::DefFun(VMFrame &f, JSFunction *fu
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
      * requests in server-side JS.
      */
     if (obj->toFunction()->environment() != obj2) {
         obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
         if (!obj)
             THROW();
-        JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
+        JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global());
     }
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     uintN attrs = fp->isEvalFrame()
                   ? JSPROP_ENUMERATE
@@ -1336,17 +1336,17 @@ stubs::DefLocalFun(VMFrame &f, JSFunctio
         parent = GetScopeChain(f.cx, f.fp());
         if (!parent)
             THROWV(NULL);
     }
     JSObject *obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, parent);
     if (!obj)
         THROWV(NULL);
 
-    JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
+    JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global());
 
     return obj;
 }
 
 JSObject * JS_FASTCALL
 stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
 {
     JSObject *obj = js_NewFlatClosure(f.cx, fun);
@@ -1357,17 +1357,17 @@ stubs::DefLocalFun_FC(VMFrame &f, JSFunc
 
 void JS_FASTCALL
 stubs::RegExp(VMFrame &f, JSObject *regex)
 {
     /*
      * Push a regexp object cloned from the regexp literal object mapped by the
      * bytecode at pc.
      */
-    JSObject *proto = f.fp()->scopeChain().getGlobal()->getOrCreateRegExpPrototype(f.cx);
+    JSObject *proto = f.fp()->scopeChain().global().getOrCreateRegExpPrototype(f.cx);
     if (!proto)
         THROW();
     JS_ASSERT(proto);
     JSObject *obj = js_CloneRegExpObject(f.cx, regex, proto);
     if (!obj)
         THROW();
     f.regs.sp[0].setObject(*obj);
 }
@@ -1446,17 +1446,17 @@ stubs::Lambda(VMFrame &f, JSFunction *fu
         if (!parent)
             THROWV(NULL);
     }
 
     JSObject *obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, parent);
     if (!obj)
         THROWV(NULL);
 
-    JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
+    JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global());
     return obj;
 }
 
 static bool JS_ALWAYS_INLINE
 InlineGetProp(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
@@ -1531,24 +1531,24 @@ stubs::CallProp(VMFrame &f, JSAtom *orig
 
     Value lval;
     lval = regs.sp[-1];
 
     Value objv;
     if (lval.isObject()) {
         objv = lval;
     } else {
-        GlobalObject *global = f.fp()->scopeChain().getGlobal();
+        GlobalObject &global = f.fp()->scopeChain().global();
         JSObject *pobj;
         if (lval.isString()) {
-            pobj = global->getOrCreateStringPrototype(cx);
+            pobj = global.getOrCreateStringPrototype(cx);
         } else if (lval.isNumber()) {
-            pobj = global->getOrCreateNumberPrototype(cx);
+            pobj = global.getOrCreateNumberPrototype(cx);
         } else if (lval.isBoolean()) {
-            pobj = global->getOrCreateBooleanPrototype(cx);
+            pobj = global.getOrCreateBooleanPrototype(cx);
         } else {
             JS_ASSERT(lval.isNull() || lval.isUndefined());
             js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
             THROW();
         }
         if (!pobj)
             THROW();
         objv.setObject(*pobj);
@@ -1814,82 +1814,74 @@ stubs::FastInstanceOf(VMFrame &f)
     f.regs.sp[-3].setBoolean(js_IsDelegate(f.cx, &lref.toObject(), f.regs.sp[-3]));
 }
 
 void JS_FASTCALL
 stubs::EnterBlock(VMFrame &f, JSObject *obj)
 {
     FrameRegs &regs = f.regs;
     StackFrame *fp = f.fp();
+    StaticBlockObject &blockObj = obj->asStaticBlock();
 
     JS_ASSERT(!f.regs.inlined());
-    JS_ASSERT(obj->isStaticBlock());
 
     if (*regs.pc == JSOP_ENTERBLOCK) {
-        JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
-        Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
+        JS_ASSERT(fp->base() + blockObj.stackDepth() == regs.sp);
+        Value *vp = regs.sp + blockObj.slotCount();
         JS_ASSERT(regs.sp < vp);
         JS_ASSERT(vp <= fp->slots() + fp->script()->nslots);
         SetValueRangeToUndefined(regs.sp, vp);
         regs.sp = vp;
     }
 
 #ifdef DEBUG
     JSContext *cx = f.cx;
-    JS_ASSERT(fp->maybeBlockChain() == obj->staticBlockScopeChain());
+    JS_ASSERT(fp->maybeBlockChain() == blockObj.enclosingBlock());
 
     /*
      * The young end of fp->scopeChain() may omit blocks if we haven't closed
      * over them, but if there are any closure blocks on fp->scopeChain(), they'd
      * better be (clones of) ancestors of the block we're entering now;
      * anything else we should have popped off fp->scopeChain() when we left its
      * static scope.
      */
     JSObject *obj2 = &fp->scopeChain();
     while (obj2->isWith())
-        obj2 = obj2->internalScopeChain();
+        obj2 = &obj2->asWith().enclosingScope();
     if (obj2->isBlock() &&
         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
-        JSObject *youngestProto = obj2->getProto();
-        JS_ASSERT(youngestProto->isStaticBlock());
-        JSObject *parent = obj;
-        while ((parent = parent->scopeChain()) != youngestProto)
+        JSObject &youngestProto = obj2->asClonedBlock().staticBlock();
+        StaticBlockObject *parent = &blockObj;
+        while ((parent = parent->enclosingBlock()) != &youngestProto)
             JS_ASSERT(parent);
     }
 #endif
 
-    fp->setBlockChain(obj);
+    fp->setBlockChain(&blockObj);
 }
 
 void JS_FASTCALL
 stubs::LeaveBlock(VMFrame &f)
 {
     JSContext *cx = f.cx;
     StackFrame *fp = f.fp();
 
-#ifdef DEBUG
-    JS_ASSERT(fp->blockChain().isBlock());
-    uintN blockDepth = OBJ_BLOCK_DEPTH(cx, &fp->blockChain());
+    StaticBlockObject &blockObj = fp->blockChain();
+    JS_ASSERT(blockObj.stackDepth() <= StackDepth(fp->script()));
 
-    JS_ASSERT(blockDepth <= StackDepth(fp->script()));
-#endif
     /*
      * If we're about to leave the dynamic scope of a block that has been
      * cloned onto fp->scopeChain(), clear its private data, move its locals from
      * the stack into the clone, and pop it off the chain.
      */
     JSObject &obj = fp->scopeChain();
-    JSObject &blockChain = fp->blockChain();
-    if (obj.getProto() == &blockChain) {
-        JS_ASSERT(obj.isBlock());
-        if (!js_PutBlockObject(cx, JS_TRUE))
-            THROW();
-    }
+    if (obj.getProto() == &blockObj && !obj.asClonedBlock().put(cx, JS_TRUE))
+        THROW();
 
-    fp->setBlockChain(blockChain.staticBlockScopeChain());
+    fp->setBlockChain(blockObj.enclosingBlock());
 }
 
 void * JS_FASTCALL
 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
 {
     jsbytecode *jpc = pc;
     JSScript *script = f.fp()->script();
     bool ctor = f.fp()->isConstructing();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3735,17 +3735,17 @@ Wrap(JSContext *cx, uintN argc, jsval *v
 {
     jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
     if (JSVAL_IS_PRIMITIVE(v)) {
         JS_SET_RVAL(cx, vp, v);
         return true;
     }
 
     JSObject *obj = JSVAL_TO_OBJECT(v);
-    JSObject *wrapped = Wrapper::New(cx, obj, obj->getProto(), obj->getGlobal(),
+    JSObject *wrapped = Wrapper::New(cx, obj, obj->getProto(), &obj->global(),
                                      &Wrapper::singleton);
     if (!wrapped)
         return false;
 
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
     return true;
 }
 
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -136,17 +136,17 @@ struct ArgumentsData
  *     the function returns, when it is replaced with null.  When an arguments
  *     object is created on-trace its private is JS_ARGUMENTS_OBJECT_ON_TRACE,
  *     and when the trace exits its private is replaced with the stack frame or
  *     null, as appropriate. This slot is used by strict arguments objects as
  *     well, but the slot is always null. Conceptually it would be better to
  *     remove this oddity, but preserving it allows us to work with arguments
  *     objects of either kind more abstractly, so we keep it for now.
  */
-class ArgumentsObject : public ::JSObject
+class ArgumentsObject : public JSObject
 {
     static const uint32_t INITIAL_LENGTH_SLOT = 0;
     static const uint32_t DATA_SLOT = 1;
     static const uint32_t STACK_FRAME_SLOT = 2;
 
   public:
     static const uint32_t RESERVED_SLOTS = 3;
     static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
@@ -238,30 +238,30 @@ class StrictArgumentsObject : public Arg
 {
     friend bool JSObject::isStrictArguments() const;
     friend ArgumentsObject *
     ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee, StackFrame *fp);
 };
 
 } // namespace js
 
-js::NormalArgumentsObject *
+js::NormalArgumentsObject &
 JSObject::asNormalArguments()
 {
     JS_ASSERT(isNormalArguments());
-    return reinterpret_cast<js::NormalArgumentsObject *>(this);
+    return *reinterpret_cast<js::NormalArgumentsObject *>(this);
 }
 
-js::StrictArgumentsObject *
+js::StrictArgumentsObject &
 JSObject::asStrictArguments()
 {
     JS_ASSERT(isStrictArguments());
-    return reinterpret_cast<js::StrictArgumentsObject *>(this);
+    return *reinterpret_cast<js::StrictArgumentsObject *>(this);
 }
 
-js::ArgumentsObject *
+js::ArgumentsObject &
 JSObject::asArguments()
 {
     JS_ASSERT(isArguments());
-    return reinterpret_cast<js::ArgumentsObject *>(this);
+    return *reinterpret_cast<js::ArgumentsObject *>(this);
 }
 
 #endif /* ArgumentsObject_h___ */
--- a/js/src/vm/BooleanObject-inl.h
+++ b/js/src/vm/BooleanObject-inl.h
@@ -40,43 +40,43 @@
 
 #ifndef BooleanObject_inl_h___
 #define BooleanObject_inl_h___
 
 #include "jsobjinlines.h"
 
 #include "vm/BooleanObject.h"
 
-inline js::BooleanObject *
+inline js::BooleanObject &
 JSObject::asBoolean()
 {
     JS_ASSERT(isBoolean());
-    return static_cast<js::BooleanObject *>(const_cast<JSObject *>(this));
+    return *static_cast<js::BooleanObject *>(this);
 }
 
 namespace js {
 
 inline BooleanObject *
 BooleanObject::create(JSContext *cx, bool b)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &BooleanClass);
     if (!obj)
         return NULL;
-    BooleanObject *boolobj = obj->asBoolean();
-    boolobj->setPrimitiveValue(b);
-    return boolobj;
+    BooleanObject &boolobj = obj->asBoolean();
+    boolobj.setPrimitiveValue(b);
+    return &boolobj;
 }
 
 inline BooleanObject *
 BooleanObject::createWithProto(JSContext *cx, bool b, JSObject &proto)
 {
     JSObject *obj = NewObjectWithClassProto(cx, &BooleanClass, &proto, NULL,
                                             gc::GetGCObjectKind(RESERVED_SLOTS));
     if (!obj)
         return NULL;
-    BooleanObject *boolobj = obj->asBoolean();
-    boolobj->setPrimitiveValue(b);
-    return boolobj;
+    BooleanObject &boolobj = obj->asBoolean();
+    boolobj.setPrimitiveValue(b);
+    return &boolobj;
 }
 
 } // namespace js
 
 #endif /* BooleanObject_inl_h__ */
--- a/js/src/vm/BooleanObject.h
+++ b/js/src/vm/BooleanObject.h
@@ -42,17 +42,17 @@
 #define BooleanObject_h___
 
 #include "mozilla/Attributes.h"
 
 #include "jsbool.h"
 
 namespace js {
 
-class BooleanObject : public ::JSObject
+class BooleanObject : public JSObject
 {
     /* Stores this Boolean object's [[PrimitiveValue]]. */
     static const uintN PRIMITIVE_VALUE_SLOT = 0;
 
   public:
     static const uintN RESERVED_SLOTS = 1;
 
     /*
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -166,17 +166,17 @@ ScriptGlobal(JSContext *cx, JSScript *sc
 
     /*
      * The referent is a non-held script. There is no direct reference from
      * script to the scope, so find it on the stack.
      */
     for (AllFramesIter i(cx->stack.space()); ; ++i) {
         JS_ASSERT(!i.done());
         if (i.fp()->maybeScript() == script)
-            return i.fp()->scopeChain().getGlobal();
+            return &i.fp()->scopeChain().global();
     }
     JS_NOT_REACHED("ScriptGlobal: live non-held script not on stack");
 }
 
 bool
 BreakpointSite::recompile(JSContext *cx, bool forTrap)
 {
 #ifdef JS_METHODJIT
@@ -426,17 +426,17 @@ Debugger::hasAnyLiveHooks(JSContext *cx)
     return false;
 }
 
 JSTrapStatus
 Debugger::slowPathOnEnterFrame(JSContext *cx, Value *vp)
 {
     /* Build the list of recipients. */
     AutoValueVector triggered(cx);
-    GlobalObject *global = cx->fp()->scopeChain().getGlobal();
+    GlobalObject *global = &cx->fp()->scopeChain().global();
     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
             Debugger *dbg = *p;
             JS_ASSERT(dbg->observesFrame(cx->fp()));
             if (dbg->observesEnterFrame() && !triggered.append(ObjectValue(*dbg->toJSObject())))
                 return JSTRAP_ERROR;
         }
     }
@@ -453,17 +453,17 @@ Debugger::slowPathOnEnterFrame(JSContext
 
     return JSTRAP_CONTINUE;
 }
 
 void
 Debugger::slowPathOnLeaveFrame(JSContext *cx)
 {
     StackFrame *fp = cx->fp();
-    GlobalObject *global = fp->scopeChain().getGlobal();
+    GlobalObject *global = &fp->scopeChain().global();
 
     /*
      * FIXME This notifies only current debuggers, so it relies on a hack in
      * Debugger::removeDebuggeeGlobal to make sure only current debuggers have
      * Frame objects with .live === true.
      */
     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
@@ -806,17 +806,17 @@ Debugger::dispatchHook(JSContext *cx, Va
      * Determine which debuggers will receive this event, and in what order.
      * Make a copy of the list, since the original is mutable and we will be
      * calling into arbitrary JS.
      *
      * Note: In the general case, 'triggered' contains references to objects in
      * different compartments--every compartment *except* this one.
      */
     AutoValueVector triggered(cx);
-    GlobalObject *global = cx->fp()->scopeChain().getGlobal();
+    GlobalObject *global = &cx->fp()->scopeChain().global();
     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
             Debugger *dbg = *p;
             if (dbg->enabled && dbg->getHook(which)) {
                 if (!triggered.append(ObjectValue(*dbg->toJSObject())))
                     return JSTRAP_ERROR;
             }
         }
@@ -894,17 +894,17 @@ Debugger::slowPathOnNewScript(JSContext 
     }
 }
 
 JSTrapStatus
 Debugger::onTrap(JSContext *cx, Value *vp)
 {
     StackFrame *fp = cx->fp();
     JSScript *script = fp->script();
-    GlobalObject *scriptGlobal = fp->scopeChain().getGlobal();
+    GlobalObject *scriptGlobal = &fp->scopeChain().global();
     jsbytecode *pc = cx->regs().pc;
     BreakpointSite *site = script->getBreakpointSite(pc);
     JSOp op = JSOp(*pc);
 
     /* Build list of breakpoint handlers. */
     Vector<Breakpoint *> triggered(cx);
     for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
         if (!triggered.append(bp))
@@ -970,17 +970,17 @@ Debugger::onSingleStep(JSContext *cx, Va
     /* We should only receive single-step traps for scripted frames. */
     JS_ASSERT(fp->isScriptFrame());
 
     /*
      * Build list of Debugger.Frame instances referring to this frame with
      * onStep handlers.
      */
     AutoObjectVector frames(cx);
-    GlobalObject *global = fp->scopeChain().getGlobal();
+    GlobalObject *global = &fp->scopeChain().global();
     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         for (Debugger **d = debuggers->begin(); d != debuggers->end(); d++) {
             Debugger *dbg = *d;
             if (FrameMap::Ptr p = dbg->frames.lookup(fp)) {
                 JSObject *frame = p->value;
                 if (!frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
                     !frames.append(frame))
                     return JSTRAP_ERROR;
@@ -1531,17 +1531,17 @@ Debugger::unwrapDebuggeeArgument(JSConte
 JSBool
 Debugger::addDebuggee(JSContext *cx, uintN argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.addDebuggee", 1);
     THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
     if (!referent)
         return false;
-    GlobalObject *global = referent->getGlobal();
+    GlobalObject *global = &referent->global();
     if (!dbg->addDebuggeeGlobal(cx, global))
         return false;
 
     Value v = ObjectValue(*referent);
     if (!dbg->wrapDebuggeeValue(cx, &v))
         return false;
     args.rval() = v;
     return true;
@@ -1550,32 +1550,32 @@ Debugger::addDebuggee(JSContext *cx, uin
 JSBool
 Debugger::removeDebuggee(JSContext *cx, uintN argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.removeDebuggee", 1);
     THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
     if (!referent)
         return false;
-    GlobalObject *global = referent->getGlobal();
+    GlobalObject *global = &referent->global();
     if (dbg->debuggees.has(global))
         dbg->removeDebuggeeGlobal(cx, global, NULL, NULL);
     args.rval().setUndefined();
     return true;
 }
 
 JSBool
 Debugger::hasDebuggee(JSContext *cx, uintN argc, Value *vp)
 {
     REQUIRE_ARGC("Debugger.hasDebuggee", 1);
     THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg);
     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
     if (!referent)
         return false;
-    args.rval().setBoolean(!!dbg->debuggees.lookup(referent->getGlobal()));
+    args.rval().setBoolean(!!dbg->debuggees.lookup(&referent->global()));
     return true;
 }
 
 JSBool
 Debugger::getDebuggees(JSContext *cx, uintN argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg);
     JSObject *arrobj = NewDenseAllocatedArray(cx, dbg->debuggees.count(), NULL);
@@ -1660,17 +1660,17 @@ Debugger::construct(JSContext *cx, uintN
     obj->setPrivate(dbg);
     if (!dbg->init(cx)) {
         cx->delete_(dbg);
         return false;
     }
 
     /* Add the initial debuggees, if any. */
     for (uintN i = 0; i < argc; i++) {
-        GlobalObject *debuggee = GetProxyPrivate(&args[i].toObject()).toObject().getGlobal();
+        GlobalObject *debuggee = &GetProxyPrivate(&args[i].toObject()).toObject().global();
         if (!dbg->addDebuggeeGlobal(cx, debuggee))
             return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
@@ -1768,17 +1768,17 @@ Debugger::removeDebuggeeGlobal(JSContext
      * Debugger objects that are no longer debugging the relevant global might
      * have live Frame objects. So we take the easy way out and kill them here.
      * This is a bug, since it's observable and contrary to the spec. One
      * possible fix would be to put such objects into a compartment-wide bag
      * which slowPathOnLeaveFrame would have to examine.
      */
     for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
         StackFrame *fp = e.front().key;
-        if (fp->scopeChain().getGlobal() == global) {
+        if (&fp->scopeChain().global() == global) {
             e.front().value->setPrivate(NULL);
             e.removeFront();
         }
     }
 
     GlobalObject::DebuggerVector *v = global->getDebuggers();
     Debugger **p;
     for (p = v->begin(); p != v->end(); p++) {
@@ -2632,17 +2632,17 @@ DebuggerFrame_getArguments(JSContext *cx
         args.rval() = argumentsv;
         return true;
     }
 
     JSObject *argsobj;
     if (fp->hasArgs()) {
         /* Create an arguments object. */
         RootedVar<GlobalObject*> global(cx);
-        global = args.callee().getGlobal();
+        global = &args.callee().global();
         JSObject *proto;
         if (!js_GetClassPrototype(cx, global, JSProto_Array, &proto))
             return false;
         argsobj = NewObjectWithGivenProto(cx, &DebuggerArguments_class, proto, global);
         if (!argsobj ||
             !js_SetReservedSlot(cx, argsobj, JSSLOT_DEBUGARGUMENTS_FRAME, ObjectValue(*thisobj)))
         {
             return false;
@@ -3678,17 +3678,17 @@ DebuggerEnv_getType(JSContext *cx, uintN
 }
 
 static JSBool
 DebuggerEnv_getParent(JSContext *cx, uintN argc, Value *vp)
 {
     THIS_DEBUGENV_OWNER(cx, argc, vp, "get parent", args, envobj, env, dbg);
 
     /* Don't bother switching compartments just to get env's parent. */
-    Env *parent = env->scopeChain();
+    Env *parent = env->enclosingScope();
     return dbg->wrapEnvironment(cx, parent, &args.rval());
 }
 
 static JSBool
 DebuggerEnv_getObject(JSContext *cx, uintN argc, Value *vp)
 {
     THIS_DEBUGENV_OWNER(cx, argc, vp, "get type", args, envobj, env, dbg);
 
@@ -3755,17 +3755,17 @@ DebuggerEnv_find(JSContext *cx, uintN ar
         AutoCompartment ac(cx, env);
         if (!ac.enter() || !cx->compartment->wrapId(cx, &id))
             return false;
 
         /* This can trigger resolve hooks. */
         ErrorCopier ec(ac, dbg->toJSObject());
         JSProperty *prop = NULL;
         JSObject *pobj;
-        for (; env && !prop; env = env->scopeChain()) {
+        for (; env && !prop; env = env->enclosingScope()) {
             if (!env->lookupGeneric(cx, id, &pobj, &prop))
                 return false;
             if (prop)
                 break;
         }
     }
 
     return dbg->wrapEnvironment(cx, env, &args.rval());
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -478,17 +478,17 @@ bool
 Debugger::observesGlobal(GlobalObject *global) const
 {
     return debuggees.has(global);
 }
 
 bool
 Debugger::observesFrame(StackFrame *fp) const
 {
-    return observesGlobal(fp->scopeChain().getGlobal());
+    return observesGlobal(&fp->scopeChain().global());
 }
 
 JSTrapStatus
 Debugger::onEnterFrame(JSContext *cx, Value *vp)
 {
     if (cx->compartment->getDebuggees().empty())
         return JSTRAP_CONTINUE;
     return slowPathOnEnterFrame(cx, vp);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -55,25 +55,25 @@
 
 using namespace js;
 
 JSObject *
 js_InitObjectClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    return obj->asGlobal()->getOrCreateObjectPrototype(cx);
+    return obj->asGlobal().getOrCreateObjectPrototype(cx);
 }
 
 JSObject *
 js_InitFunctionClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
-    return obj->asGlobal()->getOrCreateFunctionPrototype(cx);
+    return obj->asGlobal().getOrCreateFunctionPrototype(cx);
 }
 
 static JSBool
 ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
 {
     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                  JSMSG_THROW_TYPE_ERROR);
     return false;
@@ -267,17 +267,17 @@ GlobalObject::create(JSContext *cx, Clas
 {
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
     RootedVar<GlobalObject*> obj(cx);
 
     JSObject *obj_ = NewObjectWithGivenProto(cx, clasp, NULL, NULL);
     if (!obj_)
         return NULL;
-    obj = obj_->asGlobal();
+    obj = &obj_->asGlobal();
 
     if (!obj->setSingletonType(cx) || !obj->setVarObj(cx))
         return NULL;
 
     /* Construct a regexp statics object for this global object. */
     JSObject *res = RegExpStatics::create(cx, obj);
     if (!res)
         return NULL;
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -86,17 +86,17 @@ class Debugger;
  *
  * The first two ranges are necessary to implement js::FindClassObject,
  * js::FindClassPrototype, and spec language speaking in terms of "the original
  * Array prototype object", or "as if by the expression new Array()" referring
  * to the original Array constructor.  The third range stores the (writable and
  * even deletable) Object, Array, &c. properties (although a slot won't be used
  * again if its property is deleted and readded).
  */
-class GlobalObject : public ::JSObject {
+class GlobalObject : public JSObject {
     GlobalObject(const GlobalObject &other) MOZ_DELETE;
     void operator=(const GlobalObject &other) MOZ_DELETE;
 
     /*
      * Count of slots to store built-in constructors, prototypes, and initial
      * visible properties for the constructors.
      */
     static const uintN STANDARD_CLASS_SLOTS  = JSProto_LIMIT * 3;
@@ -367,16 +367,16 @@ typedef HashSet<GlobalObject *, DefaultH
 } // namespace js
 
 inline bool
 JSObject::isGlobal() const
 {
     return !!(js::GetObjectClass(this)->flags & JSCLASS_IS_GLOBAL);
 }
 
-js::GlobalObject *
+js::GlobalObject &
 JSObject::asGlobal()
 {
     JS_ASSERT(isGlobal());
-    return static_cast<js::GlobalObject *>(this);
+    return *static_cast<js::GlobalObject *>(this);
 }
 
 #endif /* GlobalObject_h___ */
--- a/js/src/vm/NumberObject-inl.h
+++ b/js/src/vm/NumberObject-inl.h
@@ -38,43 +38,43 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef NumberObject_inl_h___
 #define NumberObject_inl_h___
 
 #include "NumberObject.h"
 
-inline js::NumberObject *
+inline js::NumberObject &
 JSObject::asNumber()
 {
     JS_ASSERT(isNumber());
-    return static_cast<js::NumberObject *>(const_cast<JSObject *>(this));
+    return *static_cast<js::NumberObject *>(this);
 }
 
 namespace js {
 
 inline NumberObject *
 NumberObject::create(JSContext *cx, jsdouble d)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &NumberClass);
     if (!obj)
         return NULL;
-    NumberObject *numobj = obj->asNumber();
-    numobj->setPrimitiveValue(d);
-    return numobj;
+    NumberObject &numobj = obj->asNumber();
+    numobj.setPrimitiveValue(d);
+    return &numobj;
 }
 
 inline NumberObject *
 NumberObject::createWithProto(JSContext *cx, jsdouble d, JSObject &proto)
 {
     JSObject *obj = NewObjectWithClassProto(cx, &NumberClass, &proto, NULL,
                                             gc::GetGCObjectKind(RESERVED_SLOTS));
     if (!obj)
         return NULL;
-    NumberObject *numobj = obj->asNumber();
-    numobj->setPrimitiveValue(d);
-    return numobj;
+    NumberObject &numobj = obj->asNumber();
+    numobj.setPrimitiveValue(d);
+    return &numobj;
 }
 
 } // namespace js
 
 #endif /* NumberObject_inl_h__ */
--- a/js/src/vm/NumberObject.h
+++ b/js/src/vm/NumberObject.h
@@ -42,17 +42,17 @@
 #define NumberObject_h___
 
 #include "mozilla/Attributes.h"
 
 #include "jsnum.h"
 
 namespace js {
 
-class NumberObject : public ::JSObject
+class NumberObject : public JSObject
 {
     /* Stores this Number object's [[PrimitiveValue]]. */
     static const uintN PRIMITIVE_VALUE_SLOT = 0;
 
   public:
     static const uintN RESERVED_SLOTS = 1;
 
     /*
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -45,21 +45,21 @@
 
 #include "RegExpObject.h"
 #include "RegExpStatics.h"
 
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 #include "RegExpStatics-inl.h"
 
-inline js::RegExpObject *
+inline js::RegExpObject &
 JSObject::asRegExp()
 {
     JS_ASSERT(isRegExp());
-    return static_cast<js::RegExpObject *>(this);
+    return *static_cast<js::RegExpObject *>(this);
 }
 
 namespace js {
 
 inline bool
 ValueIsRegExp(const Value &v)
 {
     return !v.isPrimitive() && v.toObject().isRegExp();
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -93,31 +93,31 @@ RegExpObjectBuilder::getOrCreate()
     if (reobj_)
         return true;
 
     JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
     if (!obj)
         return false;
     obj->setPrivate(NULL);
 
-    reobj_ = obj->asRegExp();
+    reobj_ = &obj->asRegExp();
     return true;
 }
 
 bool
 RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
 {
     JS_ASSERT(!reobj_);
 
     JSObject *clone = NewObjectWithGivenProto(cx, &RegExpClass, proto, proto->getParent());
     if (!clone)
         return false;
     clone->setPrivate(NULL);
 
-    reobj_ = clone->asRegExp();
+    reobj_ = &clone->asRegExp();
     return true;
 }
 
 RegExpObject *
 RegExpObjectBuilder::build(AlreadyIncRefed<RegExpPrivate> rep)
 {
     if (!getOrCreate()) {
         rep->decref(cx);
@@ -322,19 +322,19 @@ RegExpObject::assignInitialShape(JSConte
 JSBool
 js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
 {
     JSString *source = 0;
     uint32_t flagsword = 0;
 
     if (xdr->mode == JSXDR_ENCODE) {
         JS_ASSERT(objp);
-        RegExpObject *reobj = (*objp)->asRegExp();
-        source = reobj->getSource();
-        flagsword = reobj->getFlags();
+        RegExpObject &reobj = (*objp)->asRegExp();
+        source = reobj.getSource();
+        flagsword = reobj.getFlags();
     }
     if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword))
         return false;
     if (xdr->mode == JSXDR_DECODE) {
         JSAtom *atom = js_AtomizeString(xdr->cx, source);
         if (!atom)
             return false;
         RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword),
@@ -355,24 +355,24 @@ js_XDRRegExpObject(JSXDRState *xdr, JSOb
 
 #define js_XDRRegExpObject NULL
 
 #endif /* !JS_HAS_XDR */
 
 static void
 regexp_finalize(JSContext *cx, JSObject *obj)
 {
-    obj->asRegExp()->finalize(cx);
+    obj->asRegExp().finalize(cx);
 }
 
 static void
 regexp_trace(JSTracer *trc, JSObject *obj)
 {
     if (trc->runtime->gcRunning)
-        obj->asRegExp()->purge(trc->context);
+        obj->asRegExp().purge(trc->context);
 }
 
 Class js::RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,         /* addProperty */
@@ -560,17 +560,17 @@ RegExpPrivate::create(JSContext *cx, JSL
 
 JSObject * JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     JS_ASSERT(obj->isRegExp());
     JS_ASSERT(proto->isRegExp());
 
     RegExpObjectBuilder builder(cx);
-    return builder.clone(obj->asRegExp(), proto->asRegExp());
+    return builder.clone(&obj->asRegExp(), &proto->asRegExp());
 }
 
 JSFlatString *
 RegExpObject::toString(JSContext *cx) const
 {
     JSLinearString *src = getSource();
     StringBuffer sb(cx);
     if (size_t len = src->length()) {
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -60,17 +60,17 @@ namespace js {
 
 enum RegExpRunStatus
 {
     RegExpRunStatus_Error,
     RegExpRunStatus_Success,
     RegExpRunStatus_Success_NotFound
 };
 
-class RegExpObject : public ::JSObject
+class RegExpObject : public JSObject
 {
     typedef detail::RegExpPrivate RegExpPrivate;
     typedef detail::RegExpPrivateCode RegExpPrivateCode;
 
     static const uintN LAST_INDEX_SLOT          = 0;
     static const uintN SOURCE_SLOT              = 1;
     static const uintN GLOBAL_FLAG_SLOT         = 2;
     static const uintN IGNORE_CASE_FLAG_SLOT    = 3;
rename from js/src/vm/CallObject-inl.h
rename to js/src/vm/ScopeObject-inl.h
--- a/js/src/vm/CallObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -33,159 +33,313 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef CallObject_inl_h___
-#define CallObject_inl_h___
+#ifndef ScopeObject_inl_h___
+#define ScopeObject_inl_h___
 
-#include "CallObject.h"
+#include "ScopeObject.h"
 
 namespace js {
 
+inline JSObject &
+ScopeObject::enclosingScope() const
+{
+    return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
+}
+
+inline bool
+ScopeObject::setEnclosingScope(JSContext *cx, JSObject &obj)
+{
+    if (!obj.setDelegate(cx))
+        return false;
+    setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(obj));
+    return true;
+}
+
+inline StackFrame *
+ScopeObject::maybeStackFrame() const
+{
+    JS_ASSERT(!isStaticBlock());
+    return reinterpret_cast<StackFrame *>(JSObject::getPrivate());
+}
+
+inline void
+ScopeObject::setStackFrame(StackFrame *frame)
+{
+    return setPrivate(frame);
+}
+
+/*static*/ inline size_t
+ScopeObject::offsetOfEnclosingScope()
+{
+    return getFixedSlotOffset(SCOPE_CHAIN_SLOT);
+}
+
 inline bool
 CallObject::isForEval() const
 {
-    JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
-    JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
-                 getFixedSlot(CALLEE_SLOT).toObject().isFunction());
-    return getFixedSlot(CALLEE_SLOT).isNull();
-}
-
-inline js::StackFrame *
-CallObject::maybeStackFrame() const
-{
-    return reinterpret_cast<js::StackFrame *>(getPrivate());
-}
-
-inline void
-CallObject::setStackFrame(StackFrame *frame)
-{
-    return setPrivate(frame);
+    JS_ASSERT(getReservedSlot(CALLEE_SLOT).isObjectOrNull());
+    JS_ASSERT_IF(getReservedSlot(CALLEE_SLOT).isObject(),
+                 getReservedSlot(CALLEE_SLOT).toObject().isFunction());
+    return getReservedSlot(CALLEE_SLOT).isNull();
 }
 
 inline void
 CallObject::setCallee(JSObject *callee)
 {
     JS_ASSERT_IF(callee, callee->isFunction());
-    setFixedSlot(CALLEE_SLOT, js::ObjectOrNullValue(callee));
-}
-
-inline void
-CallObject::initCallee(JSObject *callee)
-{
-    JS_ASSERT_IF(callee, callee->isFunction());
-    initFixedSlot(CALLEE_SLOT, js::ObjectOrNullValue(callee));
+    setFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
 }
 
 inline JSObject *
 CallObject::getCallee() const
 {
-    return getFixedSlot(CALLEE_SLOT).toObjectOrNull();
+    return getReservedSlot(CALLEE_SLOT).toObjectOrNull();
 }
 
 inline JSFunction *
 CallObject::getCalleeFunction() const
 {
-    return getFixedSlot(CALLEE_SLOT).toObject().toFunction();
+    return getReservedSlot(CALLEE_SLOT).toObject().toFunction();
 }
 
-inline const js::Value &
+inline const Value &
 CallObject::getArguments() const
 {
     JS_ASSERT(!isForEval());
-    return getFixedSlot(ARGUMENTS_SLOT);
+    return getReservedSlot(ARGUMENTS_SLOT);
 }
 
 inline void
-CallObject::setArguments(const js::Value &v)
+CallObject::setArguments(const Value &v)
 {
     JS_ASSERT(!isForEval());
     setFixedSlot(ARGUMENTS_SLOT, v);
 }
 
 inline void
-CallObject::initArguments(const js::Value &v)
+CallObject::initArguments(const Value &v)
 {
     JS_ASSERT(!isForEval());
     initFixedSlot(ARGUMENTS_SLOT, v);
 }
 
-inline const js::Value &
+inline const Value &
 CallObject::arg(uintN i) const
 {
     JS_ASSERT(i < getCalleeFunction()->nargs);
     return getSlot(RESERVED_SLOTS + i);
 }
 
 inline void
-CallObject::setArg(uintN i, const js::Value &v)
+CallObject::setArg(uintN i, const Value &v)
 {
     JS_ASSERT(i < getCalleeFunction()->nargs);
     setSlot(RESERVED_SLOTS + i, v);
 }
 
 inline void
-CallObject::initArgUnchecked(uintN i, const js::Value &v)
+CallObject::initArgUnchecked(uintN i, const Value &v)
 {
     JS_ASSERT(i < getCalleeFunction()->nargs);
     initSlotUnchecked(RESERVED_SLOTS + i, v);
 }
 
-inline const js::Value &
+inline const Value &
 CallObject::var(uintN i) const
 {
     JSFunction *fun = getCalleeFunction();
     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
     JS_ASSERT(i < fun->script()->bindings.countVars());
     return getSlot(RESERVED_SLOTS + fun->nargs + i);
 }
 
 inline void
-CallObject::setVar(uintN i, const js::Value &v)
+CallObject::setVar(uintN i, const Value &v)
 {
     JSFunction *fun = getCalleeFunction();
     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
     JS_ASSERT(i < fun->script()->bindings.countVars());
     setSlot(RESERVED_SLOTS + fun->nargs + i, v);
 }
 
 inline void
-CallObject::initVarUnchecked(uintN i, const js::Value &v)
+CallObject::initVarUnchecked(uintN i, const Value &v)
 {
     JSFunction *fun = getCalleeFunction();
     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
     JS_ASSERT(i < fun->script()->bindings.countVars());
     initSlotUnchecked(RESERVED_SLOTS + fun->nargs + i, v);
 }
 
 inline void
 CallObject::copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots)
 {
     JS_ASSERT(slotInRange(RESERVED_SLOTS + nargs + nvars, SENTINEL_ALLOWED));
     copySlotRange(RESERVED_SLOTS, argv, nargs);
     copySlotRange(RESERVED_SLOTS + nargs, slots, nvars);
 }
 
-inline js::HeapValueArray
+inline HeapValueArray
 CallObject::argArray()
 {
-    js::DebugOnly<JSFunction*> fun = getCalleeFunction();
+    DebugOnly<JSFunction*> fun = getCalleeFunction();
     JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS, fun->nargs));
     return HeapValueArray(getSlotAddress(RESERVED_SLOTS));
 }
 
-inline js::HeapValueArray
+inline HeapValueArray
 CallObject::varArray()
 {
     JSFunction *fun = getCalleeFunction();
     JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS + fun->nargs,
                                  fun->script()->bindings.countVars()));
     return HeapValueArray(getSlotAddress(RESERVED_SLOTS + fun->nargs));
 }
 
+inline uint32_t
+NestedScopeObject::stackDepth() const
+{
+    return getReservedSlot(DEPTH_SLOT).toPrivateUint32();
+}
+
+inline JSObject &
+WithObject::withThis() const
+{
+    return getReservedSlot(THIS_SLOT).toObject();
+}
+
+inline JSObject &
+WithObject::object() const
+{
+    return *JSObject::getProto();
+}
+
+inline uint32_t
+BlockObject::slotCount() const
+{
+    return propertyCount();
+}
+
+inline HeapValue &
+BlockObject::slotValue(unsigned i)
+{
+    return getSlotRef(RESERVED_SLOTS + i);
+}
+
+inline StaticBlockObject *
+StaticBlockObject::enclosingBlock() const
+{
+    JSObject *obj = getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
+    return obj ? &obj->asStaticBlock() : NULL;
+}
+
+inline void
+StaticBlockObject::setEnclosingBlock(StaticBlockObject *blockObj)
+{
+    setFixedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(blockObj));
+}
+
+inline void
+StaticBlockObject::setStackDepth(uint32_t depth)
+{
+    JS_ASSERT(getReservedSlot(DEPTH_SLOT).isUndefined());
+    getReservedSlotRef(DEPTH_SLOT).init(PrivateUint32Value(depth));
+}
+
+inline void
+StaticBlockObject::setDefinitionParseNode(unsigned i, Definition *def)
+{
+    JS_ASSERT(slotValue(i).isUndefined());
+    slotValue(i).init(PrivateValue(def));
+}
+
+inline Definition *
+StaticBlockObject::maybeDefinitionParseNode(unsigned i)
+{
+    Value v = slotValue(i);
+    return v.isUndefined() ? NULL : reinterpret_cast<Definition *>(v.toPrivate());
+}
+
+inline void
+StaticBlockObject::poisonDefinitionParseNode(unsigned i)
+{
+    slotValue(i).init(PrivateValue(NULL));
+}
+
+inline StaticBlockObject &
+ClonedBlockObject::staticBlock() const
+{
+    return getProto()->asStaticBlock();
+}
+
+inline const Value &
+ClonedBlockObject::closedSlot(unsigned i)
+{
+    JS_ASSERT(!maybeStackFrame());
+    return slotValue(i);
+}
+
+}  /* namespace js */
+
+inline js::ScopeObject &
+JSObject::asScope()
+{
+    JS_ASSERT(isScope());
+    return *static_cast<js::ScopeObject *>(this);
+}
+
+inline js::CallObject &
+JSObject::asCall()
+{
+    JS_ASSERT(isCall());
+    return *static_cast<js::CallObject *>(this);
+}
+
+inline js::DeclEnvObject &
+JSObject::asDeclEnv()
+{
+    JS_ASSERT(isDeclEnv());
+    return *static_cast<js::DeclEnvObject *>(this);
+}
+
+inline js::NestedScopeObject &
+JSObject::asNestedScope()
+{
+    JS_ASSERT(isWith() || isBlock());
+    return *static_cast<js::NestedScopeObject *>(this);
+}
+
+inline js::WithObject &
+JSObject::asWith()
+{
+    JS_ASSERT(isWith());
+    return *static_cast<js::WithObject *>(this);
+}
+
+inline js::BlockObject &
+JSObject::asBlock()
+{
+    JS_ASSERT(isBlock());
+    return *static_cast<js::BlockObject *>(this);
+}
+
+inline js::StaticBlockObject &
+JSObject::asStaticBlock()
+{
+    JS_ASSERT(isStaticBlock());
+    return *static_cast<js::StaticBlockObject *>(this);
+}
+
+inline js::ClonedBlockObject &
+JSObject::asClonedBlock()
+{
+    JS_ASSERT(isClonedBlock());
+    return *static_cast<js::ClonedBlockObject *>(this);
 }
 
 #endif /* CallObject_inl_h___ */
rename from js/src/vm/CallObject.cpp
rename to js/src/vm/ScopeObject.cpp
--- a/js/src/vm/CallObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -33,31 +33,42 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "jsobjinlines.h"
-#include "CallObject.h"
+#include "jscompartment.h"
+#include "jsiter.h"
+#include "jsscope.h"
+#if JS_HAS_XDR
+#include "jsxdrapi.h"
+#endif
 
-#include "CallObject-inl.h"
+#include "GlobalObject.h"
+#include "ScopeObject.h"
 
-namespace js {
+#include "jsatominlines.h"
+#include "jsobjinlines.h"
+
+#include "ScopeObject-inl.h"
+
+using namespace js;
+using namespace js::types;
 
 /*
  * Construct a call object for the given bindings.  If this is a call object
  * for a function invocation, callee should be the function being called.
  * Otherwise it must be a call object for eval of strict mode code, and callee
  * must be null.
  */
 CallObject *
-CallObject::create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
+CallObject::create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee)
 {
     Bindings &bindings = script->bindings;
     gc::AllocKind kind = gc::GetGCObjectKind(bindings.lastShape()->numFixedSlots() + 1);
 
     RootedVarTypeObject type(cx);
 
     type = cx->compartment->getEmptyType(cx);
     if (!type)
@@ -74,44 +85,653 @@ CallObject::create(JSContext *cx, JSScri
     if (!obj)
         return NULL;
 
     /*
      * Update the parent for bindings associated with non-compileAndGo scripts,
      * whose call objects do not have a consistent global variable and need
      * to be updated dynamically.
      */
-    JSObject *global = scopeChain.getGlobal();
-    if (global != obj->getParent()) {
+    JSObject &global = enclosing.global();
+    if (&global != obj->getParent()) {
         JS_ASSERT(obj->getParent() == NULL);
-        if (!obj->setParent(cx, global))
+        if (!obj->setParent(cx, &global))
             return NULL;
     }
 
 #ifdef DEBUG
+    JS_ASSERT(!obj->inDictionaryMode());
     for (Shape::Range r = obj->lastProperty(); !r.empty(); r.popFront()) {
         const Shape &s = r.front();
         if (s.hasSlot()) {
             JS_ASSERT(s.slot() + 1 == obj->slotSpan());
             break;
         }
     }
 #endif
 
-    JS_ASSERT(obj->isCall());
-    JS_ASSERT(!obj->inDictionaryMode());
+    if (!obj->asScope().setEnclosingScope(cx, enclosing))
+        return NULL;
 
-    if (!obj->setInternalScopeChain(cx, &scopeChain))
-        return NULL;
+    JS_ASSERT_IF(callee, callee->isFunction());
+    obj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
 
     /*
      * If |bindings| is for a function that has extensible parents, that means
-     * its Call should have its own shape; see js::BaseShape::extensibleParents.
+     * its Call should have its own shape; see BaseShape::extensibleParents.
      */
     if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx))
         return NULL;
 
-    CallObject &callobj = obj->asCall();
-    callobj.initCallee(callee);
-    return &callobj;
+    return &obj->asCall();
+}
+
+Class js::DeclEnvClass = {
+    js_Object_str,
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
+    JS_PropertyStub,         /* addProperty */
+    JS_PropertyStub,         /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub
+};
+
+DeclEnvObject *
+DeclEnvObject::create(JSContext *cx, StackFrame *fp)
+{
+    RootedVarTypeObject type(cx);
+    type = cx->compartment->getEmptyType(cx);
+    if (!type)
+        return NULL;
+
+    RootedVarShape emptyDeclEnvShape(cx);
+    emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
+                                                    &fp->scopeChain().global(),
+                                                    FINALIZE_KIND);
+    if (!emptyDeclEnvShape)
+        return NULL;
+
+    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL);
+    if (!obj)
+        return NULL;
+
+    obj->setPrivate(fp);
+    if (!obj->asScope().setEnclosingScope(cx, fp->scopeChain()))
+        return NULL;
+
+    return &obj->asDeclEnv();
+}
+
+WithObject *
+WithObject::create(JSContext *cx, StackFrame *fp, JSObject &proto, JSObject &enclosing,
+                   uint32_t depth)
+{
+    RootedVarTypeObject type(cx);
+    type = proto.getNewType(cx);
+    if (!type)
+        return NULL;
+
+    RootedVarShape emptyWithShape(cx);
+    emptyWithShape = EmptyShape::getInitialShape(cx, &WithClass, &proto,
+                                                 &enclosing.global(), FINALIZE_KIND);
+    if (!emptyWithShape)
+        return NULL;
+
+    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyWithShape, type, NULL);
+    if (!obj)
+        return NULL;
+
+    if (!obj->asScope().setEnclosingScope(cx, enclosing))
+        return NULL;
+
+    obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
+    obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
+
+    JSObject *thisp = proto.thisObject(cx);
+    if (!thisp)
+        return NULL;
+
+    obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
+
+    return &obj->asWith();
+}
+
+static JSBool
+with_LookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp)
+{
+    /* Fixes bug 463997 */
+    uintN flags = cx->resolveFlags;
+    if (flags == RESOLVE_INFER)
+        flags = js_InferFlags(cx, flags);
+    flags |= JSRESOLVE_WITH;
+    JSAutoResolveFlags rf(cx, flags);
+    return obj->asWith().object().lookupGeneric(cx, id, objp, propp);
+}
+
+static JSBool
+with_LookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, JSProperty **propp)
+{
+    return with_LookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
+}
+
+static JSBool
+with_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
+                   JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_LookupGeneric(cx, obj, id, objp, propp);
+}
+
+static JSBool
+with_LookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp)
+{
+    return with_LookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
+}
+
+static JSBool
+with_GetGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
+{
+    return obj->asWith().object().getGeneric(cx, id, vp);
+}
+
+static JSBool
+with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
+{
+    return with_GetGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp);
+}
+
+static JSBool
+with_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_GetGeneric(cx, obj, receiver, id, vp);
+}
+
+static JSBool
+with_GetSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
+{
+    return with_GetGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
+}
+
+static JSBool
+with_SetGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
+{
+    return obj->asWith().object().setGeneric(cx, id, vp, strict);
+}
+
+static JSBool
+with_SetProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
+{
+    return obj->asWith().object().setProperty(cx, name, vp, strict);
+}
+
+static JSBool
+with_SetElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
+{
+    return obj->asWith().object().setElement(cx, index, vp, strict);
+}
+
+static JSBool
+with_SetSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
+{
+    return obj->asWith().object().setSpecial(cx, sid, vp, strict);
+}
+
+static JSBool
+with_GetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
+{
+    return obj->asWith().object().getGenericAttributes(cx, id, attrsp);
+}
+
+static JSBool
+with_GetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp)
+{
+    return obj->asWith().object().getPropertyAttributes(cx, name, attrsp);
+}
+
+static JSBool
+with_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp)
+{
+    return obj->asWith().object().getElementAttributes(cx, index, attrsp);
+}
+
+static JSBool
+with_GetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp)
+{
+    return obj->asWith().object().getSpecialAttributes(cx, sid, attrsp);
+}
+
+static JSBool
+with_SetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
+{
+    return obj->asWith().object().setGenericAttributes(cx, id, attrsp);
+}
+
+static JSBool
+with_SetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, uintN *attrsp)
+{
+    return obj->asWith().object().setPropertyAttributes(cx, name, attrsp);
+}
+
+static JSBool
+with_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, uintN *attrsp)
+{
+    return obj->asWith().object().setElementAttributes(cx, index, attrsp);
+}
+
+static JSBool
+with_SetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, uintN *attrsp)
+{
+    return obj->asWith().object().setSpecialAttributes(cx, sid, attrsp);
+}
+
+static JSBool
+with_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
+{
+    return obj->asWith().object().deleteGeneric(cx, id, rval, strict);
+}
+
+static JSBool
+with_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
+{
+    return obj->asWith().object().deleteProperty(cx, name, rval, strict);
+}
+
+static JSBool
+with_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
+{
+    return obj->asWith().object().deleteElement(cx, index, rval, strict);
+}
+
+static JSBool
+with_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
+{
+    return obj->asWith().object().deleteSpecial(cx, sid, rval, strict);
+}
+
+static JSBool
+with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
+               Value *statep, jsid *idp)
+{
+    return obj->asWith().object().enumerate(cx, enum_op, statep, idp);
+}
+
+static JSType
+with_TypeOf(JSContext *cx, JSObject *obj)
+{
+    return JSTYPE_OBJECT;
+}
+
+static JSObject *
+with_ThisObject(JSContext *cx, JSObject *obj)
+{
+    return &obj->asWith().withThis();
 }
 
+Class js::WithClass = {
+    "With",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(WithObject::RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS,
+    JS_PropertyStub,         /* addProperty */
+    JS_PropertyStub,         /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    NULL,                    /* finalize */
+    NULL,                    /* reserved    */
+    NULL,                    /* checkAccess */
+    NULL,                    /* call        */
+    NULL,                    /* construct   */
+    NULL,                    /* xdrObject   */
+    NULL,                    /* hasInstance */
+    NULL,                    /* trace       */
+    JS_NULL_CLASS_EXT,
+    {
+        with_LookupGeneric,
+        with_LookupProperty,
+        with_LookupElement,
+        with_LookupSpecial,
+        NULL,             /* defineGeneric */
+        NULL,             /* defineProperty */
+        NULL,             /* defineElement */
+        NULL,             /* defineSpecial */
+        with_GetGeneric,
+        with_GetProperty,
+        with_GetElement,
+        NULL,             /* getElementIfPresent */
+        with_GetSpecial,
+        with_SetGeneric,
+        with_SetProperty,
+        with_SetElement,
+        with_SetSpecial,
+        with_GetGenericAttributes,
+        with_GetPropertyAttributes,
+        with_GetElementAttributes,
+        with_GetSpecialAttributes,
+        with_SetGenericAttributes,
+        with_SetPropertyAttributes,
+        with_SetElementAttributes,
+        with_SetSpecialAttributes,
+        with_DeleteGeneric,
+        with_DeleteProperty,
+        with_DeleteElement,
+        with_DeleteSpecial,
+        with_Enumerate,
+        with_TypeOf,
+        NULL,             /* fix   */
+        with_ThisObject,
+        NULL,             /* clear */
+    }
+};
+
+ClonedBlockObject *
+ClonedBlockObject::create(JSContext *cx, StaticBlockObject &block, StackFrame *fp)
+{
+    RootedVarTypeObject type(cx);
+    type = block.getNewType(cx);
+    if (!type)
+        return NULL;
+
+    HeapValue *slots;
+    if (!PreallocateObjectDynamicSlots(cx, block.lastProperty(), &slots))
+        return NULL;
+
+    RootedVarShape shape(cx);
+    shape = block.lastProperty();
+
+    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, shape, type, slots);
+    if (!obj)
+        return NULL;
+
+    /* Set the parent if necessary, as for call objects. */
+    JSObject &global = fp->scopeChain().global();
+    if (&global != obj->getParent()) {
+        JS_ASSERT(obj->getParent() == NULL);
+        if (!obj->setParent(cx, &global))
+            return NULL;
+    }
+
+    JS_ASSERT(!obj->inDictionaryMode());
+    JS_ASSERT(obj->slotSpan() >= block.slotCount() + RESERVED_SLOTS);
+
+    obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(block.stackDepth()));
+    obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
+
+    if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx))
+        return NULL;
+
+    return &obj->asClonedBlock();
 }
+
+bool
+ClonedBlockObject::put(JSContext *cx, JSBool normalUnwind)
+{
+    StackFrame *fp = cx->fp();
+    JS_ASSERT(maybeStackFrame() == js_FloatingFrameIfGenerator(cx, fp));
+
+    uint32_t count = slotCount();
+    uint32_t depth = stackDepth();
+
+    /* The block and its locals must be on the current stack for GC safety. */
+    JS_ASSERT(depth <= uint32_t(cx->regs().sp - fp->base()));
+    JS_ASSERT(count <= uint32_t(cx->regs().sp - fp->base() - depth));
+
+    /* See comments in CheckDestructuring in frontend/Parser.cpp. */
+    JS_ASSERT(count >= 1);
+
+    if (normalUnwind)
+        copySlotRange(RESERVED_SLOTS, fp->base() + depth, count);
+
+    /* We must clear the private slot even with errors. */
+    setPrivate(NULL);
+    fp->setScopeChainNoCallObj(enclosingScope());
+    return normalUnwind;
+}
+
+static JSBool
+block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
+{
+    /*
+     * Block objects are never exposed to script, and the engine handles them
+     * with care. So unlike other getters, this one can assert (rather than
+     * check) certain invariants about obj.
+     */
+    ClonedBlockObject &block = obj->asClonedBlock();
+    uintN index = (uintN) JSID_TO_INT(id);
+    JS_ASSERT(index < block.slotCount());
+
+    if (StackFrame *fp = block.maybeStackFrame()) {
+        fp = js_LiveFrameIfGenerator(fp);
+        index += fp->numFixed() + block.stackDepth();
+        JS_ASSERT(index < fp->numSlots());
+        *vp = fp->slots()[index];
+        return true;
+    }
+
+    /* Values are in slots immediately following the class-reserved ones. */
+    JS_ASSERT(block.closedSlot(index) == *vp);
+    return true;
+}
+
+static JSBool
+block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
+{
+    ClonedBlockObject &block = obj->asClonedBlock();
+    uintN index = (uintN) JSID_TO_INT(id);
+    JS_ASSERT(index < block.slotCount());
+
+    if (StackFrame *fp = block.maybeStackFrame()) {
+        fp = js_LiveFrameIfGenerator(fp);
+        index += fp->numFixed() + block.stackDepth();
+        JS_ASSERT(index < fp->numSlots());
+        fp->slots()[index] = *vp;
+        return true;
+    }
+
+    /*
+     * The value in *vp will be written back to the slot in obj that was
+     * allocated when this let binding was defined.
+     */
+    return true;
+}
+
+StaticBlockObject *
+StaticBlockObject::create(JSContext *cx)
+{
+    RootedVarTypeObject type(cx);
+    type = cx->compartment->getEmptyType(cx);
+    if (!type)
+        return NULL;
+
+    RootedVarShape emptyBlockShape(cx);
+    emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL, FINALIZE_KIND);
+    if (!emptyBlockShape)
+        return NULL;
+
+    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyBlockShape, type, NULL);
+    if (!obj)
+        return NULL;
+
+    return &obj->asStaticBlock();
+}
+
+const Shape *
+StaticBlockObject::addVar(JSContext *cx, jsid id, intN index, bool *redeclared)
+{
+    *redeclared = false;
+
+    /* Inline JSObject::addProperty in order to trap the redefinition case. */
+    Shape **spp;
+    if (Shape::search(cx, lastProperty(), id, &spp, true)) {
+        *redeclared = true;
+        return NULL;
+    }
+
+    /*
+     * Don't convert this object to dictionary mode so that we can clone the
+     * block's shape later.
+     */
+    uint32_t slot = JSSLOT_FREE(&BlockClass) + index;
+    return addPropertyInternal(cx, id, block_getProperty, block_setProperty,
+                               slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
+                               Shape::HAS_SHORTID, index, spp,
+                               /* allowDictionary = */ false);
+}
+
+Class js::BlockClass = {
+    "Block",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
+    JSCLASS_IS_ANONYMOUS,
+    JS_PropertyStub,         /* addProperty */
+    JS_PropertyStub,         /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub
+};
+
+#if JS_HAS_XDR
+
+#define NO_PARENT_INDEX UINT32_MAX
+
+static uint32_t
+FindObjectIndex(JSObjectArray *array, JSObject *obj)
+{
+    size_t i;
+
+    if (array) {
+        i = array->length;
+        do {
+
+            if (array->vector[--i] == obj)
+                return i;
+        } while (i != 0);
+    }
+
+    return NO_PARENT_INDEX;
+}
+
+bool
+js_XDRStaticBlockObject(JSXDRState *xdr, StaticBlockObject **objp)
+{
+    JSContext *cx;
+    uint32_t parentId;
+    StaticBlockObject *obj, *parent;
+    uintN depth, count;
+    uint32_t depthAndCount;
+    const Shape *shape;
+
+    cx = xdr->cx;
+#ifdef __GNUC__
+    obj = NULL;         /* quell GCC overwarning */
+#endif
+
+    if (xdr->mode == JSXDR_ENCODE) {
+        obj = *objp;
+        parent = obj->enclosingBlock();
+        parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
+                   ? FindObjectIndex(xdr->script->objects(), parent)
+                   : NO_PARENT_INDEX;
+        depth = uint16_t(obj->stackDepth());
+        count = uint16_t(obj->slotCount());
+        depthAndCount = uint32_t(depth << 16) | count;
+    }
+#ifdef __GNUC__ /* suppress bogus gcc warnings */
+    else count = 0;
+#endif
+
+    /* First, XDR the parent atomid. */
+    if (!JS_XDRUint32(xdr, &parentId))
+        return JS_FALSE;
+
+    if (xdr->mode == JSXDR_DECODE) {
+        obj = StaticBlockObject::create(cx);
+        if (!obj)
+            return JS_FALSE;
+        *objp = obj;
+
+        /*
+         * If there's a parent id, then get the parent out of our script's
+         * object array. We know that we XDR block object in outer-to-inner
+         * order, which means that getting the parent now will work.
+         */
+        if (parentId == NO_PARENT_INDEX)
+            parent = NULL;
+        else
+            parent = &xdr->script->getObject(parentId)->asStaticBlock();
+        obj->setEnclosingBlock(parent);
+    }
+
+    AutoObjectRooter tvr(cx, obj);
+
+    if (!JS_XDRUint32(xdr, &depthAndCount))
+        return false;
+
+    if (xdr->mode == JSXDR_DECODE) {
+        depth = uint16_t(depthAndCount >> 16);
+        count = uint16_t(depthAndCount);
+        obj->setStackDepth(depth);
+
+        /*
+         * XDR the block object's properties. We know that there are 'count'
+         * properties to XDR, stored as id/shortid pairs.
+         */
+        for (uintN i = 0; i < count; i++) {
+            JSAtom *atom;
+
+            /* XDR the real id. */
+            if (!js_XDRAtom(xdr, &atom))
+                return false;
+
+            bool redeclared;
+            if (!obj->addVar(cx, ATOM_TO_JSID(atom), i, &redeclared)) {
+                JS_ASSERT(!redeclared);
+                return false;
+            }
+        }
+    } else {
+        AutoShapeVector shapes(cx);
+        shapes.growBy(count);
+
+        for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
+            shape = &r.front();
+            shapes[shape->shortid()] = shape;
+        }
+
+        /*
+         * XDR the block object's properties. We know that there are 'count'
+         * properties to XDR, stored as id/shortid pairs.
+         */
+        for (uintN i = 0; i < count; i++) {
+            shape = shapes[i];
+            JS_ASSERT(shape->getter() == block_getProperty);
+
+            jsid propid = shape->propid();
+            JS_ASSERT(JSID_IS_ATOM(propid));
+            JSAtom *atom = JSID_TO_ATOM(propid);
+
+#ifdef DEBUG
+            uint16_t shortid = uint16_t(shape->shortid());
+            JS_ASSERT(shortid == i);
+#endif
+
+            /* XDR the real id. */
+            if (!js_XDRAtom(xdr, &atom))
+                return false;
+        }
+    }
+    return true;
+}
+
+#endif  /* JS_HAS_XDR */
rename from js/src/vm/CallObject.h
rename to js/src/vm/ScopeObject.h
--- a/js/src/vm/CallObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -18,81 +18,124 @@
  *
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *   Luke Wagner <luke@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#ifndef CallObject_h___
-#define CallObject_h___
+#ifndef ScopeObject_h___
+#define ScopeObject_h___
+
+#include "jsobj.h"
 
 namespace js {
 
-class CallObject : public ::JSObject
+/*
+ * Scope objects
+ *
+ * Scope objects are technically real JSObjects but only belong on the scope
+ * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of
+ * scope objects is:
+ *
+ *   JSObject                   Generic object
+ *     \
+ *   ScopeObject                Engine-internal scope
+ *     \   \   \
+ *      \   \  DeclEnvObject    Holds name of recursive/heavyweight named lambda
+ *       \   \
+ *        \  CallObject         Scope of entire function or strict eval
+ *         \
+ *   NestedScopeObject          Scope created for a statement
+ *     \   \
+ *      \  WithObject           with
+ *       \
+ *   BlockObject                Shared interface of cloned/static block objects
+ *     \   \
+ *      \  ClonedBlockObject    let, switch, catch, for
+ *       \
+ *       StaticBlockObject      See NB
+ *
+ * This hierarchy represents more than just the interface hierarchy: reserved
+ * slots in base classes are fixed for all derived classes. Thus, for example,
+ * ScopeObject::enclosingScope() can simply access a fixed slot without further
+ * dynamic type information.
+ *
+ * NB: Static block objects are a special case: these objects are created at
+ * compile time to hold the shape/binding information from which block objects
+ * are cloned at runtime. These objects should never escape into the wild and
+ * support a restricted set of ScopeObject operations.
+ */
+
+class ScopeObject : public JSObject
 {
+    /* Use maybeStackFrame() instead. */
+    void *getPrivate() const;
+
+  protected:
+    static const uint32_t SCOPE_CHAIN_SLOT = 0;
+
+  public:
     /*
-     * Reserved slot structure for Call objects:
-     *
-     * SCOPE_CHAIN_SLOT - The enclosing scope. This must come first, for
-     *                    JSObject::scopeParent.
-     * CALLEE_SLOT      - Callee function for the stack frame, or null if
-     *                    the stack frame is for strict mode eval code.
-     * ARGUMENTS_SLOT   - Arguments object for non-strict mode eval stack
-     *                    frames (not valid for strict mode eval frames).
-     * private          - The stack frame corresponding to the Call object
-     *                    until js_PutCallObject or its on-trace analog
-     *                    is called, null thereafter.
-     *
-     * DeclEnv objects use SCOPE_CHAIN_SLOT and private in the same fashion.
+     * Since every scope chain terminates with a global object and GlobalObject
+     * does not derive ScopeObject (it has a completely different layout), the
+     * enclosing scope of a ScopeObject is necessarily non-null.
      */
+    inline JSObject &enclosingScope() const;
+    inline bool setEnclosingScope(JSContext *cx, JSObject &obj);
+
+    /*
+     * The stack frame for this scope object, if the frame is still active.
+     * Note: these members may not be called for a StaticBlockObject.
+     */
+    inline js::StackFrame *maybeStackFrame() const;
+    inline void setStackFrame(StackFrame *frame);
+
+    /* For jit access. */
+    static inline size_t offsetOfEnclosingScope();
+};
+
+class CallObject : public ScopeObject
+{
     static const uint32_t CALLEE_SLOT = 1;
     static const uint32_t ARGUMENTS_SLOT = 2;
 
   public:
-    /* Create a CallObject for the given callee function. */
-    static CallObject *
-    create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee);
-
     static const uint32_t RESERVED_SLOTS = 3;
 
-    static const uint32_t DECL_ENV_RESERVED_SLOTS = 1;
-    static const gc::AllocKind DECL_ENV_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
+    static CallObject *
+    create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee);
 
     /* True if this is for a strict mode eval frame or for a function call. */
     inline bool isForEval() const;
 
-    /* The stack frame for this CallObject, if the frame is still active. */
-    inline js::StackFrame *maybeStackFrame() const;
-    inline void setStackFrame(js::StackFrame *frame);
-
     /*
      * The callee function if this CallObject was created for a function
      * invocation, or null if it was created for a strict mode eval frame.
      */
     inline JSObject *getCallee() const;
     inline JSFunction *getCalleeFunction() const;
     inline void setCallee(JSObject *callee);
-    inline void initCallee(JSObject *callee);
 
     /* Returns the callee's arguments object. */
     inline const js::Value &getArguments() const;
     inline void setArguments(const js::Value &v);
     inline void initArguments(const js::Value &v);
 
     /* Returns the formal argument at the given index. */
     inline const js::Value &arg(uintN i) const;
@@ -110,18 +153,112 @@ class CallObject : public ::JSObject
      * contiguous slots (see NewCallObject).
      */
     inline js::HeapValueArray argArray();
     inline js::HeapValueArray varArray();
 
     inline void copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots);
 };
 
-}
+class DeclEnvObject : public ScopeObject
+{
+  public:
+    static const uint32_t RESERVED_SLOTS = 1;
+    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2;
+
+    static DeclEnvObject *create(JSContext *cx, StackFrame *fp);
+
+};
+
+class NestedScopeObject : public ScopeObject
+{
+  protected:
+    static const unsigned DEPTH_SLOT = 1;
+
+  public:
+    /* Return the abstract stack depth right before entering this nested scope. */
+    uint32_t stackDepth() const;
+};
 
-js::CallObject &
-JSObject::asCall()
+class WithObject : public NestedScopeObject
+{
+    static const unsigned THIS_SLOT = 2;
+
+    /* Use WithObject::object() instead. */
+    JSObject *getProto() const;
+
+  public:
+    static const unsigned RESERVED_SLOTS = 3;
+    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
+
+    static WithObject *
+    create(JSContext *cx, StackFrame *fp, JSObject &proto, JSObject &enclosing, uint32_t depth);
+
+    /* Return object for the 'this' class hook. */
+    JSObject &withThis() const;
+
+    /* Return the 'o' in 'with (o)'. */
+    JSObject &object() const;
+};
+
+class BlockObject : public NestedScopeObject
 {
-    JS_ASSERT(isCall());
-    return *reinterpret_cast<js::CallObject *>(this);
-}
+  public:
+    static const unsigned RESERVED_SLOTS = 2;
+    static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4;
+
+    /* Return the number of variables associated with this block. */
+    inline uint32_t slotCount() const;
+
+  protected:
+    /* Blocks contain an object slot for each slot i: 0 <= i < slotCount. */
+    inline HeapValue &slotValue(unsigned i);
+};
+
+class StaticBlockObject : public BlockObject
+{
+    /* These ScopeObject operations are not valid on a static block object. */
+    js::StackFrame *maybeStackFrame() const;
+    void setStackFrame(StackFrame *frame);
+
+  public:
+    static StaticBlockObject *create(JSContext *cx);
+
+    inline StaticBlockObject *enclosingBlock() const;
+    inline void setEnclosingBlock(StaticBlockObject *blockObj);
+
+    void setStackDepth(uint32_t depth);
 
-#endif /* CallObject_h___ */
+    /*
+     * Frontend compilation temporarily uses the object's slots to link
+     * a let var to its associated Definition parse node.
+     */
+    void setDefinitionParseNode(unsigned i, Definition *def);
+    Definition *maybeDefinitionParseNode(unsigned i);
+    void poisonDefinitionParseNode(unsigned i);
+
+    const js::Shape *addVar(JSContext *cx, jsid id, intN index, bool *redeclared);
+};
+
+class ClonedBlockObject : public BlockObject
+{
+  public:
+    static ClonedBlockObject *create(JSContext *cx, StaticBlockObject &block, StackFrame *fp);
+
+    /* The static block from which this block was cloned. */
+    StaticBlockObject &staticBlock() const;
+
+    /*
+     * When this block's stack slots are about to be popped, 'put' must be
+     * called to copy the slot values into this block's object slots.
+     */
+    bool put(JSContext *cx, JSBool normalUnwind);
+
+    /* Assuming 'put' has been called, return the value of the ith let var. */
+    const Value &closedSlot(unsigned i);
+};
+
+}  /* namespace js */
+
+extern bool
+js_XDRStaticBlockObject(JSXDRState *xdr, js::StaticBlockObject **objp);
+
+#endif /* ScopeObject_h___ */
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -39,23 +39,24 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef Stack_inl_h__
 #define Stack_inl_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
-#include "Stack.h"
+#include "methodjit/MethodJIT.h"
+#include "vm/Stack.h"
 
 #include "jsscriptinlines.h"
+
 #include "ArgumentsObject-inl.h"
-#include "CallObject-inl.h"
+#include "ScopeObject-inl.h"
 
-#include "methodjit/MethodJIT.h"
 
 namespace js {
 
 /*
  * We cache name lookup results only for the global object or for native
  * non-global objects without prototype or with prototype that never mutates,
  * see bug 462734 and bug 487039.
  */
@@ -79,17 +80,17 @@ StackFrame::scopeChain() const
     return *scopeChain_;
 }
 
 inline JSObject &
 StackFrame::varObj()
 {
     JSObject *obj = &scopeChain();
     while (!obj->isVarObj())
-        obj = obj->scopeChain();
+        obj = obj->enclosingScope();
     return *obj;
 }
 
 inline JSCompartment *
 StackFrame::compartment() const
 {
     JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment());
     return scopeChain().compartment();
@@ -372,20 +373,20 @@ inline void
 StackFrame::setScopeChainNoCallObj(JSObject &obj)
 {
 #ifdef DEBUG
     JS_ASSERT(&obj != NULL);
     if (&obj != sInvalidScopeChain) {
         if (hasCallObj()) {
             JSObject *pobj = &obj;
             while (pobj && pobj->getPrivate() != this)
-                pobj = pobj->scopeChain();
+                pobj = pobj->enclosingScope();
             JS_ASSERT(pobj);
         } else {
-            for (JSObject *pobj = &obj; pobj->isInternalScope(); pobj = pobj->scopeChain())
+            for (JSObject *pobj = &obj; pobj->isScope(); pobj = pobj->enclosingScope())
                 JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
         }
     }
 #endif
     scopeChain_ = &obj;
     flags_ |= HAS_SCOPECHAIN;
 }
 
@@ -400,17 +401,17 @@ StackFrame::setScopeChainWithOwnCallObj(
 
 inline CallObject &
 StackFrame::callObj() const
 {
     JS_ASSERT_IF(isNonEvalFunctionFrame() || isStrictEvalFrame(), hasCallObj());
 
     JSObject *pobj = &scopeChain();
     while (JS_UNLIKELY(!pobj->isCall()))
-        pobj = pobj->scopeChain();
+        pobj = pobj->enclosingScope();
     return pobj->asCall();
 }
 
 inline bool
 StackFrame::maintainNestingState() const
 {
     /*
      * Whether to invoke the nesting epilogue/prologue to maintain active
@@ -472,17 +473,17 @@ StackFrame::markFunctionEpilogueDone()
              * For function frames, the call object may or may not have have an
              * enclosing DeclEnv object, so we use the callee's parent, since
              * it was the initial scope chain. For global (strict) eval frames,
              * there is no callee, but the call object's parent is the initial
              * scope chain.
              */
             scopeChain_ = isFunctionFrame()
                           ? callee().toFunction()->environment()
-                          : scopeChain_->internalScopeChain();
+                          : &scopeChain_->asScope().enclosingScope();
             flags_ &= ~HAS_CALL_OBJ;
         }
     }
 
     /*
      * For outer/inner function frames, undo the active frame balancing so that
      * when we redo it in the epilogue we get the right final value. The other
      * nesting epilogue changes (update active args/vars) are idempotent.
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -143,23 +143,22 @@ StackFrame::stealFrameAndSlots(Value *vp
 
     /*
      * Repoint Call, Arguments, Block and With objects to the new live frame.
      * Call and Arguments are done directly because we have pointers to them.
      * Block and With objects are done indirectly through 'liveFrame'. See
      * js_LiveFrameToFloating comment in jsiter.h.
      */
     if (hasCallObj()) {
-        JSObject &obj = callObj();
-        obj.setPrivate(this);
+        CallObject &obj = callObj();
+        obj.setStackFrame(this);
         otherfp->flags_ &= ~HAS_CALL_OBJ;
         if (js_IsNamedLambda(fun())) {
-            JSObject *env = obj.internalScopeChain();
-            JS_ASSERT(env->isDeclEnv());
-            env->setPrivate(this);
+            DeclEnvObject &env = obj.enclosingScope().asDeclEnv();
+            env.setStackFrame(this);
         }
     }
     if (hasArgsObj()) {
         ArgumentsObject &argsobj = argsObj();
         if (argsobj.isNormalArguments())
             argsobj.setStackFrame(this);
         else
             JS_ASSERT(!argsobj.maybeStackFrame());
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -68,16 +68,17 @@ namespace mjit {
 }
 namespace detail { struct OOMCheck; }
 
 class CallIter;
 class FrameRegsIter;
 class AllFramesIter;
 
 class ArgumentsObject;
+class StaticBlockObject;
 
 #ifdef JS_METHODJIT
 typedef js::mjit::CallSite JSInlinedSite;
 #else
 struct JSInlinedSite {};
 #endif
 
 typedef /* js::mjit::RejoinState */ size_t JSRejoinState;
@@ -364,17 +365,17 @@ class StackFrame
         JSScript        *script;        /* eval has no args, but needs a script */
     } args;
     mutable JSObject    *scopeChain_;   /* current scope chain */
     StackFrame          *prev_;         /* previous cx->regs->fp */
     void                *ncode_;        /* return address for method JIT */
 
     /* Lazily initialized */
     Value               rval_;          /* return value of the frame */
-    JSObject            *blockChain_;   /* innermost let block */
+    StaticBlockObject   *blockChain_;   /* innermost let block */
     jsbytecode          *prevpc_;       /* pc of previous frame*/
     JSInlinedSite       *prevInline_;   /* inlined site in previous frame */
     void                *hookData_;     /* closure returned by call hook */
     void                *annotation_;   /* perhaps remove with bug 546848 */
     JSRejoinState       rejoin_;        /* If rejoining into the interpreter
                                          * from JIT code, state at rejoin. */
 
     static void staticAsserts() {
@@ -843,26 +844,26 @@ class StackFrame
     inline void setScopeChainWithOwnCallObj(CallObject &obj);
 
     /* Block chain */
 
     bool hasBlockChain() const {
         return (flags_ & HAS_BLOCKCHAIN) && blockChain_;
     }
 
-    JSObject *maybeBlockChain() {
+    StaticBlockObject *maybeBlockChain() {
         return (flags_ & HAS_BLOCKCHAIN) ? blockChain_ : NULL;
     }
 
-    JSObject &blockChain() const {
+    StaticBlockObject &blockChain() const {
         JS_ASSERT(hasBlockChain());
         return *blockChain_;
     }
 
-    void setBlockChain(JSObject *obj) {
+    void setBlockChain(StaticBlockObject *obj) {
         flags_ |= HAS_BLOCKCHAIN;
         blockChain_ = obj;
     }
 
     /*
      * Prologue for function frames: make a call object for heavyweight
      * functions, and maintain type nesting invariants.
      */
--- a/js/src/vm/StringObject-inl.h
+++ b/js/src/vm/StringObject-inl.h
@@ -38,21 +38,21 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef StringObject_inl_h___
 #define StringObject_inl_h___
 
 #include "StringObject.h"
 
-inline js::StringObject *
+inline js::StringObject &
 JSObject::asString()
 {
     JS_ASSERT(isString());
-    return static_cast<js::StringObject *>(const_cast<JSObject *>(this));
+    return *static_cast<js::StringObject *>(this);
 }
 
 namespace js {
 
 inline bool
 StringObject::init(JSContext *cx, JSString *str)
 {
     JS_ASSERT(gc::GetGCKindSlots(getAllocKind()) == 2);
@@ -77,29 +77,29 @@ StringObject::init(JSContext *cx, JSStri
 }
 
 inline StringObject *
 StringObject::create(JSContext *cx, JSString *str)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &StringClass);
     if (!obj)
         return NULL;
-    StringObject *strobj = obj->asString();
-    if (!strobj->init(cx, str))
+    StringObject &strobj = obj->asString();
+    if (!strobj.init(cx, str))
         return NULL;
-    return strobj;
+    return &strobj;
 }
 
 inline StringObject *
 StringObject::createWithProto(JSContext *cx, JSString *str, JSObject &proto)
 {
     JSObject *obj = NewObjectWithClassProto(cx, &StringClass, &proto, NULL);
     if (!obj)
         return NULL;
-    StringObject *strobj = obj->asString();
-    if (!strobj->init(cx, str))
+    StringObject &strobj = obj->asString();
+    if (!strobj.init(cx, str))
         return NULL;
-    return strobj;
+    return &strobj;
 }
 
 } // namespace js
 
 #endif /* StringObject_inl_h__ */
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -43,17 +43,17 @@
 
 #include "mozilla/Attributes.h"
 
 #include "jsobj.h"
 #include "jsstr.h"
 
 namespace js {
 
-class StringObject : public ::JSObject
+class StringObject : public JSObject
 {
     static const uintN PRIMITIVE_THIS_SLOT = 0;
     static const uintN LENGTH_SLOT = 1;
 
   public:
     static const uintN RESERVED_SLOTS = 2;
 
     /*