Merge last green PGO from mozilla-inbound to mozilla-central
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 03 Jan 2012 12:36:26 +0100
changeset 84891 cb73188eedf8255c212e5a55c1d4826c916a6f9b
parent 84880 f301341f2e02f2ad33525f616dac019dc0f1e158 (current diff)
parent 84890 6ff43da616bed93b5264b0cdf62aca4349f13006 (diff)
child 84896 a737cc816eeccdb145f4d73043d02a2d4f79c5aa
child 84907 cb346e8b07897fc24f75723cfbb3385c4757e5a4
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)
milestone12.0a1
Merge last green PGO from mozilla-inbound to mozilla-central
js/src/jsgc.cpp
js/src/jsstack.js
js/src/vm/CallObject-inl.h
js/src/vm/CallObject.cpp
js/src/vm/CallObject.h
js/src/vm/Stack.cpp
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -535,16 +535,19 @@ include $(topsrcdir)/config/rules.mk
 		test_bug702439.html \
 		test_bug702439.html^headers^ \
 		file_bug702439.html \
 		test_bug707142.html \
 		file_bug707142_baseline.json \
 		file_bug707142_bom.json \
 		file_bug707142_utf-16.json \
 		test_reentrant_flush.html \
+		test_bug708620.html \
+		file_bug708620.html \
+		file_bug708620-2.html \
 		$(NULL)
 
 _CHROME_FILES =	\
 		test_bug357450.js \
 		$(NULL)
 
 # This test fails on the Mac for some reason
 ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_bug708620-2.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Non-UTF form target</title>
+<body onload="parent.finish();">
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_bug708620.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset=windows-1252>
+<title>Non-UTF form</title>
+<body onload="document.forms[0].submit();">
+<form action="file_bug708620-2.html">
+<input name=foo value=bar>
+</form>
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug708620.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=708620
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 708620</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="start();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=708620">Mozilla Bug 708620</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 708620 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+  "file_bug708620.html"
+];
+
+var expectedErrors = [
+  '[JavaScript Warning: "A form was submitted in the windows-1252 encoding which cannot encode all Unicode characters, so user input may get corrupted. To avoid this problem, the page should be changed so that the form is submitted in the UTF-8 encoding either by changing the encoding of the page itself to UTF-8 or by specifying accept-charset=utf-8 on the form element."]'
+];
+
+function consoleError(msg) {
+  var expected = expectedErrors.shift();
+  is(msg, expected, "Not the right error message");
+}
+
+SpecialPowers.addErrorConsoleListener(consoleError);
+
+function start() {
+  var url = tests.shift();
+  document.getElementsByTagName("iframe")[0].src = url;
+}
+
+function finish() {
+    is(expectedErrors.length, 0, "The error supply was not exhausted");
+    SpecialPowers.removeErrorConsoleListener(consoleError);
+    SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -719,16 +719,27 @@ nsEncodingFormSubmission::nsEncodingForm
   }
 
   // use UTF-8 for UTF-16* (per WHATWG and existing practice of
   // MS IE/Opera). 
   if (StringBeginsWith(charset, NS_LITERAL_CSTRING("UTF-16"))) {
     charset.AssignLiteral("UTF-8");
   }
 
+  if (!(charset.EqualsLiteral("UTF-8") || charset.EqualsLiteral("gb18030"))) {
+    nsAutoString charsetUtf16;
+    CopyUTF8toUTF16(charset, charsetUtf16);
+    const PRUnichar* charsetPtr = charsetUtf16.get();
+    SendJSWarning(aOriginatingElement ? aOriginatingElement->GetOwnerDocument()
+                                      : nsnull,
+                  "CannotEncodeAllUnicode",
+                  &charsetPtr,
+                  1);
+  }
+
   mEncoder = do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID);
   if (mEncoder) {
     nsresult rv =
       mEncoder->Init(charset.get(),
                      (nsISaveAsCharset::attr_EntityAfterCharsetConv + 
                       nsISaveAsCharset::attr_FallbackDecimalNCR),
                      0);
     if (NS_FAILED(rv)) {
--- a/dom/locales/en-US/chrome/layout/HtmlForm.properties
+++ b/dom/locales/en-US/chrome/layout/HtmlForm.properties
@@ -43,8 +43,9 @@ MediaUpload=Media Upload
 # should be a space (U+0020) in most locales. The prompt is followed by an 
 # input field. The space needs be escaped in the property file to avoid 
 # trimming.
 IsIndexPromptWithSpace=This is a searchable index. Enter search keywords:\u0020
 ForgotPostWarning=Form contains enctype=%S, but does not contain method=post.  Submitting normally with method=GET and no enctype instead.
 ForgotFileEnctypeWarning=Form contains a file input, but is missing method=POST and enctype=multipart/form-data on the form.  The file will not be sent.
 # LOCALIZATION NOTE (DefaultFormSubject): %S will be replaced with brandShortName
 DefaultFormSubject=Form Post from %S
+CannotEncodeAllUnicode=A form was submitted in the %S encoding which cannot encode all Unicode characters, so user input may get corrupted. To avoid this problem, the page should be changed so that the form is submitted in the UTF-8 encoding either by changing the encoding of the page itself to UTF-8 or by specifying accept-charset=utf-8 on the form element.
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -43,16 +43,18 @@
 #ifndef jshashtable_h_
 #define jshashtable_h_
 
 #include "TemplateLib.h"
 #include "Utility.h"
 
 namespace js {
 
+class TempAllocPolicy;
+
 /* Integral types for all hash functions. */
 typedef uint32_t HashNumber;
 
 /*****************************************************************************/
 
 namespace detail {
 
 template <class T, class HashPolicy, class AllocPolicy>
@@ -953,17 +955,20 @@ struct IsPodType<HashMapEntry<K, V> >
  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
  * AllocPolicy:
  *  - see "Allocation policies" in jsalloc.h
  *
  * N.B: HashMap is not reentrant: Key/Value/HashPolicy/AllocPolicy members
  *      called by HashMap must not call back into the same HashMap object.
  * N.B: Due to the lack of exception handling, the user must call |init()|.
  */
-template <class Key, class Value, class HashPolicy, class AllocPolicy>
+template <class Key,
+          class Value,
+          class HashPolicy = DefaultHasher<Key>,
+          class AllocPolicy = TempAllocPolicy>
 class HashMap
 {
   public:
     typedef typename HashPolicy::Lookup Lookup;
 
     typedef HashMapEntry<Key, Value> Entry;
 
   private:
@@ -1195,17 +1200,17 @@ class HashMap
  *  - see "Hash policy" above (default js::DefaultHasher<Key>)
  * AllocPolicy:
  *  - see "Allocation policies" in jsalloc.h
  *
  * N.B: HashSet is not reentrant: T/HashPolicy/AllocPolicy members called by
  *      HashSet must not call back into the same HashSet object.
  * N.B: Due to the lack of exception handling, the user must call |init()|.
  */
-template <class T, class HashPolicy, class AllocPolicy>
+template <class T, class HashPolicy = DefaultHasher<T>, class AllocPolicy = TempAllocPolicy>
 class HashSet
 {
     typedef typename HashPolicy::Lookup Lookup;
 
     /* Implement HashSet in terms of HashTable. */
     struct SetOps : HashPolicy {
         typedef T KeyType;
         static const KeyType &getKey(const T &t) { return t; }
--- a/js/public/Vector.h
+++ b/js/public/Vector.h
@@ -49,17 +49,21 @@
 /* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4345)
 #endif
 
 namespace js {
 
-template <class T, size_t N, class AllocPolicy>
+class TempAllocPolicy;
+
+template <class T,
+          size_t MinInlineCapacity = 0,
+          class AllocPolicy = TempAllocPolicy>
 class Vector;
 
 /*
  * This template class provides a default implementation for vector operations
  * when the element type is not known to be a POD, as judged by IsPodType.
  */
 template <class T, size_t N, class AP, bool IsPod>
 struct VectorImpl
--- 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/jit-test/tests/basic/bug680217.js
+++ b/js/src/jit-test/tests/basic/bug680217.js
@@ -1,12 +1,13 @@
 try {
-for (var BUGNUMBER = 0, sz = Math.pow(2, 21); i < sz; i++)
-  str += '0,';
+    for (var BUGNUMBER = 0, sz = Math.pow(2, 12); i < sz; i++)
+        str += '0,';
 } catch (exc1) {}
 var str = '[';
 for (var i = 0, BUGNUMBER; i < sz; i++)
-  str += '0,'; 
-  var obj = {
+    str += '0,';
+var obj = {
     p: { __proto__: null },
-  };
-for (var i = 0, sz = Math.pow(2, 21); i < sz; i++)
-  str += '0,';
+};
+for (var i = 0; i < sz; i++)
+    str += '0,';
+gc();
--- 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);
 }
 
 }
@@ -2010,17 +2010,17 @@ struct SortComparatorStringValuePairs {
 struct SortComparatorFunction {
     JSContext          *const cx;
     const Value        &fval;
     InvokeArgsGuard    &ag;
 
     SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag)
       : cx(cx), fval(fval), ag(ag) { }
 
-    bool JS_REQUIRES_STACK operator()(const Value &a, const Value &b, bool *lessOrEqualp);
+    bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
 };
 
 bool
 SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp)
 {
     /*
      * array_sort deals with holes and undefs on its own and they should not
      * come here.
@@ -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;
 }
 
@@ -497,21 +497,18 @@ inline bool
 JSContext::ensureParseMapPool()
 {
     if (parseMapPool_)
         return true;
     parseMapPool_ = js::OffTheBooks::new_<js::ParseMapPool>(this);
     return parseMapPool_;
 }
 
-/*
- * Get the current frame, first lazily instantiating stack frames if needed.
- * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
- */
-static JS_FORCES_STACK JS_INLINE js::StackFrame *
+/* Get the current frame, first lazily instantiating stack frames if needed. */
+static inline js::StackFrame *
 js_GetTopStackFrame(JSContext *cx, FrameExpandKind expand)
 {
 #ifdef JS_METHODJIT
     if (expand)
         js::mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
     return cx->maybefp();
--- 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
@@ -2076,17 +2076,17 @@ MarkWeakReferences(GCMarker *gcmarker)
     while (WatchpointMap::markAllIteratively(gcmarker) ||
            WeakMapBase::markAllIteratively(gcmarker) ||
            Debugger::markAllIteratively(gcmarker)) {
         gcmarker->drainMarkStack();
     }
     JS_ASSERT(gcmarker->isMarkStackEmpty());
 }
 
-JS_REQUIRES_STACK void
+void
 MarkRuntime(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime;
 
     if (rt->state != JSRTS_LANDING)
         MarkConservativeStackRoots(trc);
 
     for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1363,23 +1363,23 @@ js_GCThingIsMarked(void *thing, uintN co
 extern void
 js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp);
 
 extern bool
 js_IsAddressableGCThing(JSRuntime *rt, jsuword w, js::gc::AllocKind *thingKind, void **thing);
 
 namespace js {
 
-extern JS_REQUIRES_STACK void
+extern void
 MarkRuntime(JSTracer *trc);
 
 extern void
 TraceRuntime(JSTracer *trc);
 
-extern JS_REQUIRES_STACK JS_FRIEND_API(void)
+extern JS_FRIEND_API(void)
 MarkContext(JSTracer *trc, JSContext *acx);
 
 /* Must be called with GC lock taken. */
 extern void
 TriggerGC(JSRuntime *rt, js::gcstats::Reason reason);
 
 /* Must be called with GC lock taken. */
 extern void
--- 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);
@@ -416,17 +415,17 @@ js::OnUnknownMethod(JSContext *cx, Value
 
         obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
         obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
         vp[0].setObject(*obj);
     }
     return true;
 }
 
-static JS_REQUIRES_STACK JSBool
+static JSBool
 NoSuchMethod(JSContext *cx, uintN argc, Value *vp)
 {
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, 2, &args))
         return JS_FALSE;
 
     JS_ASSERT(vp[0].isObject());
     JS_ASSERT(vp[1].isObject());
@@ -442,29 +441,29 @@ NoSuchMethod(JSContext *cx, uintN argc, 
     args[1].setObject(*argsobj);
     JSBool ok = Invoke(cx, args);
     vp[0] = args.rval();
     return ok;
 }
 
 #endif /* JS_HAS_NO_SUCH_METHOD */
 
-JS_REQUIRES_STACK bool
+bool
 js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp)
 {
     JS_ASSERT(script);
     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,80 +1082,67 @@ 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)
+/* Unwind block and scope chains to match the given depth. */
+void
+js::UnwindScope(JSContext *cx, uint32_t stackDepth)
 {
-    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()) {
-            /* Don't fail until after we've updated all stacks. */
-            normalUnwind &= js_PutBlockObject(cx, normalUnwind);
-        } else {
+        if (scopeChain.isClonedBlock())
+            scopeChain.asClonedBlock().put(cx);
+        else
             LeaveWith(cx);
-        }
     }
-
-    cx->regs().sp = fp->base() + stackDepth;
-    return normalUnwind;
 }
 
 /*
  * Find the results of incrementing or decrementing *vp. For pre-increments,
  * both *vp and *vp2 will contain the result on return. For post-increments,
  * vp will contain the original value converted to a number and vp2 will get
  * the result. Both vp and vp2 must be roots.
  */
@@ -1917,27 +1903,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 +2326,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 +2960,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 +3019,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 +3106,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 +3239,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 +3487,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 +3533,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 +3613,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 +5083,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);
+
+    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)
@@ -5390,25 +5371,18 @@ END_CASE(JSOP_ARRAYPUSH)
 
             /*
              * Set pc to the first bytecode after the the try note to point
              * to the beginning of catch or finally or to [enditer] closing
              * the for-in loop.
              */
             regs.pc = (script)->main() + tn->start + tn->length;
 
-            JSBool ok = UnwindScope(cx, tn->stackDepth, JS_TRUE);
-            JS_ASSERT(regs.sp == regs.fp()->base() + tn->stackDepth);
-            if (!ok) {
-                /*
-                 * Restart the handler search with updated pc and stack depth
-                 * to properly notify the debugger.
-                 */
-                goto error;
-            }
+            UnwindScope(cx, tn->stackDepth);
+            regs.sp = regs.fp()->base() + tn->stackDepth;
 
             switch (tn->kind) {
               case JSTRY_CATCH:
                   JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
 
 #if JS_HAS_GENERATORS
                 /* Catch cannot intercept the closing of a generator. */
                   if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
@@ -5434,17 +5408,17 @@ END_CASE(JSOP_ARRAYPUSH)
                 len = 0;
                 DO_NEXT_OP(len);
 
               case JSTRY_ITER: {
                 /* This is similar to JSOP_ENDITER in the interpreter loop. */
                 JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
                 Value v = cx->getPendingException();
                 cx->clearPendingException();
-                ok = js_CloseIterator(cx, &regs.sp[-1].toObject());
+                bool ok = js_CloseIterator(cx, &regs.sp[-1].toObject());
                 regs.sp -= 1;
                 if (!ok)
                     goto error;
                 cx->setPendingException(v);
               }
            }
         } while (++tn != tnlimit);
 
@@ -5460,25 +5434,18 @@ END_CASE(JSOP_ARRAYPUSH)
             cx->clearPendingException();
             interpReturnOK = JS_TRUE;
             regs.fp()->clearReturnValue();
         }
 #endif
     }
 
   forced_return:
-    /*
-     * Unwind the scope making sure that interpReturnOK stays false even when
-     * UnwindScope returns true.
-     *
-     * When a trap handler returns JSTRAP_RETURN, we jump here with
-     * interpReturnOK set to true bypassing any finally blocks.
-     */
-    interpReturnOK &= (JSBool)UnwindScope(cx, 0, interpReturnOK || cx->isExceptionPending());
-    JS_ASSERT(regs.sp == regs.fp()->base());
+    UnwindScope(cx, 0);
+    regs.sp = regs.fp()->base();
 
     if (entryFrame != regs.fp())
         goto inline_return;
 
   exit:
     if (cx->compartment->debugMode())
         interpReturnOK = ScriptDebugEpilogue(cx, regs.fp(), interpReturnOK);
     interpReturnOK = ScriptEpilogueOrGeneratorYield(cx, regs.fp(), interpReturnOK);
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -175,17 +175,17 @@ Invoke(JSContext *cx, const Value &thisv
 extern bool
 InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv,
                      Value *rval);
 
 /*
  * InvokeConstructor* implement a function call from a constructor context
  * (e.g. 'new') handling the the creation of the new 'this' object.
  */
-extern JS_REQUIRES_STACK bool
+extern bool
 InvokeConstructorKernel(JSContext *cx, const CallArgs &args);
 
 /* See the InvokeArgsGuard overload of Invoke. */
 inline bool
 InvokeConstructor(JSContext *cx, InvokeArgsGuard &args)
 {
     args.setActive();
     bool ok = InvokeConstructorKernel(cx, ImplicitCast<CallArgs>(args));
@@ -196,17 +196,17 @@ InvokeConstructor(JSContext *cx, InvokeA
 /* See the fval overload of Invoke. */
 extern bool
 InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval);
 
 /*
  * InvokeConstructorWithGivenThis directly calls the constructor with the given
  * 'this'; the caller must choose the semantically correct 'this'.
  */
-extern JS_REQUIRES_STACK bool
+extern bool
 InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
                                uintN argc, Value *argv, Value *rval);
 
 /*
  * Executes a script with the given scopeChain/this. The 'type' indicates
  * whether this is eval code or global code. To support debugging, the
  * evalFrame parameter can point to an arbitrary frame in the context's call
  * stack to simulate executing an eval in that frame.
@@ -228,20 +228,20 @@ enum InterpMode
     JSINTERP_REJOIN    = 3, /* as normal, but the frame has already started */
     JSINTERP_SKIP_TRAP = 4  /* as REJOIN, but skip trap at first opcode */
 };
 
 /*
  * Execute the caller-initialized frame for a user-defined script or function
  * pointed to by cx->fp until completion or error.
  */
-extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
+extern JS_NEVER_INLINE bool
 Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL);
 
-extern JS_REQUIRES_STACK bool
+extern bool
 RunScript(JSContext *cx, JSScript *script, StackFrame *fp);
 
 extern bool
 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs);
 
 extern bool
 StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal);
 
@@ -318,24 +318,24 @@ class InterpreterFrames {
     FrameRegs *regs;
     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);
+extern void
+UnwindScope(JSContext *cx, uint32_t stackDepth);
 
 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
@@ -861,17 +861,17 @@ js_ValueToIterator(JSContext *cx, uintN 
                 return false;
         }
     }
 
     return GetIterator(cx, obj, flags, vp);
 }
 
 #if JS_HAS_GENERATORS
-static JS_REQUIRES_STACK JSBool
+static JSBool
 CloseGenerator(JSContext *cx, JSObject *genobj);
 #endif
 
 JS_FRIEND_API(JSBool)
 js_CloseIterator(JSContext *cx, JSObject *obj)
 {
     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
 
@@ -1265,25 +1265,25 @@ Class js::GeneratorClass = {
 /*
  * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
  * to the frame by which the generator function was activated.  Create a new
  * JSGenerator object, which contains its own StackFrame that we populate
  * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
  * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
  * if they are non-null.
  */
-JS_REQUIRES_STACK JSObject *
+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. */
@@ -1335,17 +1335,17 @@ typedef enum JSGeneratorOp {
     JSGENOP_THROW,
     JSGENOP_CLOSE
 } JSGeneratorOp;
 
 /*
  * Start newborn or restart yielding generator and perform the requested
  * operation inside its frame.
  */
-static JS_REQUIRES_STACK JSBool
+static JSBool
 SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
                 JSGenerator *gen, const Value &arg)
 {
     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
         js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
                             JSDVG_SEARCH_STACK, ObjectOrNullValue(obj),
                             JS_GetFunctionId(gen->floatingFrame()->fun()));
         return JS_FALSE;
@@ -1441,17 +1441,17 @@ SendToGenerator(JSContext *cx, JSGenerat
 
     /*
      * An error, silent termination by operation callback or an exception.
      * Propagate the condition to the caller.
      */
     return JS_FALSE;
 }
 
-static JS_REQUIRES_STACK JSBool
+static JSBool
 CloseGenerator(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isGenerator());
 
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
     if (!gen) {
         /* Generator prototype object. */
         return JS_TRUE;
@@ -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));
@@ -3180,17 +3180,17 @@ js_CreateThisForFunction(JSContext *cx, 
     return obj;
 }
 
 /*
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "object-detecting" in the sense used by web scripts, e.g., when
  * checking whether document.all is defined.
  */
-JS_REQUIRES_STACK JSBool
+JSBool
 Detecting(JSContext *cx, jsbytecode *pc)
 {
     jsbytecode *endpc;
     JSOp op;
     JSAtom *atom;
 
     JSScript *script = cx->stack.currentScript();
     endpc = script->code + script->length;
@@ -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))
@@ -1820,17 +1784,17 @@ js_FindPropertyHelper(JSContext *cx, jsi
 /*
  * Search for id either on the current scope chain or on the scope chain's
  * global object, per the global parameter.
  */
 extern JS_FRIEND_API(JSBool)
 js_FindProperty(JSContext *cx, jsid id, bool global,
                 JSObject **objp, JSObject **pobjp, JSProperty **propp);
 
-extern JS_REQUIRES_STACK JSObject *
+extern JSObject *
 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id);
 
 extern JSObject *
 js_FindVariableScope(JSContext *cx, JSFunction **funp);
 
 /*
  * JSGET_CACHE_RESULT is the analogue of JSDNP_CACHE_RESULT for js_GetMethod.
  *
@@ -1983,17 +1947,17 @@ obj_toStringHelper(JSContext *cx, JSObje
 extern JSBool
 eval(JSContext *cx, uintN argc, Value *vp);
 
 /*
  * Performs a direct eval for the given arguments, which must correspond to the
  * currently-executing stack frame, which must be a script frame. On completion
  * the result is returned in args.rval.
  */
-extern JS_REQUIRES_STACK bool
+extern bool
 DirectEval(JSContext *cx, const CallArgs &args);
 
 /*
  * True iff |v| is the built-in eval function for the global object that
  * corresponds to |scopeChain|.
  */
 extern bool
 IsBuiltinEvalForScope(JSObject *scopeChain, const js::Value &v);
--- 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
@@ -41,17 +41,17 @@
 #include "jspropertycache.h"
 #include "jscntxt.h"
 #include "jsnum.h"
 #include "jsobjinlines.h"
 #include "jspropertycacheinlines.h"
 
 using namespace js;
 
-JS_REQUIRES_STACK PropertyCacheEntry *
+PropertyCacheEntry *
 PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *pobj,
                     const Shape *shape)
 {
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(!cx->runtime->gcRunning);
 
     /*
      * Check for fill from js_SetPropertyHelper where the setter removed shape
@@ -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()) {
@@ -171,17 +171,17 @@ GetAtomFromBytecode(JSContext *cx, jsbyt
 
     JSScript *script = cx->stack.currentScript();
     ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
     JSAtom *atom;
     GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom);
     return atom;
 }
 
-JS_REQUIRES_STACK JSAtom *
+JSAtom *
 PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
                         PropertyCacheEntry *entry)
 {
     JSObject *obj, *pobj, *tmp;
 #ifdef DEBUG
     JSScript *script = cx->stack.currentScript();
 #endif
 
@@ -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/jspropertycache.h
+++ b/js/src/jspropertycache.h
@@ -175,29 +175,29 @@ class PropertyCache
     static inline jsuword
     hash(jsbytecode *pc, const Shape *kshape)
     {
         return (((jsuword(pc) >> SIZE_LOG2) ^ jsuword(pc) ^ ((jsuword)kshape >> 3)) & MASK);
     }
 
     static inline bool matchShape(JSContext *cx, JSObject *obj, uint32_t shape);
 
-    JS_REQUIRES_STACK JSAtom *fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
-                                       JSObject **pobjp, PropertyCacheEntry *entry);
+    JSAtom *fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
+                     JSObject **pobjp, PropertyCacheEntry *entry);
 
 #ifdef DEBUG
     void assertEmpty();
 #else
     inline void assertEmpty() {}
 #endif
 
   public:
-    JS_ALWAYS_INLINE JS_REQUIRES_STACK void test(JSContext *cx, jsbytecode *pc,
-                                                 JSObject *&obj, JSObject *&pobj,
-                                                 PropertyCacheEntry *&entry, JSAtom *&atom);
+    JS_ALWAYS_INLINE void test(JSContext *cx, jsbytecode *pc,
+                               JSObject *&obj, JSObject *&pobj,
+                               PropertyCacheEntry *&entry, JSAtom *&atom);
 
     /*
      * Test for cached information about a property set on *objp at pc.
      *
      * On a hit, set *entryp to the entry and return true.
      *
      * On a miss, set *atomp to the name of the property being set and return false.
      */
@@ -208,18 +208,18 @@ class PropertyCache
     /*
      * Fill property cache entry for key cx->fp->pc, optimized value word
      * computed from obj and shape, and entry capability forged from 24-bit
      * obj->shape(), 4-bit scopeIndex, and 4-bit protoIndex.
      *
      * Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was
      * not possible.
      */
-    JS_REQUIRES_STACK PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, uintN scopeIndex,
-                                               JSObject *pobj, const js::Shape *shape);
+    PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, uintN scopeIndex,
+                             JSObject *pobj, const js::Shape *shape);
 
     void purge(JSContext *cx);
 
     /* Restore an entry that may have been purged during a GC. */
     void restore(PropertyCacheEntry *entry);
 };
 
 } /* namespace js */
--- 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
@@ -50,18 +50,22 @@
  * declaring a pointer to struct type, or defining a member of struct type.
  *
  * A few fundamental scalar types are defined here too.  Neither the scalar
  * nor the struct typedefs should change much, therefore the nearly-global
  * make dependency induced by this file should not prove painful.
  */
 
 #include "jsapi.h"
+#include "jsutil.h"
 
-#include "jsutil.h"
+#ifdef __cplusplus
+#include "js/HashTable.h"
+#include "js/Vector.h"
+#endif
 
 JS_BEGIN_EXTERN_C
 
 /*
  * Convenience constants.
  */
 #define JS_BITS_PER_UINT32_LOG2 5
 #define JS_BITS_PER_UINT32      32
@@ -183,35 +187,16 @@ class ProxyHandler;
 class Wrapper;
 class CrossCompartmentWrapper;
 
 class TempAllocPolicy;
 class RuntimeAllocPolicy;
 
 class GlobalObject;
 
-template <class T,
-          size_t MinInlineCapacity = 0,
-          class AllocPolicy = TempAllocPolicy>
-class Vector;
-
-template <class>
-struct DefaultHasher;
-
-template <class Key,
-          class Value,
-          class HashPolicy = DefaultHasher<Key>,
-          class AllocPolicy = TempAllocPolicy>
-class HashMap;
-
-template <class T,
-          class HashPolicy = DefaultHasher<T>,
-          class AllocPolicy = TempAllocPolicy>
-class HashSet;
-
 template <typename K,
           typename V,
           size_t InlineElems>
 class InlineMap;
 
 class LifoAlloc;
 
 class PropertyCache;
@@ -326,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
deleted file mode 100644
--- a/js/src/jsstack.js
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Check that only JS_REQUIRES_STACK/JS_FORCES_STACK functions, and functions
- * that have called a JS_FORCES_STACK function, access cx->fp directly or
- * indirectly.
- */
-
-require({ after_gcc_pass: 'cfg' });
-include('gcc_util.js');
-include('unstable/adts.js');
-include('unstable/analysis.js');
-include('unstable/lazy_types.js');
-include('unstable/esp.js');
-
-var Zero_NonZero = {};
-include('unstable/zero_nonzero.js', Zero_NonZero);
-
-// Tell MapFactory we don't need multimaps (a speed optimization).
-MapFactory.use_injective = true;
-
-/*
- * There are two regions in the program: RED and GREEN.  Functions and member
- * variables may be declared RED in the C++ source.  GREEN is the default.
- *
- * RED signals danger.  A GREEN part of a function must not call a RED function
- * or access a RED member.
- *
- * The body of a RED function is all red.  The body of a GREEN function is all
- * GREEN by default, but parts dominated by a call to a TURN_RED function are
- * red.  This way GREEN functions can safely access RED stuff by calling a
- * TURN_RED function as preparation.
- *
- * The analysis does not attempt to prove anything about the body of a TURN_RED
- * function.  (Both annotations are trusted; only unannotated code is checked
- * for errors.)
- */
-const RED = 'JS_REQUIRES_STACK';
-const TURN_RED = 'JS_FORCES_STACK';
-const IGNORE_ERRORS = 'JS_IGNORE_STACK';
-
-function attrs(tree) {
-  let a = DECL_P(tree) ? DECL_ATTRIBUTES(tree) : TYPE_ATTRIBUTES(tree);
-  return translate_attributes(a);
-}
-
-function hasUserAttribute(tree, attrname) {
-  let attributes = attrs(tree);
-  if (attributes) {
-    for (let i = 0; i < attributes.length; i++) {
-      let attr = attributes[i];
-      if (attr.name == 'user' && attr.value.length == 1 && attr.value[0] == attrname)
-        return true;
-    }
-  }
-  return false;
-}
-
-function process_tree_type(d)
-{
-  let t = dehydra_convert(d);
-  if (t.isFunction)
-    return;
-
-  if (t.typedef !== undefined)
-    if (isRed(TYPE_NAME(d)))
-      warning("Typedef declaration is annotated JS_REQUIRES_STACK: the annotation should be on the type itself", t.loc);
-  
-  if (hasAttribute(t, RED)) {
-    warning("Non-function is annotated JS_REQUIRES_STACK", t.loc);
-    return;
-  }
-  
-  for (let st = t; st !== undefined && st.isPointer; st = st.type) {
-    if (hasAttribute(st, RED)) {
-      warning("Non-function is annotated JS_REQUIRES_STACK", t.loc);
-      return;
-    }
-    
-    if (st.parameters)
-      return;
-  }
-}
-
-function process_tree_decl(d)
-{
-  // For VAR_DECLs, walk the DECL_INITIAL looking for bad assignments
-  if (TREE_CODE(d) != VAR_DECL)
-    return;
-  
-  let i = DECL_INITIAL(d);
-  if (!i)
-    return;
-  
-  assignCheck(i, TREE_TYPE(d), function() { return location_of(d); });
-
-  functionPointerWalk(i, d);
-}
-
-/*
- * x is an expression or decl.  These functions assume that 
- */
-function isRed(x) { return hasUserAttribute(x, RED); }
-function isTurnRed(x) { return hasUserAttribute(x, TURN_RED); }
-
-function process_tree(fndecl)
-{
-  if (hasUserAttribute(fndecl, IGNORE_ERRORS))
-    return;
-
-  if (!(isRed(fndecl) || isTurnRed(fndecl))) {
-    // Ordinarily a user of ESP runs the analysis, then generates output based
-    // on the results.  But in our case (a) we need sub-basic-block resolution,
-    // which ESP doesn't keep; (b) it so happens that even though ESP can
-    // iterate over blocks multiple times, in our case that won't cause
-    // spurious output.  (It could cause us to the same error message each time
-    // through--but that's easily avoided.)  Therefore we generate the output
-    // while the ESP analysis is running.
-    let a = new RedGreenCheck(fndecl, 0);
-    if (a.hasRed)
-      a.run();
-  }
-  
-  functionPointerCheck(fndecl);
-}
-
-function RedGreenCheck(fndecl, trace) {
-  //print("RedGreenCheck: " + fndecl.toCString());
-  this._fndecl = fndecl;
-
-  // Tell ESP that fndecl is a "property variable".  This makes ESP track it in
-  // a flow-sensitive way.  The variable will be 1 in RED regions and "don't
-  // know" in GREEN regions.  (We are technically lying to ESP about fndecl
-  // being a variable--what we really want is a synthetic variable indicating
-  // RED/GREEN state, but ESP operates on GCC decl nodes.)
-  this._state_var_decl = fndecl;
-  let state_var = new ESP.PropVarSpec(this._state_var_decl, true, undefined);
-
-  // Call base class constructor.
-  let cfg = function_decl_cfg(fndecl);
-  ESP.Analysis.apply(this, [cfg, [state_var], Zero_NonZero.meet, trace]);
-  this.join = Zero_NonZero.join;
-
-  // Preprocess all instructions in the cfg to determine whether this analysis
-  // is necessary and gather some information we'll use later.
-  //
-  // Each isn may include a function call, an assignment, and/or some reads.
-  // Using walk_tree to walk the isns is a little crazy but robust.
-  //
-  this.hasRed = false;
-  let self = this;         // Allow our 'this' to be accessed inside closure
-  for (let bb in cfg_bb_iterator(cfg)) {
-    for (let isn in bb_isn_iterator(bb)) {
-      // Treehydra objects don't support reading never-defined properties
-      // as undefined, so we have to explicitly initialize anything we want
-      // to check for later.
-      isn.redInfo = undefined;
-      walk_tree(isn, function(t, stack) {
-        function getLocation(skiptop) {
-          if (!skiptop) {
-            let loc = location_of(t);
-            if (loc !== undefined)
-              return loc;
-          }
-          
-          for (let i = stack.length - 1; i >= 0; --i) {
-            let loc = location_of(stack[i]);
-            if (loc !== undefined)
-              return loc;
-          }
-          return location_of(fndecl);
-        }
-                  
-        switch (TREE_CODE(t)) {
-          case FIELD_DECL:
-            if (isRed(t)) {
-              let varName = dehydra_convert(t).name;
-              // location_of(t) is the location of the declaration.
-              isn.redInfo = ["cannot access JS_REQUIRES_STACK variable " + varName,
-                             getLocation(true)];
-              self.hasRed = true;
-            }
-            break;
-          case GIMPLE_CALL:
-          {
-            let callee = gimple_call_fndecl(t);
-            if (callee) {
-              if (isRed(callee)) {
-                let calleeName = dehydra_convert(callee).name;
-                isn.redInfo = ["cannot call JS_REQUIRES_STACK function " + calleeName,
-                              getLocation(false)];
-                self.hasRed = true;
-              } else if (isTurnRed(callee)) {
-                isn.turnRed = true;
-              }
-            }
-            else {
-              let fntype = TREE_CHECK(
-                TREE_TYPE( // the function type
-                  TREE_TYPE( // the function pointer type
-                    gimple_call_fn(t)
-                  )
-                ),
-                FUNCTION_TYPE, METHOD_TYPE);
-              if (isRed(fntype)) {
-                isn.redInfo = ["cannot call JS_REQUIRES_STACK function pointer",
-                               getLocation(false)];
-                self.hasRed = true;
-              }
-              else if (isTurnRed(fntype)) {
-                isn.turnRed = true;
-              }
-            }
-          }
-          break;
-        }
-      });
-    }
-  }
-
-  // Initialize mixin for infeasible-path elimination.
-  this._zeroNonzero = new Zero_NonZero.Zero_NonZero();
-}
-
-RedGreenCheck.prototype = new ESP.Analysis;
-
-RedGreenCheck.prototype.flowStateCond = function(isn, truth, state) {
-  // forward event to mixin
-  this._zeroNonzero.flowStateCond(isn, truth, state);
-};
-
-RedGreenCheck.prototype.flowState = function(isn, state) {
-  // forward event to mixin
-  //try { // The try/catch here is a workaround for some baffling bug in zero_nonzero.
-    this._zeroNonzero.flowState(isn, state);
-  //} catch (exc) {
-  //  warning(exc, location_of(isn));
-  //  warning("(Remove the workaround in jsstack.js and recompile to get a JS stack trace.)",
-  //          location_of(isn));
-  //}
-  let stackState = state.get(this._state_var_decl);
-  let green = stackState != 1 && stackState != ESP.NOT_REACHED;
-  let redInfo = isn.redInfo;
-  if (green && redInfo) {
-    warning(redInfo[0], redInfo[1]);
-    isn.redInfo = undefined;  // avoid duplicate messages about this instruction
-  }
-
-  // If we call a TURNS_RED function, it doesn't take effect until after the
-  // whole isn finishes executing (the most conservative rule).
-  if (isn.turnRed)
-    state.assignValue(this._state_var_decl, 1, isn);
-};
-
-function followTypedefs(type)
-{
-  while (type.typedef !== undefined)
-    type = type.typedef;
-  return type;
-}
-
-function assignCheck(source, destType, locfunc)
-{
-  if (TREE_CODE(destType) != POINTER_TYPE)
-    return;
-    
-  let destCode = TREE_CODE(TREE_TYPE(destType));
-  if (destCode != FUNCTION_TYPE && destCode != METHOD_TYPE)
-    return;
-  
-  if (isRed(TREE_TYPE(destType)))
-    return;
-
-  while (TREE_CODE(source) == NOP_EXPR)
-    source = source.operands()[0];
-  
-  // The destination is a green function pointer
-
-  if (TREE_CODE(source) == ADDR_EXPR) {
-    let sourcefn = source.operands()[0];
-    
-    // oddly enough, SpiderMonkey assigns the address of something that's not
-    // a function to a function pointer as part of the API! See JS_TN
-    if (TREE_CODE(sourcefn) != FUNCTION_DECL)
-      return;
-    
-    if (isRed(sourcefn))
-      warning("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function " + dehydra_convert(sourcefn).name, locfunc());
-  } else if (TREE_TYPE(source).tree_code() == POINTER_TYPE) {
-    let sourceType = TREE_TYPE(TREE_TYPE(source));
-    switch (TREE_CODE(sourceType)) {
-      case FUNCTION_TYPE:
-      case METHOD_TYPE:
-        if (isRed(sourceType))
-          warning("Assigning non-JS_REQUIRES_STACK function pointer from JS_REQUIRES_STACK function pointer", locfunc());
-        break;
-    }
-  }
-}
-
-/**
- * A type checker which verifies that a red function pointer is never converted
- * to a green function pointer.
- */
-
-function functionPointerWalk(t, baseloc)
-{
-  walk_tree(t, function(t, stack) {
-    function getLocation(skiptop) {
-      if (!skiptop) {
-        let loc = location_of(t);
-        if (loc !== undefined)
-          return loc;
-      }
-          
-      for (let i = stack.length - 1; i >= 0; --i) {
-        let loc = location_of(stack[i]);
-        if (loc !== undefined)
-          return loc;
-      }
-      return location_of(baseloc);
-    }
-                  
-    switch (TREE_CODE(t)) {
-      case GIMPLE_ASSIGN: {
-        let [dest, source] = t.operands();
-        assignCheck(source, TREE_TYPE(dest), getLocation);
-        break;
-      }
-      case CONSTRUCTOR: {
-        let ttype = TREE_TYPE(t);
-        switch (TREE_CODE(ttype)) {
-          case RECORD_TYPE:
-          case UNION_TYPE: {
-            for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t)))
-              assignCheck(ce.value, TREE_TYPE(ce.index), getLocation);
-            break;
-          }
-          case ARRAY_TYPE: {
-            let eltype = TREE_TYPE(ttype);
-            for each (let ce in VEC_iterate(CONSTRUCTOR_ELTS(t)))
-              assignCheck(ce.value, eltype, getLocation);
-            break;
-          }
-          case LANG_TYPE:
-            // these can be safely ignored
-            break;
-          default:
-            warning("Unexpected type in initializer: " + TREE_CODE(TREE_TYPE(t)), getLocation());
-        }
-        break;
-      }
-      case GIMPLE_CALL: {
-        // Check that the arguments to a function and the declared types
-        // of those arguments are compatible.
-        let ops = t.operands();
-        let funcType = TREE_TYPE( // function type
-          TREE_TYPE(ops[1])); // function pointer type
-        let argTypes = [t for (t in function_type_args(funcType))];
-        for (let i = argTypes.length - 1; i >= 0; --i) {
-          let destType = argTypes[i];
-          let source = ops[i + 3];
-          assignCheck(source, destType, getLocation);
-        }
-        break;
-      }
-    }
-  });
-}
-  
-function functionPointerCheck(fndecl)
-{
-  let cfg = function_decl_cfg(fndecl);
-  for (let bb in cfg_bb_iterator(cfg))
-    for (let isn in bb_isn_iterator(bb))
-      functionPointerWalk(isn, fndecl);
-}
--- 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/jstypes.h
+++ b/js/src/jstypes.h
@@ -152,35 +152,16 @@
 #ifndef JS_WARN_UNUSED_RESULT
 # if defined __GNUC__
 #  define JS_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
 # else
 #  define JS_WARN_UNUSED_RESULT
 # endif
 #endif
 
-#ifdef NS_STATIC_CHECKING
-/*
- * Attributes for static analysis. Functions declared with JS_REQUIRES_STACK
- * always have a valid cx->fp and can access it freely.  Other functions can
- * access cx->fp only after calling a function that "forces" the stack
- * (i.e. lazily instantiates it as needed).
- */
-# define JS_REQUIRES_STACK   __attribute__((user("JS_REQUIRES_STACK")))
-# define JS_FORCES_STACK     __attribute__((user("JS_FORCES_STACK")))
-/*
- * Skip the JS_REQUIRES_STACK analysis within functions with this annotation.
- */
-# define JS_IGNORE_STACK     __attribute__((user("JS_IGNORE_STACK")))
-#else
-# define JS_REQUIRES_STACK
-# define JS_FORCES_STACK
-# define JS_IGNORE_STACK
-#endif
-
 /***********************************************************************
 ** MACROS:      JS_BEGIN_MACRO
 **              JS_END_MACRO
 ** DESCRIPTION:
 **      Macro body brackets so that macros with compound statement definitions
 **      behave syntactically more like functions when called.
 ***********************************************************************/
 #define JS_BEGIN_MACRO  do {
--- 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
@@ -106,20 +106,21 @@ top:
             //       exception, in which case this would not be the right handler.
             //       But the first ops of exception handlers generated by our
             //       bytecode compiler cannot throw, so this is not possible.
             if (offset - tn->start > tn->length)
                 continue;
             if (tn->stackDepth > cx->regs().sp - fp->base())
                 continue;
 
+            UnwindScope(cx, tn->stackDepth);
+
             jsbytecode *pc = script->main() + tn->start + tn->length;
             cx->regs().pc = pc;
-            JSBool ok = UnwindScope(cx, tn->stackDepth, JS_TRUE);
-            JS_ASSERT(cx->regs().sp == fp->base() + tn->stackDepth);
+            cx->regs().sp = fp->base() + tn->stackDepth;
 
             switch (tn->kind) {
                 case JSTRY_CATCH:
                   JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
 
 #if JS_HAS_GENERATORS
                   /* Catch cannot intercept the closing of a generator. */
                   if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
@@ -151,17 +152,17 @@ top:
                    * except the code now uses the stack slot normally used by
                    * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
                    * adjustment and regs.sp[1] after, to save and restore the
                    * pending exception.
                    */
                   Value v = cx->getPendingException();
                   JS_ASSERT(JSOp(*pc) == JSOP_ENDITER);
                   cx->clearPendingException();
-                  ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject());
+                  bool ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject());
                   cx->regs().sp -= 1;
                   if (!ok)
                       goto top;
                   cx->setPendingException(v);
                 }
             }
         }
     }
@@ -574,17 +575,18 @@ js_InternalThrow(VMFrame &f)
             break;
 
         // The JIT guarantees that ScriptEpilogue() has always been run
         // upon exiting to its caller. This is important for consistency,
         // where execution modes make similar guarantees about prologues
         // and epilogues. RunTracer(), Interpret(), and Invoke() all
         // rely on this property.
         JS_ASSERT(!f.fp()->finishedInInterpreter());
-        UnwindScope(cx, 0, cx->isExceptionPending());
+        UnwindScope(cx, 0);
+        f.regs.sp = f.fp()->base();
 
         if (cx->compartment->debugMode())
             js::ScriptDebugEpilogue(cx, f.fp(), false);
 
         ScriptEpilogue(f.cx, f.fp(), false);
 
         // Don't remove the last frame, this is the responsibility of
         // JaegerShot()'s caller. We only guarantee that ScriptEpilogue()
@@ -625,26 +627,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);
 
-    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,651 @@ 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();
 }
+
+void
+ClonedBlockObject::put(JSContext *cx)
+{
+    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);
+
+    copySlotRange(RESERVED_SLOTS, fp->base() + depth, count);
+
+    /* We must clear the private slot even with errors. */
+    setPrivate(NULL);
+    fp->setScopeChainNoCallObj(enclosingScope());
+}
+
+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.
+     */
+    void put(JSContext *cx);
+
+    /* 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;
 
     /*
--- a/js/src/vm/Unicode.h
+++ b/js/src/vm/Unicode.h
@@ -34,17 +34,19 @@
  * 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 Unicode_h__
 #define Unicode_h__
 
-#include "jsstr.h"
+#include "mozilla/StdInt.h"
+
+#include "jspubtd.h"
 
 #ifdef DEBUG
 #include <stdio.h> /* For EOF */
 #endif
 
 extern const bool js_isidstart[];
 extern const bool js_isident[];
 extern const bool js_isspace[];
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -148,29 +148,20 @@ public class PanZoomController
     }
 
     private PanZoomState mState;
 
     private boolean mOverridePanning;
     private boolean mOverrideScrollAck;
     private boolean mOverrideScrollPending;
 
-    /* The current frame of the bounce-back animation, or -1 if the animation is not running. */
-    private int mBounceFrame;
-    /*
-     * The viewport metrics that represent the start and end of the bounce-back animation,
-     * respectively.
-     */
-    private ViewportMetrics mBounceStartMetrics, mBounceEndMetrics;
-
     public PanZoomController(LayerController controller) {
         mController = controller;
         mX = new AxisX(); mY = new AxisY();
         mState = PanZoomState.NOTHING;
-        mBounceFrame = -1;
 
         GeckoAppShell.registerGeckoEventListener("Browser:ZoomToRect", this);
         GeckoAppShell.registerGeckoEventListener("Browser:ZoomToPageWidth", this);
         GeckoAppShell.registerGeckoEventListener("Panning:Override", this);
         GeckoAppShell.registerGeckoEventListener("Panning:CancelOverride", this);
         GeckoAppShell.registerGeckoEventListener("Gesture:ScrollAck", this);
     }
 
@@ -513,29 +504,27 @@ public class PanZoomController
 
         startAnimationTimer(new FlingRunnable());
     }
 
     /* Performs a bounce-back animation to the given viewport metrics. */
     private void bounce(ViewportMetrics metrics) {
         stopAnimationTimer();
 
-        mBounceStartMetrics = new ViewportMetrics(mController.getViewportMetrics());
-        if (mBounceStartMetrics.fuzzyEquals(metrics)) {
+        ViewportMetrics bounceStartMetrics = new ViewportMetrics(mController.getViewportMetrics());
+        if (bounceStartMetrics.fuzzyEquals(metrics)) {
             mState = PanZoomState.NOTHING;
             return;
         }
 
-        mBounceFrame = 0;
         mState = PanZoomState.FLING;
         mX.setFlingState(Axis.FlingStates.SNAPPING); mY.setFlingState(Axis.FlingStates.SNAPPING);
-        mBounceEndMetrics = metrics;
-        Log.d(LOGTAG, "end bounce at " + mBounceEndMetrics);
+        Log.d(LOGTAG, "end bounce at " + metrics);
 
-        startAnimationTimer(new BounceRunnable());
+        startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
     }
 
     /* Performs a bounce-back animation to the nearest valid viewport metrics. */
     private void bounce() {
         bounce(getValidViewportMetrics());
     }
 
     /* Starts the fling or bounce animation. */
@@ -592,16 +581,30 @@ public class PanZoomController
             }
         }
 
         mX.displacement = mY.displacement = 0;
     }
 
     /* The callback that performs the bounce animation. */
     private class BounceRunnable implements Runnable {
+        /* The current frame of the bounce-back animation */
+        private int mBounceFrame;
+        /*
+         * The viewport metrics that represent the start and end of the bounce-back animation,
+         * respectively.
+         */
+        private ViewportMetrics mBounceStartMetrics;
+        private ViewportMetrics mBounceEndMetrics;
+
+        BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
+            mBounceStartMetrics = startMetrics;
+            mBounceEndMetrics = endMetrics;
+        }
+
         public void run() {
             /*
              * The pan/zoom controller might have signaled to us that it wants to abort the
              * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
              * out.
              */
             if (mState != PanZoomState.FLING) {
                 finishAnimation();
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -934,17 +934,17 @@ public abstract class TreeBuilder<T> imp
             }
         }
 
         // optimize the most common case
         switch (mode) {
             case IN_BODY:
             case IN_CELL:
             case IN_CAPTION:
-                if (!isInForeignButNotHtmlIntegrationPoint()) {
+                if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
                     reconstructTheActiveFormattingElements();
                 }
                 // fall through
             case TEXT:
                 accumulateCharacters(buf, start, length);
                 return;
             case IN_TABLE:
             case IN_TABLE_BODY:
@@ -993,17 +993,17 @@ public abstract class TreeBuilder<T> imp
                                                 - start);
                                         start = i;
                                     }
 
                                     /*
                                      * Reconstruct the active formatting
                                      * elements, if any.
                                      */
-                                    if (!isInForeignButNotHtmlIntegrationPoint()) {
+                                    if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
                                         flushCharacters();
                                         reconstructTheActiveFormattingElements();
                                     }
                                     /*
                                      * Append the token's character to the
                                      * current node.
                                      */
                                     break charactersloop;
@@ -1190,17 +1190,17 @@ public abstract class TreeBuilder<T> imp
                                         accumulateCharacters(buf, start, i
                                                 - start);
                                         start = i;
                                     }
                                     /*
                                      * Reconstruct the active formatting
                                      * elements, if any.
                                      */
-                                    if (!isInForeignButNotHtmlIntegrationPoint()) {
+                                    if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
                                         flushCharacters();
                                         reconstructTheActiveFormattingElements();
                                     }
                                     /*
                                      * Append the token's character to the
                                      * current node.
                                      */
                                     break charactersloop;
@@ -1305,21 +1305,17 @@ public abstract class TreeBuilder<T> imp
      * @see nu.validator.htmlparser.common.TokenHandler#zeroOriginatingReplacementCharacter()
      */
     public void zeroOriginatingReplacementCharacter() throws SAXException {
         if (mode == TEXT) {
             accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
             return;
         }
         if (currentPtr >= 0) {
-            StackNode<T> stackNode = stack[currentPtr];
-            if (stackNode.ns == "http://www.w3.org/1999/xhtml") {
-                return;
-            }
-            if (stackNode.isHtmlIntegrationPoint()) {
+            if (isSpecialParentInForeign(stack[currentPtr])) {
                 return;
             }
             accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
         }
     }
 
     public final void eof() throws SAXException {
         flushCharacters();
@@ -2281,40 +2277,26 @@ public abstract class TreeBuilder<T> imp
                                 }
                                 reconstructTheActiveFormattingElements();
                                 appendToCurrentNodeAndPushElementMayFoster(
                                         elementName,
                                         attributes);
                                 attributes = null; // CPP
                                 break starttagloop;
                             case RT_OR_RP:
-                                /*
-                                 * If the stack of open elements has a ruby
-                                 * element in scope, then generate implied end
-                                 * tags. If the current node is not then a ruby
-                                 * element, this is a parse error; pop all the
-                                 * nodes from the current node up to the node
-                                 * immediately before the bottommost ruby
-                                 * element on the stack of open elements.
-                                 * 
-                                 * Insert an HTML element for the token.
-                                 */
                                 eltPos = findLastInScope("ruby");
                                 if (eltPos != NOT_FOUND_ON_STACK) {
                                     generateImpliedEndTags();
                                 }
                                 if (eltPos != currentPtr) {
                                     if (eltPos != NOT_FOUND_ON_STACK) {
                                         errStartTagSeenWithoutRuby(name);
                                     } else {
                                         errUnclosedChildrenInRuby();
                                     }
-                                    while (currentPtr > eltPos) {
-                                        pop();
-                                    }
                                 }
                                 appendToCurrentNodeAndPushElementMayFoster(
                                         elementName,
                                         attributes);
                                 attributes = null; // CPP
                                 break starttagloop;
                             case MATH:
                                 reconstructTheActiveFormattingElements();
@@ -5289,20 +5271,21 @@ public abstract class TreeBuilder<T> imp
         return isInForeign();
     }
     
     private boolean isInForeign() {
         return currentPtr >= 0
                 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml";
     }
 
-    private boolean isInForeignButNotHtmlIntegrationPoint() {
-        return currentPtr >= 0
-                && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml"
-                && !stack[currentPtr].isHtmlIntegrationPoint();
+    private boolean isInForeignButNotHtmlOrMathTextIntegrationPoint() {
+        if (currentPtr < 0) {
+            return false;
+        }
+        return !isSpecialParentInForeign(stack[currentPtr]);
     }
 
     /**
      * The argument MUST be an interned string or <code>null</code>.
      * 
      * @param context
      */
     public final void setFragmentContext(@Local String context,
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -195,17 +195,17 @@ nsHtml5TreeBuilder::characters(const PRU
         return;
       }
     }
   }
   switch(mode) {
     case NS_HTML5TREE_BUILDER_IN_BODY:
     case NS_HTML5TREE_BUILDER_IN_CELL:
     case NS_HTML5TREE_BUILDER_IN_CAPTION: {
-      if (!isInForeignButNotHtmlIntegrationPoint()) {
+      if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
         reconstructTheActiveFormattingElements();
       }
     }
     case NS_HTML5TREE_BUILDER_TEXT: {
       accumulateCharacters(buf, start, length);
       return;
     }
     case NS_HTML5TREE_BUILDER_IN_TABLE:
@@ -241,17 +241,17 @@ nsHtml5TreeBuilder::characters(const PRU
               case NS_HTML5TREE_BUILDER_FRAMESET_OK:
               case NS_HTML5TREE_BUILDER_IN_BODY:
               case NS_HTML5TREE_BUILDER_IN_CELL:
               case NS_HTML5TREE_BUILDER_IN_CAPTION: {
                 if (start < i) {
                   accumulateCharacters(buf, start, i - start);
                   start = i;
                 }
-                if (!isInForeignButNotHtmlIntegrationPoint()) {
+                if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
                   flushCharacters();
                   reconstructTheActiveFormattingElements();
                 }
                 NS_HTML5_BREAK(charactersloop);
               }
               case NS_HTML5TREE_BUILDER_IN_SELECT:
               case NS_HTML5TREE_BUILDER_IN_SELECT_IN_TABLE: {
                 NS_HTML5_BREAK(charactersloop);
@@ -343,17 +343,17 @@ nsHtml5TreeBuilder::characters(const PRU
               }
               case NS_HTML5TREE_BUILDER_IN_BODY:
               case NS_HTML5TREE_BUILDER_IN_CELL:
               case NS_HTML5TREE_BUILDER_IN_CAPTION: {
                 if (start < i) {
                   accumulateCharacters(buf, start, i - start);
                   start = i;
                 }
-                if (!isInForeignButNotHtmlIntegrationPoint()) {
+                if (!isInForeignButNotHtmlOrMathTextIntegrationPoint()) {
                   flushCharacters();
                   reconstructTheActiveFormattingElements();
                 }
                 NS_HTML5_BREAK(charactersloop);
               }
               case NS_HTML5TREE_BUILDER_IN_TABLE:
               case NS_HTML5TREE_BUILDER_IN_TABLE_BODY:
               case NS_HTML5TREE_BUILDER_IN_ROW: {
@@ -433,21 +433,17 @@ nsHtml5TreeBuilder::characters(const PRU
 void 
 nsHtml5TreeBuilder::zeroOriginatingReplacementCharacter()
 {
   if (mode == NS_HTML5TREE_BUILDER_TEXT) {
     accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
     return;
   }
   if (currentPtr >= 0) {
-    nsHtml5StackNode* stackNode = stack[currentPtr];
-    if (stackNode->ns == kNameSpaceID_XHTML) {
-      return;
-    }
-    if (stackNode->isHtmlIntegrationPoint()) {
+    if (isSpecialParentInForeign(stack[currentPtr])) {
       return;
     }
     accumulateCharacters(REPLACEMENT_CHARACTER, 0, 1);
   }
 }
 
 void 
 nsHtml5TreeBuilder::eof()
@@ -1242,19 +1238,16 @@ nsHtml5TreeBuilder::startTag(nsHtml5Elem
                 generateImpliedEndTags();
               }
               if (eltPos != currentPtr) {
                 if (eltPos != NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) {
                   errStartTagSeenWithoutRuby(name);
                 } else {
                   errUnclosedChildrenInRuby();
                 }
-                while (currentPtr > eltPos) {
-                  pop();
-                }
               }
               appendToCurrentNodeAndPushElementMayFoster(elementName, attributes);
               attributes = nsnull;
               NS_HTML5_BREAK(starttagloop);
             }
             case NS_HTML5TREE_BUILDER_MATH: {
               reconstructTheActiveFormattingElements();
               attributes->adjustForMath();
@@ -3816,19 +3809,22 @@ nsHtml5TreeBuilder::requestSuspension()
 
 bool 
 nsHtml5TreeBuilder::isInForeign()
 {
   return currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML;
 }
 
 bool 
-nsHtml5TreeBuilder::isInForeignButNotHtmlIntegrationPoint()
+nsHtml5TreeBuilder::isInForeignButNotHtmlOrMathTextIntegrationPoint()
 {
-  return currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML && !stack[currentPtr]->isHtmlIntegrationPoint();
+  if (currentPtr < 0) {
+    return false;
+  }
+  return !isSpecialParentInForeign(stack[currentPtr]);
 }
 
 void 
 nsHtml5TreeBuilder::setFragmentContext(nsIAtom* context, PRInt32 ns, nsIContent** node, bool quirks)
 {
   this->contextName = context;
   this->contextNamespace = ns;
   this->contextNode = node;
--- a/parser/html/nsHtml5TreeBuilder.h
+++ b/parser/html/nsHtml5TreeBuilder.h
@@ -213,17 +213,17 @@ class nsHtml5TreeBuilder : public nsAHtm
   public:
     inline bool cdataSectionAllowed()
     {
       return isInForeign();
     }
 
   private:
     bool isInForeign();
-    bool isInForeignButNotHtmlIntegrationPoint();
+    bool isInForeignButNotHtmlOrMathTextIntegrationPoint();
   public:
     void setFragmentContext(nsIAtom* context, PRInt32 ns, nsIContent** node, bool quirks);
   protected:
     nsIContent** currentNode();
   public:
     bool isScriptingEnabled();
     void setScriptingEnabled(bool scriptingEnabled);
     void flushCharacters();
index 1f01e86f405219b13c6de7075a45619f9d114ade..04cc11fb9d458ea32dca02e2f3bf39221196ab8e
GIT binary patch
literal 4166
zc%0o=O^?$s5Y5?A|HCwuxS;O#P%euVsoE~rAh8@^wTDUL?gr&ciGfApuOWUCe}S=+
zHg%eXG#^ExQWa-Le(%k*GfrkcN@Q<b_r>gKu~@8Me{@|o6IqsIIh*;(=5r)s$-V=B
zlu_u=0B9)CFNa>@e=CLnHo;72zRhT+f-UA)e4Ak;av>dlxgVZFQcW^|!Gf`%(Zb%)
zhBb(FS4OkNVx!r@a}kP-B#Mqc>;QJK9e{R%^j(knX^25A#S>Mt4Z=`lmb&R)S%p_d
zXw`rH{83=jK&9wXZG-&FgTtqH*Xz~o>dw$`f8TkdDO6C%Ek)?Ld}?(koX+P!kT9P+
zN2muy^(HWCXkgQJZj(#|53%cg(%bZae>g*R#3b71$nJtuQJPCVj+mp>t&#HHt*e>S
zuGw+aFEl%hMGx)xaV&mp$ImZ|T{%n(E-f7%6Z>GcGRx?BDauDWn99r0)$nvMxar!x
zzWwm_28zQk&4e>y6SgAlG;GE;=d?XL6x`l2wzB^yVe+p9jLrW%y+kh*bjj%NJ3P``
zC@M~I-0N8`uizbih4(M%m39F^47tn%Mi*@P>I!aRACfIl*Ayy+yIu~lTK^O`0yc?0
z$u9<|tWaEa3wf9=BH7`s3ixJ#LaI>E)z&imocZQa{cLP&X43)sKYM!AtBlOE2Yd@-
z%WWCZXM{K4L(;Gb%2;5$fuQ#{Wy#r;B_}G2&!#LsQCWUAW%-H9^rL?+arHi<_5B7{
C$<?I*
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tables01.dat
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tables01.dat
@@ -190,8 +190,23 @@
 |   <head>
 |   <body>
 |     <table>
 |       <tbody>
 |         <tr>
 |           <td>
 |             <button>
 |           <td>
+
+#data
+<table><tr><td><svg><desc><td>
+#errors
+#document
+| <html>
+|   <head>
+|   <body>
+|     <table>
+|       <tbody>
+|         <tr>
+|           <td>
+|             <svg svg>
+|               <svg desc>
+|           <td>
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests16.dat
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests16.dat
@@ -1071,16 +1071,38 @@ Line: 1 Col: 64 Unexpected end tag (text
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
 |     <textarea>
 |       "</textarea>"
 
 #data
+<!doctype html><textarea>&lt;</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     <textarea>
+|       "<"
+
+#data
+<!doctype html><textarea>a&lt;b</textarea>
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     <textarea>
+|       "a<b"
+
+#data
 <!doctype html><iframe><!--<iframe></iframe>--></iframe>
 #errors
 Line: 1 Col: 56 Unexpected end tag (iframe).
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests19.dat
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests19.dat
@@ -168,30 +168,30 @@
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
 |     <ruby>
 |       <div>
 |         <span>
-|       <rp>
+|           <rp>
 
 #data
 <!doctype html><ruby><div><p><rp>
 #errors
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
 |     <ruby>
 |       <div>
 |         <p>
-|       <rp>
+|         <rp>
 
 #data
 <!doctype html><ruby><p><rt>
 #errors
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
@@ -206,30 +206,30 @@
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
 |     <ruby>
 |       <div>
 |         <span>
-|       <rt>
+|           <rt>
 
 #data
 <!doctype html><ruby><div><p><rt>
 #errors
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
 |     <ruby>
 |       <div>
 |         <p>
-|       <rt>
+|         <rt>
 
 #data
 <!doctype html><math/><foo>
 #errors
 #document
 | <!DOCTYPE html>
 | <html>
 |   <head>
@@ -1213,8 +1213,25 @@
 | <html>
 |   <head>
 |   <body>
 |     <p>
 |       <a>
 |     <plaintext>
 |       <a>
 |         "b"
+
+#data
+<!DOCTYPE html><div>a<a></div>b<p>c</p>d
+#errors
+#document
+| <!DOCTYPE html>
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       "a"
+|       <a>
+|     <a>
+|       "b"
+|       <p>
+|         "c"
+|       "d"
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests26.dat
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests26.dat
@@ -267,17 +267,18 @@ 39: End of file in a foreign namespace c
 | <!DOCTYPE html>
 | <html>
 |   <head>
 |   <body>
 |     <math math>
 |       <math mtext>
 |         <p>
 |           <i>
-|         "a"
+|         <i>
+|           "a"
 
 #data
 <!DOCTYPE html><table><tr><td><math><mtext><p><i></p>a
 #errors
 53: End tag “p” seen, but there were open elements.
 49: Unclosed element “i”.
 54: End of file in a foreign namespace context.
 #document
@@ -288,17 +289,18 @@ 54: End of file in a foreign namespace c
 |     <table>
 |       <tbody>
 |         <tr>
 |           <td>
 |             <math math>
 |               <math mtext>
 |                 <p>
 |                   <i>
-|                 "a"
+|                 <i>
+|                   "a"
 
 #data
 <!DOCTYPE html><body><div><!/div>a
 #errors
 29: Bogus comment.
 34: End of file seen and there were open elements.
 26: Unclosed element “div”.
 #document
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests_innerHTML_1.dat
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/tests_innerHTML_1.dat
@@ -726,8 +726,16 @@ html
 
 #data
 </frameset><frame>
 #errors
 #document-fragment
 frameset
 #document
 | <frame>
+
+#data
+#errors
+#document-fragment
+html
+#document
+| <head>
+| <body>
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/webkit01.dat
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/webkit01.dat
@@ -284,30 +284,30 @@ console.log("FOO<span>BAR</span>BAZ");
 <html><body><ruby><div><rp>xx</rp></div></ruby></body></html>
 #errors
 #document
 | <html>
 |   <head>
 |   <body>
 |     <ruby>
 |       <div>
-|       <rp>
-|         "xx"
+|         <rp>
+|           "xx"
 
 #data
 <html><body><ruby><div><rt>xx</rt></div></ruby></body></html>
 #errors
 #document
 | <html>
 |   <head>
 |   <body>
 |     <ruby>
 |       <div>
-|       <rt>
-|         "xx"
+|         <rt>
+|           "xx"
 
 #data
 <html><frameset><!--1--><noframes>A</noframes><!--2--></frameset><!--3--><noframes>B</noframes><!--4--></html><!--5--><noframes>C</noframes><!--6-->
 #errors
 #document
 | <html>
 |   <head>
 |   <frameset>
--- a/xpcom/ds/nsExpirationTracker.h
+++ b/xpcom/ds/nsExpirationTracker.h
@@ -43,16 +43,17 @@
 #include "nsTArray.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
+#include "mozilla/Attributes.h"
 
 /**
  * Data used to track the expiration state of an object. We promise that this
  * is 32 bits so that objects that includes this as a field can pad and align
  * efficiently.
  */
 struct nsExpirationState {
   enum { NOT_TRACKED = (1U << 4) - 1,
@@ -310,17 +311,17 @@ template <class T, PRUint32 K> class nsE
     PRUint32           mTimerPeriod;
     PRUint32           mNewestGeneration;
     bool               mInAgeOneGeneration;
 
     /**
      * Whenever "memory-pressure" is observed, it calls AgeAllGenerations()
      * to minimize memory usage.
      */
-    class ExpirationTrackerObserver : public nsIObserver {
+    class ExpirationTrackerObserver MOZ_FINAL : public nsIObserver {
     public:
       void Init(nsExpirationTracker<T,K> *obj) {
         mOwner = obj;
         nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
         if (obs) {
           obs->AddObserver(this, "memory-pressure", false);
         }
       }