Backed out changeset 09ffb30caa47
authorBrian Hackett
Mon, 20 Sep 2010 12:05:21 -0700
changeset 54426 7e801a9e2276071b78520e8a9ca47fd32cd0ac55
parent 54425 09ffb30caa47adebb50ed58aa12a4c83ce8fb564
child 54427 d20abbebe373dd276dd94b6cf84abac670937b0b
push id15881
push userrsayre@mozilla.com
push dateTue, 21 Sep 2010 05:12:47 +0000
treeherdermozilla-central@e6cfaf0840cb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b7pre
backs out09ffb30caa47adebb50ed58aa12a4c83ce8fb564
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 09ffb30caa47
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsbuiltins.cpp
js/src/jsdate.cpp
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jslock.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jsregexp.h
js/src/jsscope.cpp
js/src/jsscopeinlines.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jsvalue.h
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/NunboxAssembler.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PunboxAssembler.h
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
js/src/trace-test/tests/basic/testHoleInDenseArray.js
js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -47,14 +47,14 @@ BEGIN_TEST(testConservativeGC)
 
 bool checkObjectFields(JSObject *savedCopy, JSObject *obj)
 {
     /*
      * The GC can change the shape and shrink dslots so we update them before
      * doing memcmp.
      */
     savedCopy->objShape = obj->objShape;
-    savedCopy->slots = obj->slots;
-    CHECK(!memcmp(savedCopy, obj, JSOBJECT_SIZE));
+    savedCopy->dslots = obj->dslots;
+    CHECK(!memcmp(savedCopy, obj, sizeof(*obj)));
     return true;
 }
 
 END_TEST(testConservativeGC)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3026,16 +3026,18 @@ JS_SealObject(JSContext *cx, JSObject *o
     /* If not sealing an entire object graph, we're done after sealing obj. */
     obj->seal(cx);
     if (!deep)
         return true;
 
     /* Walk slots in obj and if any value is a non-null object, seal it. */
     for (uint32 i = 0, n = obj->slotSpan(); i != n; ++i) {
         const Value &v = obj->getSlot(i);
+        if (i == JSSLOT_PRIVATE && (obj->getClass()->flags & JSCLASS_HAS_PRIVATE))
+            continue;
         if (v.isPrimitive())
             continue;
         if (!JS_SealObject(cx, &v.toObject(), deep))
             return false;
     }
     return true;
 }
 
@@ -3824,40 +3826,41 @@ JS_Enumerate(JSContext *cx, JSObject *ob
 
 /*
  * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's
  *     prop_iterator_class somehow...
  * + preserve the obj->enumerate API while optimizing the native object case
  * + native case here uses a Shape *, but that iterates in reverse!
  * + so we make non-native match, by reverse-iterating after JS_Enumerating
  */
-const uint32 JSSLOT_ITER_INDEX = 0;
+const uint32 JSSLOT_ITER_INDEX = JSSLOT_PRIVATE + 1;
+JS_STATIC_ASSERT(JSSLOT_ITER_INDEX < JS_INITIAL_NSLOTS);
 
 static void
 prop_iter_finalize(JSContext *cx, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
         return;
 
-    if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) {
+    if (obj->fslots[JSSLOT_ITER_INDEX].toInt32() >= 0) {
         /* Non-native case: destroy the ida enumerated when obj was created. */
         JSIdArray *ida = (JSIdArray *) pdata;
         JS_DestroyIdArray(cx, ida);
     }
 }
 
 static void
 prop_iter_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
         return;
 
-    if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) {
+    if (obj->fslots[JSSLOT_ITER_INDEX].toInt32() < 0) {
         /* Native case: just mark the next property to visit. */
         ((Shape *) pdata)->trace(trc);
     } else {
         /* Non-native case: mark each id in the JSIdArray private. */
         JSIdArray *ida = (JSIdArray *) pdata;
         MarkIdRange(trc, ida->length, ida->vector, "prop iter");
     }
 }
@@ -3913,31 +3916,31 @@ JS_NewPropertyIterator(JSContext *cx, JS
         if (!ida)
             return NULL;
         pdata = ida;
         index = ida->length;
     }
 
     /* iterobj cannot escape to other threads here. */
     iterobj->setPrivate(const_cast<void *>(pdata));
-    iterobj->getSlotRef(JSSLOT_ITER_INDEX).setInt32(index);
+    iterobj->fslots[JSSLOT_ITER_INDEX].setInt32(index);
     return iterobj;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
 {
     jsint i;
     JSObject *obj;
     const Shape *shape;
     JSIdArray *ida;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, iterobj);
-    i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32();
+    i = iterobj->fslots[JSSLOT_ITER_INDEX].toInt32();
     if (i < 0) {
         /* Native case: private data is a property tree node pointer. */
         obj = iterobj->getParent();
         JS_ASSERT(obj->isNative());
         shape = (Shape *) iterobj->getPrivate();
 
         /*
          * If the next property mapped by obj in the property tree ancestor
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -305,16 +305,119 @@ BigIndexToId(JSContext *cx, JSObject *ob
         if (!atom)
             return JS_FALSE;
     }
 
     *idp = ATOM_TO_JSID(atom);
     return JS_TRUE;
 }
 
+bool
+JSObject::growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap)
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(newcap >= ARRAY_CAPACITY_MIN);
+    JS_ASSERT(newcap >= oldcap);
+
+    if (newcap > MAX_DSLOTS_LENGTH32) {
+        if (!JS_ON_TRACE(cx))
+            js_ReportAllocationOverflow(cx);
+        return JS_FALSE;
+    }
+
+    /* dslots can be NULL during array creation. */
+    Value *slots = dslots ? dslots - 1 : NULL;
+    Value *newslots = (Value *) cx->realloc(slots, (size_t(newcap) + 1) * sizeof(Value));
+    if (!newslots)
+        return false;
+
+    dslots = newslots + 1;
+    setDenseArrayCapacity(newcap);
+
+    Value *base = addressOfDenseArrayElement(0);
+    for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp)
+        vp->setMagic(JS_ARRAY_HOLE);
+
+    return true;
+}
+
+bool
+JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap)
+{
+    /*
+     * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to
+     * grow, double its capacity, to push() N elements in amortized O(N) time.
+     *
+     * Above this limit, grow by 12.5% each time. Speed is still amortized
+     * O(N), with a higher constant factor, and we waste less space.
+     */
+    static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
+
+    /*
+     * Round up all large allocations to a multiple of this (1MB), so as not
+     * to waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
+     */
+    static const size_t CAPACITY_CHUNK = 1024 * 1024 / sizeof(Value);
+
+    uint32 oldcap = getDenseArrayCapacity();
+
+    if (newcap > oldcap) {
+        /*
+         * If this overflows uint32, newcap is very large. nextsize will end
+         * up being less than newcap, the code below will thus disregard it,
+         * and resizeDenseArrayElements() will fail.
+         *
+         * The way we use dslots[-1] forces a few +1s and -1s here. For
+         * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ...
+         * which makes the total allocation size (with dslots[-1]) a power
+         * of two.
+         */
+        uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
+                          ? oldcap * 2 + 1
+                          : oldcap + (oldcap >> 3);
+
+        uint32 actualCapacity = JS_MAX(newcap, nextsize);
+        if (actualCapacity >= CAPACITY_CHUNK)
+            actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */
+        else if (actualCapacity < ARRAY_CAPACITY_MIN)
+            actualCapacity = ARRAY_CAPACITY_MIN;
+
+        if (!growDenseArrayElements(cx, oldcap, actualCapacity))
+            return false;
+    }
+    return true;
+}
+
+bool
+JSObject::shrinkDenseArrayElements(JSContext *cx, uint32 newcap)
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(newcap < getDenseArrayCapacity());
+    JS_ASSERT(dslots);
+
+    uint32 fill = newcap;
+
+    if (newcap < ARRAY_CAPACITY_MIN)
+        newcap = ARRAY_CAPACITY_MIN;
+
+    Value *newslots = (Value *) cx->realloc(dslots - 1, (size_t(newcap) + 1) * sizeof(Value));
+    if (!newslots)
+        return false;
+
+    dslots = newslots + 1;
+    setDenseArrayCapacity(newcap);
+
+    /* we refuse to shrink below a minimum value, so we have to clear the excess space */
+    Value *base = addressOfDenseArrayElement(0);
+    while (fill < newcap)
+        base[fill++].setMagic(JS_ARRAY_HOLE);
+
+    return true;
+}
+
 static bool
 ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
 {
     return js_ValueToStringId(cx, DoubleValue(index), idp);
 }
 
 static bool
 IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp,
@@ -421,18 +524,18 @@ JSBool JS_FASTCALL
 js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i)
 {
     jsuint u = jsuint(i);
     jsuint capacity = obj->getDenseArrayCapacity();
     if (u < capacity)
         return true;
     if (INDEX_TOO_SPARSE(obj, u))
         return false;
-
-    return obj->ensureDenseArrayElements(cx, u + 1);
+    return obj->ensureDenseArrayElements(cx, u + 1);    
+
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_EnsureDenseArrayCapacity, CONTEXT, OBJECT, INT32, 0,
                      nanojit::ACCSET_STORE_ANY)
 #endif
 
 static JSBool
 DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool strict)
 {
@@ -551,23 +654,23 @@ array_length_setter(JSContext *cx, JSObj
     if (oldlen < newlen) {
         obj->setArrayLength(newlen);
         return true;
     }
 
     if (obj->isDenseArray()) {
         /*
          * Don't reallocate if we're not actually shrinking our slots. If we do
-         * shrink slots here, ensureDenseArrayElements will fill all slots to the
+         * shrink slots here, resizeDenseArrayElements will fill all slots to the
          * right of newlen with JS_ARRAY_HOLE. This permits us to disregard
          * length when reading from arrays as long we are within the capacity.
          */
         jsuint oldcap = obj->getDenseArrayCapacity();
-        if (oldcap > newlen)
-            obj->shrinkDenseArrayElements(cx, newlen);
+        if (oldcap > newlen && !obj->shrinkDenseArrayElements(cx, newlen))
+            return false;
         obj->setArrayLength(newlen);
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
                 obj->setArrayLength(oldlen + 1);
                 return false;
             }
@@ -866,20 +969,29 @@ array_deleteProperty(JSContext *cx, JSOb
     if (!js_SuppressDeletedProperty(cx, obj, id))
         return false;
 
     rval->setBoolean(true);
     return JS_TRUE;
 }
 
 static void
+array_finalize(JSContext *cx, JSObject *obj)
+{
+    obj->freeDenseArrayElements(cx);
+}
+
+static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isDenseArray());
 
+    if (!obj->dslots)
+        return;
+
     size_t holes = 0;
     uint32 capacity = obj->getDenseArrayCapacity();
     for (uint32 i = 0; i < capacity; i++) {
         Value v = obj->getDenseArrayElement(i);
         if (v.isMagic(JS_ARRAY_HOLE))
             ++holes;
         else
             MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
@@ -889,26 +1001,26 @@ array_trace(JSTracer *trc, JSObject *obj
         /* This might fail, in which case we don't slowify it. */
         static_cast<GCMarker *>(trc)->arraysToSlowify.append(obj);
     }
 }
 
 Class js_ArrayClass = {
     "Array",
     Class::NON_NATIVE |
-    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_RESERVED_SLOTS(JSObject::DENSE_ARRAY_CLASS_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
     PropertyStub,   /* addProperty */
     PropertyStub,   /* delProperty */
     PropertyStub,   /* getProperty */
     PropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     js_TryValueOf,
-    NULL,
+    array_finalize,
     NULL,           /* reserved0   */
     NULL,           /* checkAccess */
     NULL,           /* call        */
     NULL,           /* construct   */
     NULL,           /* xdrObject   */
     NULL,           /* hasInstance */
     NULL,           /* mark        */
     JS_NULL_CLASS_EXT,
@@ -982,28 +1094,36 @@ JSObject::makeDenseArraySlow(JSContext *
      */
     JSObjectMap *oldMap = map;
 
     /*
      * Create a native scope. All slow arrays other than Array.prototype get
      * the same initial shape.
      */
     JSObject *arrayProto = getProto();
-    if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto, FINALIZE_OBJECT0))
+    if (!InitScopeForObject(cx, this, &js_SlowArrayClass, arrayProto))
         return false;
 
-    uint32 capacity = getDenseArrayCapacity();
-
-    /*
-     * Begin with the length property to share more of the property tree.
-     * The getter/setter here will directly access the object's private value.
-     */
+    uint32 capacity;
+
+    if (dslots) {
+        capacity = getDenseArrayCapacity();
+        dslots[-1].setPrivateUint32(JS_INITIAL_NSLOTS + capacity);
+    } else {
+        /*
+         * Array.prototype is constructed as a dense array, but is immediately slowified before
+         * we have time to set capacity.
+         */
+        capacity = 0;
+    }
+
+    /* Begin with the length property to share more of the property tree. */
     if (!addProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
                      array_length_getter, NULL,
-                     SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) {
+                     JSSLOT_ARRAY_LENGTH, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0)) {
         setMap(oldMap);
         return false;
     }
 
     /* Create new properties pointing to existing elements. */
     for (uint32 i = 0; i < capacity; i++) {
         jsid id;
         if (!ValueToId(cx, Int32Value(i), &id)) {
@@ -1011,23 +1131,35 @@ JSObject::makeDenseArraySlow(JSContext *
             return false;
         }
 
         if (getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
             setDenseArrayElement(i, UndefinedValue());
             continue;
         }
 
-        if (!addDataProperty(cx, id, i, JSPROP_ENUMERATE)) {
+        /* Assert that the length covering i fits in the alloted bits. */
+        JS_ASSERT(JS_INITIAL_NSLOTS + i + 1 < NSLOTS_LIMIT);
+
+        if (!addDataProperty(cx, id, JS_INITIAL_NSLOTS + i, JSPROP_ENUMERATE)) {
             setMap(oldMap);
             return false;
         }
     }
 
     /*
+     * Render our formerly-reserved non-private properties GC-safe.  We do not
+     * need to make the length slot GC-safe because it is the private slot
+     * (this is statically asserted within JSObject) where the implementation
+     * can store an arbitrary value.
+     */
+    JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE);
+    voidDenseOnlyArraySlots();
+
+    /*
      * Finally, update class. If |this| is Array.prototype, then js_InitClass
      * will create an emptyShape whose class is &js_SlowArrayClass, to ensure
      * that delegating instances can share shapes in the tree rooted at the
      * proto's empty shape.
      */
     clasp = &js_SlowArrayClass;
     return true;
 }
@@ -1358,16 +1490,17 @@ InitArrayElements(JSContext *cx, JSObjec
 
 static JSBool
 InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector)
 {
     JS_ASSERT(obj->isArray());
 
     JS_ASSERT(obj->isDenseArray());
     obj->setArrayLength(length);
+    obj->setDenseArrayCapacity(0);
     if (!vector || !length)
         return true;
     if (!obj->ensureDenseArrayElements(cx, length))
         return false;
     memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
     return true;
 }
 
@@ -2316,18 +2449,20 @@ array_concat(JSContext *cx, uintN argc, 
     /* Create a new Array object and root it using *vp. */
     JSObject *aobj = ComputeThisFromVp(cx, vp);
     JSObject *nobj;
     jsuint length;
     if (aobj->isDenseArray()) {
         /*
          * Clone aobj but pass the minimum of its length and capacity, to
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
-         * the normal case where length is <= capacity, nobj and aobj will have
-         * the same capacity.
+         * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which
+         * will cause nobj to be over-allocated to 16. But in the normal case
+         * where length is <= capacity, nobj and aobj will have the same
+         * capacity.
          */
         length = aobj->getArrayLength();
         jsuint capacity = aobj->getDenseArrayCapacity();
         nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
         if (!nobj)
             return JS_FALSE;
         nobj->setArrayLength(length);
         vp->setObject(*nobj);
@@ -2837,86 +2972,84 @@ static JSFunctionSpec array_methods[] = 
     JS_FS_END
 };
 
 static JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
     JS_FS_END
 };
 
-/* The count here is a guess for the final capacity. */
 static inline JSObject *
-NewDenseArrayObject(JSContext *cx, jsuint count)
+NewDenseArrayObject(JSContext *cx)
 {
-    JSFinalizeGCThingKind kind = GuessObjectGCKind(count);
-    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
+    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL);
 }
 
 JSBool
 js_Array(JSContext *cx, uintN argc, Value *vp)
 {
     jsuint length;
     const Value *vector;
 
+    /* Whether called with 'new' or not, use a new Array object. */
+    JSObject *obj = NewDenseArrayObject(cx);
+    if (!obj)
+        return JS_FALSE;
+    vp->setObject(*obj);
+
     if (argc == 0) {
         length = 0;
         vector = NULL;
     } else if (argc > 1) {
         length = (jsuint) argc;
         vector = vp + 2;
     } else if (!vp[2].isNumber()) {
         length = 1;
         vector = vp + 2;
     } else {
         length = ValueIsLength(cx, vp + 2);
         if (vp[2].isNull())
             return JS_FALSE;
         vector = NULL;
     }
 
-    /* Whether called with 'new' or not, use a new Array object. */
-    JSObject *obj = NewDenseArrayObject(cx, length);
-    if (!obj)
-        return JS_FALSE;
-    vp->setObject(*obj);
-
     return InitArrayObject(cx, obj, length, vector);
 }
 
 JSObject* JS_FASTCALL
 js_NewEmptyArray(JSContext* cx, JSObject* proto, int32 len)
 {
     if (len < 0)
         return NULL;
 
     JS_ASSERT(proto->isArray());
 
-    JSFinalizeGCThingKind kind = js_GetGCObjectKind(len);
-    JSObject* obj = js_NewGCObject(cx, kind);
+    JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
-    /* Initialize all fields of JSObject. */
-    obj->init(cx, &js_ArrayClass, proto, proto->getParent(),
-              (void*) len, true);
+    /* Initialize all fields, calling init before setting obj->map. */
+    obj->init(&js_ArrayClass, proto, proto->getParent(), NullValue(), cx);
     obj->setSharedNonNativeMap();
+    obj->setArrayLength(len);
+    obj->setDenseArrayCapacity(0);
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, INT32, 0,
                      nanojit::ACCSET_STORE_ANY)
 #endif
 
 JSObject* JS_FASTCALL
 js_NewPreallocatedArray(JSContext* cx, JSObject* proto, int32 len)
 {
     JSObject *obj = js_NewEmptyArray(cx, proto, len);
     if (!obj)
         return NULL;
-    if (!obj->ensureDenseArrayElements(cx, len))
+    if (!obj->growDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
         return NULL;
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, INT32,
                      0, nanojit::ACCSET_STORE_ANY)
 #endif
 
@@ -2927,26 +3060,26 @@ js_InitArrayClass(JSContext *cx, JSObjec
                                    NULL, array_methods, NULL, array_static_methods);
     if (!proto)
         return NULL;
 
     /*
      * Assert that js_InitClass used the correct (slow array, not dense array)
      * class for proto's emptyShape class.
      */
-    JS_ASSERT(proto->emptyShapes && proto->emptyShapes[0]->getClass() == proto->getClass());
+    JS_ASSERT(proto->emptyShape->getClass() == proto->getClass());
 
     proto->setArrayLength(0);
     return proto;
 }
 
 JSObject *
 js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
 {
-    JSObject *obj = NewDenseArrayObject(cx, length);
+    JSObject *obj = NewDenseArrayObject(cx);
     if (!obj)
         return NULL;
 
     /*
      * If this fails, the global object was not initialized and its class does
      * not have JSCLASS_IS_GLOBAL.
      */
     JS_ASSERT(obj->getProto());
@@ -3057,17 +3190,17 @@ js_CoerceArrayToCanvasImageData(JSObject
 JS_FRIEND_API(JSBool)
 js_IsDensePrimitiveArray(JSObject *obj)
 {
     if (!obj || !obj->isDenseArray())
         return JS_FALSE;
 
     jsuint capacity = obj->getDenseArrayCapacity();
     for (jsuint i = 0; i < capacity; i++) {
-        if (obj->getDenseArrayElement(i).isObject())
+        if (obj->dslots[i].isObject())
             return JS_FALSE;
     }
 
     return JS_TRUE;
 }
 
 JS_FRIEND_API(JSBool)
 js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
@@ -3081,28 +3214,29 @@ js_CloneDensePrimitiveArray(JSContext *c
         *clone = NULL;
         return JS_TRUE;
     }
 
     jsuint length = obj->getArrayLength();
 
     /*
      * Must use the minimum of original array's length and capacity, to handle
-     * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In the normal
-     * case where length is <= capacity, the clone and original array will have
-     * the same capacity.
+     * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In such a case
+     * we would use ARRAY_CAPACITY_MIN (not 3), which will cause the clone to be
+     * over-allocated. In the normal case where length is <= capacity the
+     * clone and original array will have the same capacity.
      */
     jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length);
 
     js::AutoValueVector vector(cx);
     if (!vector.reserve(jsvalCount))
         return JS_FALSE;
 
     for (jsuint i = 0; i < jsvalCount; i++) {
-        const Value &val = obj->getDenseArrayElement(i);
+        const Value &val = obj->dslots[i];
 
         if (val.isString()) {
             // Strings must be made immutable before being copied to a clone.
             if (!js_MakeStringImmutable(cx, val.toString()))
                 return JS_FALSE;
         } else if (val.isObject()) {
             /*
              * This wasn't an array of primitives. Return JS_TRUE but a null
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -41,16 +41,18 @@
 #define jsarray_h___
 /*
  * JS Array interface.
  */
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsobj.h"
 
+#define ARRAY_CAPACITY_MIN      7
+
 extern JSBool
 js_StringIsIndex(JSString *str, jsuint *indexp);
 
 inline JSBool
 js_IdIsIndex(jsid id, jsuint *indexp)
 {
     if (JSID_IS_INT(id)) {
         jsint i;
@@ -132,16 +134,23 @@ js_GetProtoIfDenseArray(JSObject *obj)
 }
 
 extern JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj);
 
 extern bool
 js_InitContextBusyArrayTable(JSContext *cx);
 
+/*
+ * Creates a new array with the given length and proto (NB: NULL is not
+ * translated to Array.prototype), with len slots preallocated.
+ */
+extern JSObject * JS_FASTCALL
+js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len);
+
 extern JSObject *
 js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
 
 /* Create an array object that starts out already made slow/sparse. */
 extern JSObject *
 js_NewSlowArrayObject(JSContext *cx);
 
 extern JSBool
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -297,21 +297,18 @@ js_NewNullClosure(JSContext* cx, JSObjec
 {
     JS_ASSERT(funobj->isFunction());
     JS_ASSERT(proto->isFunction());
     JS_ASSERT(JS_ON_TRACE(cx));
 
     JSFunction *fun = (JSFunction*) funobj;
     JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
 
-    JSObject* closure = js_NewGCObject(cx, FINALIZE_OBJECT2);
+    JSObject* closure = js_NewGCObject(cx);
     if (!closure)
         return NULL;
 
-    if (!closure->initSharingEmptyShape(cx, &js_FunctionClass, proto, parent,
-                                        fun, FINALIZE_OBJECT2)) {
-        return NULL;
-    }
+    closure->initSharingEmptyShape(&js_FunctionClass, proto, parent, fun, cx);
     return closure;
 }
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT,
                      0, ACCSET_STORE_ANY)
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2558,19 +2558,17 @@ js_InitDateClass(JSContext *cx, JSObject
 
     return proto;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass);
-    if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
-        return NULL;
-    if (!SetUTCTime(cx, obj, msec_time))
+    if (!obj || !SetUTCTime(cx, obj, msec_time))
         return NULL;
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
                  int hour, int min, int sec)
 {
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1640,17 +1640,25 @@ JS_SetDebugErrorHook(JSRuntime *rt, JSDe
     return JS_TRUE;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(size_t)
 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
 {
-    return obj->slotsAndStructSize();
+    size_t nbytes = (obj->isFunction() && obj->getPrivate() == obj)
+                    ? sizeof(JSFunction)
+                    : sizeof *obj;
+
+    if (obj->dslots) {
+        nbytes += (obj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS + 1)
+                  * sizeof obj->dslots[0];
+    }
+    return nbytes;
 }
 
 static size_t
 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
 {
     size_t nbytes;
 
     nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -230,17 +230,17 @@ UpdateDepth(JSContext *cx, JSCodeGenerat
     if (ndefs < 0) {
         JSObject *blockObj;
 
         /* We just executed IndexParsedObject */
         JS_ASSERT(op == JSOP_ENTERBLOCK);
         JS_ASSERT(nuses == 0);
         blockObj = cg->objectList.lastbox->object;
         JS_ASSERT(blockObj->isStaticBlock());
-        JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined());
+        JS_ASSERT(blockObj->fslots[JSSLOT_BLOCK_DEPTH].isUndefined());
 
         OBJ_SET_BLOCK_DEPTH(cx, blockObj, cg->stackDepth);
         ndefs = OBJ_BLOCK_COUNT(cx, blockObj);
     }
     cg->stackDepth += ndefs;
     if ((uintN)cg->stackDepth > cg->maxStackDepth)
         cg->maxStackDepth = cg->stackDepth;
 }
@@ -1594,18 +1594,19 @@ js_LexicalLookup(JSTreeContext *tc, JSAt
         JSObject *obj = stmt->blockObj;
         JS_ASSERT(obj->isStaticBlock());
 
         const Shape *shape = obj->nativeLookup(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;
+                JS_ASSERT(obj->fslots[JSSLOT_BLOCK_DEPTH].isInt32());
+                *slotp = obj->fslots[JSSLOT_BLOCK_DEPTH].toInt32() +
+                         shape->shortid;
             }
             return stmt;
         }
     }
 
     if (slotp)
         *slotp = -1;
     return stmt;
@@ -1862,22 +1863,21 @@ EmitEnterBlock(JSContext *cx, JSParseNod
             JS_ASSERT(pnu->pn_lexdef == dn);
             JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             JS_ASSERT(pnu->pn_cookie.isFree());
         }
 #endif
     }
 
     /*
-     * Clear blockObj->dslots and ensure a prompt safe crash if by accident
-     * some code tries to get a slot from a compiler-created Block prototype
-     * instead of from a clone.
+     * Shrink slots to free blockObj->dslots and ensure a prompt safe crash if
+     * by accident some code tries to get a slot from a compiler-created Block
+     * prototype instead of from a clone.
      */
-    if (blockObj->hasSlotsArray())
-        blockObj->removeSlotsArray(cx);
+    blockObj->shrinkSlots(cx, base);
     return true;
 }
 
 /*
  * When eval is called from a function, the eval code or function code it
  * compiles may reference upvars that live in the eval-calling function. The
  * eval-invoked compiler does not have explicit definitions for these upvars
  * and we do not attempt to create them a-priori (by inspecting the function's
@@ -4348,27 +4348,18 @@ EmitFunctionDefNop(JSContext *cx, JSCode
 {
     return js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 &&
            js_Emit1(cx, cg, JSOP_NOP) >= 0;
 }
 
 static bool
 EmitNewInit(JSContext *cx, JSCodeGenerator *cg, JSProtoKey key, JSParseNode *pn, int sharpnum)
 {
-    /*
-     * Watch for overflow on the initializer size.  This isn't problematic because
-     * (a) we'll be reporting an error for the initializer shortly, and (b)
-     * the count is only used as a hint for the interpreter and JITs, and does not
-     * need to be correct.
-     */
-    uint16 count = pn->pn_count;
-    if (count >= JS_BIT(16))
-        count = JS_BIT(16) - 1;
-
-    EMIT_UINT16PAIR_IMM_OP(JSOP_NEWINIT, (uint16) key, count);
+    if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) key) < 0)
+        return false;
 #if JS_HAS_SHARP_VARS
     if (cg->hasSharps()) {
         if (pn->pn_count != 0)
             EMIT_UINT16_IMM_OP(JSOP_SHARPINIT, cg->sharpSlotBase);
         if (sharpnum >= 0)
             EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, cg->sharpSlotBase, sharpnum);
     } else {
         JS_ASSERT(sharpnum < 0);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -177,32 +177,31 @@ js_GetArgsProperty(JSContext *cx, JSStac
 
 static JSObject *
 NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
 {
     JSObject *proto;
     if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
         return NULL;
 
-    JS_ASSERT(js_GetGCKindSlots(FINALIZE_OBJECT2) == JSObject::ARGS_CLASS_RESERVED_SLOTS);
-    JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
+    JSObject *argsobj = js_NewGCObject(cx);
     if (!argsobj)
         return NULL;
 
     ArgumentsData *data = (ArgumentsData *)
         cx->malloc(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
     if (!data)
         return NULL;
     SetValueRangeToUndefined(data->slots, argc);
 
     /* Can't fail from here on, so initialize everything in argsobj. */
-    argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode()
+    argsobj->init(callee.getFunctionPrivate()->inStrictMode()
                   ? &StrictArgumentsClass
                   : &js_ArgumentsClass,
-                  proto, parent, NULL, false);
+                  proto, parent, NULL, cx);
 
     argsobj->setMap(cx->runtime->emptyArgumentsShape);
 
     argsobj->setArgsLength(argc);
     argsobj->setArgsData(data);
     data->callee.setObject(callee);
 
     return argsobj;
@@ -960,30 +959,25 @@ static JSBool
 CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     return CheckForEscapingClosure(cx, obj, vp);
 }
 
 static JSObject *
 NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee)
 {
-    size_t vars = fun->countArgsAndVars();
-    size_t slots = JSObject::CALL_RESERVED_SLOTS + vars;
-    JSFinalizeGCThingKind kind = js_GetGCObjectKind(slots);
-
-    JSObject *callobj = js_NewGCObject(cx, kind);
+    JSObject *callobj = js_NewGCObject(cx);
     if (!callobj)
         return NULL;
 
-    /* Init immediately to avoid GC seeing a half-init'ed object. */
-    callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
+    callobj->init(&js_CallClass, NULL, &scopeChain, NULL, cx);
     callobj->setMap(fun->u.i.names);
 
     /* This must come after callobj->lastProp has been set. */
-    if (!callobj->ensureInstanceReservedSlots(cx, vars))
+    if (!callobj->ensureInstanceReservedSlots(cx, fun->countArgsAndVars()))
         return NULL;
 
 #ifdef DEBUG
     for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) {
         const Shape &s = r.front();
         if (s.slot != SHAPE_INVALID_SLOT) {
             JS_ASSERT(s.slot + 1 == callobj->slotSpan());
             break;
@@ -993,21 +987,21 @@ NewCallObject(JSContext *cx, JSFunction 
 
     callobj->setCallObjCallee(callee);
     return callobj;
 }
 
 static inline JSObject *
 NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
 {
-    JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
+    JSObject *envobj = js_NewGCObject(cx);
     if (!envobj)
         return NULL;
 
-    envobj->init(cx, &js_DeclEnvClass, NULL, &fp->scopeChain(), fp, false);
+    envobj->init(&js_DeclEnvClass, NULL, &fp->scopeChain(), fp, cx);
     envobj->setMap(cx->runtime->emptyDeclEnvShape);
     return envobj;
 }
 
 JSObject *
 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
 {
     /* Create a call object for fp only if it lacks one. */
@@ -1072,19 +1066,44 @@ js_CreateCallObjectOnTrace(JSContext *cx
 }
 
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
                      0, nanojit::ACCSET_STORE_ANY)
 
 inline static void
 CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots)
 {
-    Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS;
-    memcpy(base, argv, nargs * sizeof(Value));
-    memcpy(base + nargs, slots, nvars * sizeof(Value));
+    /* Copy however many args fit into fslots. */
+    uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1;
+    JS_ASSERT(first <= JS_INITIAL_NSLOTS);
+    
+    Value *vp = &callobj.fslots[first];
+    uintN len = Min(nargs, uintN(JS_INITIAL_NSLOTS) - first);
+
+    memcpy(vp, argv, len * sizeof(Value));
+    vp += len;
+
+    nargs -= len;
+    if (nargs != 0) {
+        /* Copy any remaining args into dslots. */
+        vp = callobj.dslots;
+        memcpy(vp, argv + len, nargs * sizeof(Value));
+        vp += nargs;
+    } else {
+        /* Copy however many vars fit into any remaining fslots. */
+        first += len;
+        len = JS_MIN(nvars, JS_INITIAL_NSLOTS - first);
+        memcpy(vp, slots, len * sizeof(Value));
+        slots += len;
+        nvars -= len;
+        vp = callobj.dslots;
+    }
+
+    /* Copy any remaining vars into dslots. */
+    memcpy(vp, slots, nvars * sizeof(Value));
 }
 
 void
 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject &callobj = fp->callObj();
 
     /* Get the arguments object to snapshot fp's actual argument values. */
@@ -1093,33 +1112,38 @@ js_PutCallObject(JSContext *cx, JSStackF
             callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
         js_PutArgsObject(cx, fp);
     }
 
     JSFunction *fun = fp->fun();
     JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
     uintN n = fun->countArgsAndVars();
 
+    /*
+     * Since for a call object all fixed slots happen to be taken, we can copy
+     * arguments and variables straight into JSObject.dslots.
+     */
+    JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
+                     1 + JSObject::CALL_RESERVED_SLOTS);
     if (n != 0) {
-        JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
+        JS_ASSERT(JSFunction::FIRST_FREE_SLOT + n <= callobj.numSlots());
 
         uint32 nargs = fun->nargs;
         uint32 nvars = fun->u.i.nvars;
 
-        Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS;
-
 #ifdef JS_METHODJIT
+        JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1);
         JSScript *script = fun->u.i.script;
-        memcpy(base, fp->formalArgs(), nargs * sizeof(Value));
+        memcpy(callobj.dslots, fp->formalArgs(), nargs * sizeof(Value));
         if (!script->jit || script->usesEval) {
-            memcpy(base + nargs, fp->slots(), nvars * sizeof(Value));
+            memcpy(callobj.dslots + nargs, fp->slots(), nvars * sizeof(Value));
         } else if (script->jit) {
             for (uint32 i = 0; i < script->jit->nescaping; i++) {
                 uint32 e = script->jit->escaping[i];
-                base[nargs + e] = fp->slots()[e];
+                callobj.dslots[nargs + e] = fp->slots()[e];
             }
         }
 #else
         CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
 #endif
     }
 
     /* Clear private pointers to fp, which is about to go away (js_Invoke). */
@@ -1204,23 +1228,26 @@ CallPropertyOp(JSContext *cx, JSObject *
                 } else {
                     *vp = obj->getCallObjArguments();
                 }
             }
             return true;
         }
 
         if (!fp) {
-            i += JSObject::CALL_RESERVED_SLOTS;
             if (kind == JSCPK_VAR)
                 i += fun->nargs;
             else
                 JS_ASSERT(kind == JSCPK_ARG);
 
-            array = obj->getSlots();
+            const uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1;
+            JS_ASSERT(first == JSSLOT_FREE(&js_CallClass));
+            JS_ASSERT(first <= JS_INITIAL_NSLOTS);
+
+            array = (i < JS_INITIAL_NSLOTS - first) ? obj->fslots : obj->dslots;
         } else if (kind == JSCPK_ARG) {
             array = fp->formalArgs();
         } else {
             JS_ASSERT(kind == JSCPK_VAR);
             array = fp->slots();
         }
     }
 
@@ -1352,21 +1379,24 @@ call_trace(JSTracer *trc, JSObject *obj)
     if (fp) {
         /*
          * FIXME: Hide copies of stack values rooted by fp from the Cycle
          * Collector, which currently lacks a non-stub Unlink implementation
          * for JS objects (including Call objects), so is unable to collect
          * cycles involving Call objects whose frames are active without this
          * hiding hack.
          */
-        uintN first = JSObject::CALL_RESERVED_SLOTS;
+        uintN first = JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1;
+        JS_ASSERT(first <= JS_INITIAL_NSLOTS);
+
         uintN count = fp->fun()->countArgsAndVars();
-
-        JS_ASSERT(obj->numSlots() >= first + count);
-        SetValueRangeToUndefined(obj->getSlots() + first, count);
+        uintN fixed = JS_MIN(count, JS_INITIAL_NSLOTS - first);
+
+        SetValueRangeToUndefined(&obj->fslots[first], fixed);
+        SetValueRangeToUndefined(obj->dslots, count - fixed);
     }
 
     MaybeMarkGenerator(trc, obj);
 }
 
 JS_PUBLIC_DATA(Class) js_CallClass = {
     "Call",
     JSCLASS_HAS_PRIVATE |
@@ -2370,32 +2400,33 @@ CallOrConstructBoundFunction(JSContext *
 
 inline bool
 JSObject::initBoundFunction(JSContext *cx, const Value &thisArg,
                             const Value *args, uintN argslen)
 {
     JS_ASSERT(isFunction());
 
     flags |= JSObject::BOUND_FUNCTION;
-    getSlotRef(JSSLOT_BOUND_FUNCTION_THIS) = thisArg;
-    getSlotRef(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).setPrivateUint32(argslen);
+    fslots[JSSLOT_BOUND_FUNCTION_THIS] = thisArg;
+    fslots[JSSLOT_BOUND_FUNCTION_ARGS_COUNT].setPrivateUint32(argslen);
     if (argslen != 0) {
         /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */
         EmptyShape *empty = EmptyShape::create(cx, clasp);
         if (!empty)
             return false;
 
         empty->slotSpan += argslen;
         map = empty;
 
         if (!ensureInstanceReservedSlots(cx, argslen))
             return false;
 
-        JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS);
-        memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value));
+        JS_ASSERT(dslots);
+        JS_ASSERT(dslots[-1].toPrivateUint32() >= argslen);
+        memcpy(&dslots[0], args, argslen * sizeof(Value));
     }
     return true;
 }
 
 inline JSObject *
 JSObject::getBoundFunctionTarget() const
 {
     JS_ASSERT(isFunction());
@@ -2406,29 +2437,29 @@ JSObject::getBoundFunctionTarget() const
 }
 
 inline const js::Value &
 JSObject::getBoundFunctionThis() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
-    return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
+    return fslots[JSSLOT_BOUND_FUNCTION_THIS];
 }
 
 inline const js::Value *
 JSObject::getBoundFunctionArguments(uintN &argslen) const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
-    argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
-    JS_ASSERT_IF(argslen > 0, numSlots() >= argslen);
-
-    return getSlots() + FUN_CLASS_RESERVED_SLOTS;
+    argslen = fslots[JSSLOT_BOUND_FUNCTION_ARGS_COUNT].toPrivateUint32();
+    JS_ASSERT_IF(argslen > 0, dslots);
+    JS_ASSERT_IF(argslen > 0, dslots[-1].toPrivateUint32() >= argslen);
+    return &dslots[0];
 }
 
 namespace {
 
 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
 JSBool
 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
 {
@@ -3105,17 +3136,17 @@ JSFunction::addLocal(JSContext *cx, JSAt
     /*
      * We still follow 10.2.3 of ES3 and make argument and variable properties
      * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
      * avoid objects as activations, something we should do too.
      */
     uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
     uint16 *indexp;
     PropertyOp getter, setter;
-    uint32 slot = JSObject::CALL_RESERVED_SLOTS;
+    uint32 slot = JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS;
 
     if (kind == JSLOCAL_ARG) {
         JS_ASSERT(u.i.nupvars == 0);
 
         indexp = &nargs;
         getter = js_GetCallArg;
         setter = SetCallArg;
         slot += nargs;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -129,18 +129,16 @@ enum JSLocalKind {
     JSLOCAL_ARG,
     JSLOCAL_VAR,
     JSLOCAL_CONST,
     JSLOCAL_UPVAR
 };
 
 struct JSFunction : public JSObject
 {
-    /* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */
-
     uint16          nargs;        /* maximum number of specified arguments,
                                      reflected as f.length/f.arity */
     uint16          flags;        /* flags, see JSFUN_* below and in jsapi.h */
     union U {
         struct {
             js::Native  native;   /* native method pointer or null */
             js::Class   *clasp;   /* class of objects constructed
                                      by this function */
@@ -266,47 +264,48 @@ struct JSFunction : public JSObject
      */
     enum {
         METHOD_ATOM_SLOT  = JSSLOT_FUN_METHOD_ATOM
     };
 
   public:
     void setJoinable() {
         JS_ASSERT(FUN_INTERPRETED(this));
-        getSlotRef(METHOD_ATOM_SLOT).setNull();
+        fslots[METHOD_ATOM_SLOT].setNull();
         flags |= JSFUN_JOINABLE;
     }
 
     /*
      * Method name imputed from property uniquely assigned to or initialized,
      * where the function does not need to be cloned to carry a scope chain or
      * flattened upvars.
      */
     JSAtom *methodAtom() const {
-        return (joinable() && getSlot(METHOD_ATOM_SLOT).isString())
-               ? STRING_TO_ATOM(getSlot(METHOD_ATOM_SLOT).toString())
+        return (joinable() && fslots[METHOD_ATOM_SLOT].isString())
+               ? STRING_TO_ATOM(fslots[METHOD_ATOM_SLOT].toString())
                : NULL;
     }
 
     void setMethodAtom(JSAtom *atom) {
         JS_ASSERT(joinable());
-        getSlotRef(METHOD_ATOM_SLOT).setString(ATOM_TO_STRING(atom));
+        fslots[METHOD_ATOM_SLOT].setString(ATOM_TO_STRING(atom));
     }
 
     js::Native maybeNative() const {
         return isInterpreted() ? NULL : u.n.native;
     }
 
     JSScript *script() const {
         JS_ASSERT(isInterpreted());
         return u.i.script;
     }
 
-    /* Number of extra fixed function object slots. */
+    /* Number of extra fixed function object slots besides JSSLOT_PRIVATE. */
     static const uint32 CLASS_RESERVED_SLOTS = JSObject::FUN_CLASS_RESERVED_SLOTS;
+    static const uint32 FIRST_FREE_SLOT = JSSLOT_PRIVATE + CLASS_RESERVED_SLOTS + 1;
 };
 
 JS_STATIC_ASSERT(sizeof(JSFunction) % JS_GCTHING_ALIGN == 0);
 
 /*
  * Trace-annotated native. This expands to a JSFunctionSpec initializer (like
  * JS_FN in jsapi.h). fastcall is a FastNative; trcinfo is a
  * JSNativeTraceInfo*.
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -230,17 +230,17 @@ const size_t BITS_PER_GC_CELL = GC_CELL_
 
 const size_t GC_CELLS_PER_ARENA = size_t(1) << (GC_ARENA_SHIFT - GC_CELL_SHIFT);
 const size_t GC_MARK_BITMAP_SIZE = GC_CELLS_PER_ARENA / JS_BITS_PER_BYTE;
 const size_t GC_MARK_BITMAP_WORDS = GC_CELLS_PER_ARENA / JS_BITS_PER_WORD;
 
 JS_STATIC_ASSERT(sizeof(jsbitmap) == sizeof(jsuword));
 
 JS_STATIC_ASSERT(sizeof(JSString) % GC_CELL_SIZE == 0);
-JS_STATIC_ASSERT(JSOBJECT_SIZE % GC_CELL_SIZE == 0);
+JS_STATIC_ASSERT(sizeof(JSObject) % GC_CELL_SIZE == 0);
 JS_STATIC_ASSERT(sizeof(JSFunction) % GC_CELL_SIZE == 0);
 #ifdef JSXML
 JS_STATIC_ASSERT(sizeof(JSXML) % GC_CELL_SIZE == 0);
 #endif
 
 #ifdef JS_GCMETER
 # define METER(x)               ((void) (x))
 # define METER_IF(condition, x) ((void) ((condition) && (x)))
@@ -766,25 +766,19 @@ FreeGCChunks(JSRuntime *rt)
             JS_ALWAYS_TRUE(rt->gcFreeArenaChunks.append(ci));
     }
 }
 
 static inline size_t
 GetFinalizableThingSize(unsigned thingKind)
 {
     JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8);
-    JS_STATIC_ASSERT(FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 == 5);
 
     static const uint8 map[FINALIZE_LIMIT] = {
-        JSOBJECT_SIZE,                      /* FINALIZE_OBJECT0 */
-        JSOBJECT_SIZE + 2 * sizeof(Value),  /* FINALIZE_OBJECT2 */
-        JSOBJECT_SIZE + 4 * sizeof(Value),  /* FINALIZE_OBJECT4 */
-        JSOBJECT_SIZE + 8 * sizeof(Value),  /* FINALIZE_OBJECT8 */
-        JSOBJECT_SIZE + 12 * sizeof(Value), /* FINALIZE_OBJECT12 */
-        JSOBJECT_SIZE + 16 * sizeof(Value), /* FINALIZE_OBJECT16 */
+        sizeof(JSObject),      /* FINALIZE_OBJECT */
         sizeof(JSFunction),    /* FINALIZE_FUNCTION */
 #if JS_HAS_XML_SUPPORT
         sizeof(JSXML),         /* FINALIZE_XML */
 #endif
         sizeof(JSShortString), /* FINALIZE_SHORT_STRING */
         sizeof(JSString),      /* FINALIZE_STRING */
         sizeof(JSString),      /* FINALIZE_EXTERNAL_STRING0 */
         sizeof(JSString),      /* FINALIZE_EXTERNAL_STRING1 */
@@ -801,22 +795,17 @@ GetFinalizableThingSize(unsigned thingKi
 }
 
 static inline size_t
 GetFinalizableTraceKind(size_t thingKind)
 {
     JS_STATIC_ASSERT(JS_EXTERNAL_STRING_LIMIT == 8);
 
     static const uint8 map[FINALIZE_LIMIT] = {
-        JSTRACE_OBJECT,     /* FINALIZE_OBJECT0 */
-        JSTRACE_OBJECT,     /* FINALIZE_OBJECT2 */
-        JSTRACE_OBJECT,     /* FINALIZE_OBJECT4 */
-        JSTRACE_OBJECT,     /* FINALIZE_OBJECT8 */
-        JSTRACE_OBJECT,     /* FINALIZE_OBJECT12 */
-        JSTRACE_OBJECT,     /* FINALIZE_OBJECT16 */
+        JSTRACE_OBJECT,     /* FINALIZE_OBJECT */
         JSTRACE_OBJECT,     /* FINALIZE_FUNCTION */
 #if JS_HAS_XML_SUPPORT      /* FINALIZE_XML */
         JSTRACE_XML,
 #endif
         JSTRACE_STRING,     /* FINALIZE_SHORT_STRING */
         JSTRACE_STRING,     /* FINALIZE_STRING */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING0 */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING1 */
@@ -1555,32 +1544,18 @@ CheckGCFreeListLink(JSGCThing *thing)
      * the free list are linked in ascending address order.
      */
     JS_ASSERT_IF(thing->link,
                  JSGCArena::fromGCThing(thing) ==
                  JSGCArena::fromGCThing(thing->link));
     JS_ASSERT_IF(thing->link, thing < thing->link);
 }
 
-JSFinalizeGCThingKind js_GCObjectSlotsToThingKind[] = {
-    /* 0 */  FINALIZE_OBJECT0,  FINALIZE_OBJECT2,  FINALIZE_OBJECT2,  FINALIZE_OBJECT4,
-    /* 4 */  FINALIZE_OBJECT4,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,
-    /* 8 */  FINALIZE_OBJECT8,  FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
-    /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16,
-    /* 16 */ FINALIZE_OBJECT16, FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,
-    /* 20 */ FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,
-    /* 24 */ FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,
-    /* 28 */ FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,  FINALIZE_OBJECT0,
-    /* 32 */ FINALIZE_OBJECT0
-};
-
-JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_GCObjectSlotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT);
-
 void *
-js_NewFinalizableGCThing(JSContext *cx, JSFinalizeGCThingKind thingKind)
+js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
 {
     JS_ASSERT(thingKind < FINALIZE_LIMIT);
 #ifdef JS_THREADSAFE
     JS_ASSERT(cx->thread);
 #endif
 
     /* Updates of metering counters here may not be thread-safe. */
     METER(cx->runtime->gcArenaStats[thingKind].alloc++);
@@ -1608,23 +1583,16 @@ js_NewFinalizableGCThing(JSContext *cx, 
     JS_ASSERT(!*freeListp || *freeListp == thing);
     *freeListp = thing->link;
 
     CheckGCFreeListLink(thing);
 
     return thing;
 }
 
-JSFinalizeGCThingKind
-js_KindFromGCThing(const void *thing)
-{
-    JSGCArenaInfo *info = JSGCArenaInfo::fromGCThing((void*)thing);
-    return (JSFinalizeGCThingKind) info->list->thingKind;
-}
-
 JSBool
 js_LockGCThingRT(JSRuntime *rt, void *thing)
 {
     GCLocks *locks;
 
     if (!thing)
         return true;
     locks = &rt->gcLocksHash;
@@ -1671,24 +1639,18 @@ JS_TraceChildren(JSTracer *trc, void *th
         if (!obj->map)
             break;
 
         /* Trace universal (ops-independent) members. */
         if (JSObject *proto = obj->getProto())
             JS_CALL_OBJECT_TRACER(trc, proto, "proto");
         if (JSObject *parent = obj->getParent())
             JS_CALL_OBJECT_TRACER(trc, parent, "parent");
-
-        if (obj->emptyShapes) {
-            int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1;
-            for (int i = 0; i < count; i++) {
-                if (obj->emptyShapes[i])
-                    obj->emptyShapes[i]->trace(trc);
-            }
-        }
+        if (obj->emptyShape)
+            obj->emptyShape->trace(trc);
 
         /* Delegate to ops or the native marking op. */
         JSTraceOp op = obj->getOps()->trace;
         (op ? op : js_TraceObject)(trc, obj);
         break;
       }
 
       case JSTRACE_STRING: {
@@ -2382,17 +2344,17 @@ js_DestroyScriptsToGC(JSContext *cx, JST
             js_DestroyScript(cx, script);
         }
     }
 }
 
 inline void
 FinalizeObject(JSContext *cx, JSObject *obj, unsigned thingKind)
 {
-    JS_ASSERT((thingKind >= FINALIZE_OBJECT0 && thingKind <= FINALIZE_OBJECT_LAST) ||
+    JS_ASSERT(thingKind == FINALIZE_OBJECT ||
               thingKind == FINALIZE_FUNCTION);
 
     /* Cope with stillborn objects that have no map. */
     if (!obj->map)
         return;
 
     /* Finalize obj first, in case it needs map and slots. */
     Class *clasp = obj->getClass();
@@ -2521,24 +2483,24 @@ js_FinalizeStringRT(JSRuntime *rt, JSStr
                  * string knows how to deal with null context.
                  */
                 finalizer(NULL, str);
             }
         }
     }
 }
 
-template<typename T, unsigned SIZE,
+template<typename T,
          void finalizer(JSContext *cx, T *thing, unsigned thingKind)>
 static void
 FinalizeArenaList(JSContext *cx, unsigned thingKind)
 {
-    JS_STATIC_ASSERT(!(SIZE & GC_CELL_MASK));
+    JS_STATIC_ASSERT(!(sizeof(T) & GC_CELL_MASK));
     JSGCArenaList *arenaList = &cx->runtime->gcArenaList[thingKind];
-    JS_ASSERT(SIZE == arenaList->thingSize);
+    JS_ASSERT(sizeof(T) == arenaList->thingSize);
 
     JSGCArena **ap = &arenaList->head;
     JSGCArena *a = *ap;
     if (!a)
         return;
 
 #ifdef JS_GCMETER
     uint32 nlivearenas = 0, nkilledarenas = 0, nthings = 0;
@@ -2549,30 +2511,29 @@ FinalizeArenaList(JSContext *cx, unsigne
         JS_ASSERT(!a->getMarkingDelay()->link);
         JS_ASSERT(a->getMarkingDelay()->unmarkedChildren == 0);
 
         JSGCThing *freeList = NULL;
         JSGCThing **tailp = &freeList;
         bool allClear = true;
 
         jsuword thing = a->toPageStart();
-        jsuword thingsEnd = thing + GC_ARENA_SIZE - (GC_ARENA_SIZE % SIZE);
-        JS_ASSERT((thingsEnd - thing) % SIZE == 0);
+        jsuword thingsEnd = thing + GC_ARENA_SIZE / sizeof(T) * sizeof(T);
 
         jsuword nextFree = reinterpret_cast<jsuword>(ainfo->freeList);
         if (!nextFree) {
             nextFree = thingsEnd;
         } else {
             JS_ASSERT(thing <= nextFree);
             JS_ASSERT(nextFree < thingsEnd);
         }
 
         jsuword gcCellIndex = 0;
         jsbitmap *bitmap = a->getMarkBitmap();
-        for (;; thing += SIZE, gcCellIndex += SIZE >> GC_CELL_SHIFT) {
+        for (;; thing += sizeof(T), gcCellIndex += sizeof(T) >> GC_CELL_SHIFT) {
             if (thing == nextFree) {
                 if (thing == thingsEnd)
                     break;
                 nextFree = reinterpret_cast<jsuword>(
                     reinterpret_cast<JSGCThing *>(nextFree)->link);
                 if (!nextFree) {
                     nextFree = thingsEnd;
                 } else {
@@ -2582,17 +2543,17 @@ FinalizeArenaList(JSContext *cx, unsigne
             } else if (JS_TEST_BIT(bitmap, gcCellIndex)) {
                 allClear = false;
                 METER(nthings++);
                 continue;
             } else {
                 T *t = reinterpret_cast<T *>(thing);
                 finalizer(cx, t, thingKind);
 #ifdef DEBUG
-                memset(t, JS_FREE_PATTERN, SIZE);
+                memset(t, JS_FREE_PATTERN, sizeof(T));
 #endif
             }
             JSGCThing *t = reinterpret_cast<JSGCThing *>(thing);
             *tailp = t;
             tailp = &t->link;
         }
 
 #ifdef DEBUG
@@ -2610,22 +2571,22 @@ FinalizeArenaList(JSContext *cx, unsigne
             }
         }
 #endif
         if (allClear) {
             /*
              * Forget just assembled free list head for the arena and
              * add the arena itself to the destroy list.
              */
-            JS_ASSERT(nfree == ThingsPerArena(SIZE));
+            JS_ASSERT(nfree == ThingsPerArena(sizeof(T)));
             *ap = ainfo->prev;
             ReleaseGCArena(cx->runtime, a);
             METER(nkilledarenas++);
         } else {
-            JS_ASSERT(nfree < ThingsPerArena(SIZE));
+            JS_ASSERT(nfree < ThingsPerArena(sizeof(T)));
             *tailp = NULL;
             ainfo->freeList = freeList;
             ap = &ainfo->prev;
             METER(nlivearenas++);
         }
         if (!(a = *ap))
             break;
     }
@@ -2918,47 +2879,35 @@ MarkAndSweep(JSContext *cx  GCTIMER_PARA
 
     /*
      * We finalize iterators before other objects so the iterator can use the
      * object which properties it enumerates over to finalize the enumeration
      * state. We finalize objects before other GC things to ensure that
      * object's finalizer can access them even if they will be freed.
      */
     JS_ASSERT(!rt->gcEmptyArenaList);
-    FinalizeArenaList<JSObject, JSOBJECT_SIZE,
-                      FinalizeObject>(cx, FINALIZE_OBJECT0);
-    FinalizeArenaList<JSObject, JSOBJECT_SIZE + sizeof(Value) * 2,
-                      FinalizeObject>(cx, FINALIZE_OBJECT2);
-    FinalizeArenaList<JSObject, JSOBJECT_SIZE + sizeof(Value) * 4,
-                      FinalizeObject>(cx, FINALIZE_OBJECT4);
-    FinalizeArenaList<JSObject, JSOBJECT_SIZE + sizeof(Value) * 8,
-                      FinalizeObject>(cx, FINALIZE_OBJECT8);
-    FinalizeArenaList<JSObject, JSOBJECT_SIZE + sizeof(Value) * 12,
-                      FinalizeObject>(cx, FINALIZE_OBJECT12);
-    FinalizeArenaList<JSObject, JSOBJECT_SIZE + sizeof(Value) * 16,
-                      FinalizeObject>(cx, FINALIZE_OBJECT16);
-    FinalizeArenaList<JSFunction, sizeof(JSFunction), FinalizeFunction>(cx, FINALIZE_FUNCTION);
+    FinalizeArenaList<JSObject, FinalizeObject>(cx, FINALIZE_OBJECT);
+    FinalizeArenaList<JSFunction, FinalizeFunction>(cx, FINALIZE_FUNCTION);
 #if JS_HAS_XML_SUPPORT
-    FinalizeArenaList<JSXML, sizeof(JSXML), FinalizeXML>(cx, FINALIZE_XML);
+    FinalizeArenaList<JSXML, FinalizeXML>(cx, FINALIZE_XML);
 #endif
     TIMESTAMP(sweepObjectEnd);
 
     /*
      * We sweep the deflated cache before we finalize the strings so the
      * cache can safely use js_IsAboutToBeFinalized..
      */
     rt->deflatedStringCache->sweep(cx);
 
-    FinalizeArenaList<JSShortString, sizeof(JSShortString),
-                      FinalizeShortString>(cx, FINALIZE_SHORT_STRING);
-    FinalizeArenaList<JSString, sizeof(JSString), FinalizeString>(cx, FINALIZE_STRING);
+    FinalizeArenaList<JSShortString, FinalizeShortString>(cx, FINALIZE_SHORT_STRING);
+    FinalizeArenaList<JSString, FinalizeString>(cx, FINALIZE_STRING);
     for (unsigned i = FINALIZE_EXTERNAL_STRING0;
          i <= FINALIZE_EXTERNAL_STRING_LAST;
          ++i) {
-        FinalizeArenaList<JSString, sizeof(JSString), FinalizeExternalString>(cx, i);
+        FinalizeArenaList<JSString, FinalizeExternalString>(cx, i);
     }
 
     rt->gcNewArenaTriggerBytes = rt->gcBytes < GC_ARENA_ALLOCATION_TRIGGER ?
                                  GC_ARENA_ALLOCATION_TRIGGER :
                                  rt->gcBytes;
 
     TIMESTAMP(sweepStringEnd);
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -231,23 +231,17 @@ js_WaitForGC(JSRuntime *rt);
 
 #endif
 
 /*
  * The kind of GC thing with a finalizer. The external strings follow the
  * ordinary string to simplify js_GetExternalStringGCType.
  */
 enum JSFinalizeGCThingKind {
-    FINALIZE_OBJECT0,
-    FINALIZE_OBJECT2,
-    FINALIZE_OBJECT4,
-    FINALIZE_OBJECT8,
-    FINALIZE_OBJECT12,
-    FINALIZE_OBJECT16,
-    FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16,
+    FINALIZE_OBJECT,
     FINALIZE_FUNCTION,
 #if JS_HAS_XML_SUPPORT
     FINALIZE_XML,
 #endif
     FINALIZE_SHORT_STRING,
     FINALIZE_STRING,
     FINALIZE_EXTERNAL_STRING0,
     FINALIZE_EXTERNAL_STRING1,
@@ -270,71 +264,22 @@ IsFinalizableStringKind(unsigned thingKi
 
 /*
  * Allocates a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
  */
 extern void *
-js_NewFinalizableGCThing(JSContext *cx, JSFinalizeGCThingKind thingKind);
-
-/* Get the kind which was used when making a GC thing. */
-extern JSFinalizeGCThingKind
-js_KindFromGCThing(const void *thing);
-
-/* Maximum number of fixed slots for an object. */
-const size_t JSOBJECT_FIXED_SLOTS_LIMIT = 16;
-
-/* Capacity for js_GCObjectSlotsToThingKind */
-const size_t SLOTS_TO_THING_KIND_LIMIT = 33;
-
-/* Get the best kind to use when making an object with the given slot count. */
-static inline JSFinalizeGCThingKind
-js_GetGCObjectKind(size_t numSlots)
-{
-    extern JSFinalizeGCThingKind js_GCObjectSlotsToThingKind[];
-
-    if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
-        return FINALIZE_OBJECT0;
-    return js_GCObjectSlotsToThingKind[numSlots];
-}
-
-/* Get the number of fixed slots and initial capacity associated with a kind. */
-static inline size_t
-js_GetGCKindSlots(JSFinalizeGCThingKind thingKind)
-{
-    /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
-    switch (thingKind) {
-      case FINALIZE_OBJECT0:
-        return 0;
-      case FINALIZE_OBJECT2:
-        return 2;
-      case FINALIZE_OBJECT4:
-        return 4;
-      case FINALIZE_OBJECT8:
-        return 8;
-      case FINALIZE_OBJECT12:
-        return 12;
-      case FINALIZE_OBJECT16:
-        return 16;
-      default:
-        JS_NOT_REACHED("Bad object finalize kind");
-        return 0;
-    }
-}
+js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind);
 
 static inline JSObject *
-js_NewGCObject(JSContext *cx, JSFinalizeGCThingKind thingKind)
+js_NewGCObject(JSContext *cx)
 {
-    JS_ASSERT(thingKind >= FINALIZE_OBJECT0 && thingKind <= FINALIZE_OBJECT_LAST);
-    JSObject *obj = (JSObject *) js_NewFinalizableGCThing(cx, thingKind);
-    if (obj)
-        obj->capacity = js_GetGCKindSlots(thingKind);
-    return obj;
+    return (JSObject *) js_NewFinalizableGCThing(cx, FINALIZE_OBJECT);
 }
 
 static inline JSString *
 js_NewGCString(JSContext *cx)
 {
     return (JSString *) js_NewFinalizableGCThing(cx, FINALIZE_STRING);
 }
 
@@ -346,31 +291,30 @@ js_NewGCShortString(JSContext *cx)
     return (JSShortString *) js_NewFinalizableGCThing(cx, FINALIZE_SHORT_STRING);
 }
 
 static inline JSString *
 js_NewGCExternalString(JSContext *cx, uintN type)
 {
     JS_ASSERT(type < JS_EXTERNAL_STRING_LIMIT);
     type += FINALIZE_EXTERNAL_STRING0;
-    return (JSString *) js_NewFinalizableGCThing(cx, JSFinalizeGCThingKind(type));
+    return (JSString *) js_NewFinalizableGCThing(cx, type);
 }
 
 static inline JSFunction *
 js_NewGCFunction(JSContext *cx)
 {
     JSFunction* obj = (JSFunction *)js_NewFinalizableGCThing(cx, FINALIZE_FUNCTION);
 
+#ifdef DEBUG
     if (obj) {
-        obj->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS;
-#ifdef DEBUG
         memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN,
                sizeof(JSFunction) - sizeof(JSObject));
+    }
 #endif
-    }
 
     return obj;
 }
 
 #if JS_HAS_XML_SUPPORT
 static inline JSXML *
 js_NewGCXML(JSContext *cx)
 {
@@ -467,23 +411,20 @@ class GCHelperThread {
     /* Must be called with GC lock taken. */
     void startBackgroundSweep(JSRuntime *rt);
     
     /* Must be called outside the GC lock. */
     void waitBackgroundSweepEnd(JSRuntime *rt);
     
     void freeLater(void *ptr) {
         JS_ASSERT(!sweeping);
-        js_free(ptr);
-        /*
         if (freeCursor != freeCursorEnd)
             *freeCursor++ = ptr;
         else
             replenishAndFreeLater(ptr);
-        */
     }
 };
 
 #endif /* JS_THREADSAFE */
 
 
 struct GCChunkInfo;
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -325,18 +325,18 @@ ComputeThisFromArgv(JSContext *cx, Value
     JS_ASSERT(IsSaneThisObject(argv[-1].toObject()));
     return true;
 }
 
 }
 
 #if JS_HAS_NO_SUCH_METHOD
 
-const uint32 JSSLOT_FOUND_FUNCTION  = 0;
-const uint32 JSSLOT_SAVED_ID        = 1;
+const uint32 JSSLOT_FOUND_FUNCTION  = JSSLOT_PRIVATE;
+const uint32 JSSLOT_SAVED_ID        = JSSLOT_PRIVATE + 1;
 
 Class js_NoSuchMethodClass = {
     "NoSuchMethod",
     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
     PropertyStub,   /* addProperty */
     PropertyStub,   /* delProperty */
     PropertyStub,   /* getProperty */
     PropertyStub,   /* setProperty */
@@ -377,30 +377,29 @@ js_OnUnknownMethod(JSContext *cx, Value 
         if (vp[0].isObject()) {
             obj = &vp[0].toObject();
             if (!js_IsFunctionQName(cx, obj, &id))
                 return false;
             if (!JSID_IS_VOID(id))
                 vp[0] = IdToValue(id);
         }
 #endif
-        obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
+        obj = js_NewGCObject(cx);
         if (!obj)
             return false;
 
         /*
          * Null map to cause prompt and safe crash if this object were to
          * escape due to a bug. This will make the object appear to be a
          * stillborn instance that needs no finalization, which is sound:
          * NoSuchMethod helper objects own no manually allocated resources.
          */
         obj->map = NULL;
-        obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false);
-        obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
-        obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
+        obj->init(&js_NoSuchMethodClass, NULL, NULL, tvr.value(), cx);
+        obj->fslots[JSSLOT_SAVED_ID] = vp[0];
         vp[0].setObject(*obj);
     }
     return true;
 }
 
 static JS_REQUIRES_STACK JSBool
 NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
 {
@@ -408,19 +407,19 @@ NoSuchMethod(JSContext *cx, uintN argc, 
     if (!cx->stack().pushInvokeArgs(cx, 2, &args))
         return JS_FALSE;
 
     JS_ASSERT(vp[0].isObject());
     JS_ASSERT(vp[1].isObject());
     JSObject *obj = &vp[0].toObject();
     JS_ASSERT(obj->getClass() == &js_NoSuchMethodClass);
 
-    args.callee() = obj->getSlot(JSSLOT_FOUND_FUNCTION);
+    args.callee() = obj->fslots[JSSLOT_FOUND_FUNCTION];
     args.thisv() = vp[1];
-    args[0] = obj->getSlot(JSSLOT_SAVED_ID);
+    args[0] = obj->fslots[JSSLOT_SAVED_ID];
     JSObject *argsobj = js_NewArrayObject(cx, argc, vp + 2);
     if (!argsobj)
         return JS_FALSE;
     args[1].setObject(*argsobj);
     JSBool ok = (flags & JSINVOKE_CONSTRUCT)
                 ? InvokeConstructor(cx, args)
                 : Invoke(cx, args, flags);
     vp[0] = args.rval();
@@ -5647,30 +5646,25 @@ BEGIN_CASE(JSOP_NEWARRAY)
         goto error;
     regs.sp -= len - 1;
     regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_NEWARRAY)
 
 BEGIN_CASE(JSOP_NEWINIT)
 {
-    jsint i = GET_UINT16(regs.pc);
-    jsint count = GET_UINT16(regs.pc + UINT16_LEN);
-
+    jsint i = GET_INT8(regs.pc);
     JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
     JSObject *obj;
-
-    JSFinalizeGCThingKind kind = GuessObjectGCKind(count);
-
     if (i == JSProto_Array) {
-        obj = NewArrayWithKind(cx, kind);
+        obj = js_NewArrayObject(cx, 0, NULL);
         if (!obj)
             goto error;
     } else {
-        obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
+        obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
         if (!obj)
             goto error;
     }
 
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWINIT)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -283,17 +283,17 @@ EnumerateDenseArrayProperties(JSContext 
 {
     if (!Enumerate<EnumPolicy>(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true,
                                flags, ht, props)) {
         return false;
     }
 
     if (pobj->getArrayLength() > 0) {
         size_t capacity = pobj->getDenseArrayCapacity();
-        Value *vp = pobj->getDenseArrayElements();
+        Value *vp = pobj->dslots;
         for (size_t i = 0; i < capacity; ++i, ++vp) {
             if (!vp->isMagic(JS_ARRAY_HOLE)) {
                 /* Dense arrays never get so large that i would not fit into an integer id. */
                 if (!Enumerate<EnumPolicy>(cx, obj, pobj, INT_TO_JSID(i), true, false, flags, ht, props))
                     return false;
             }
         }
     }
@@ -450,20 +450,20 @@ NewIteratorObject(JSContext *cx, uintN f
         /*
          * Non-escaping native enumerator objects do not need map, proto, or
          * parent. However, code in jstracer.cpp and elsewhere may find such a
          * native enumerator object via the stack and (as for all objects that
          * are not stillborn, with the exception of "NoSuchMethod" internal
          * helper objects) expect it to have a non-null map pointer, so we
          * share an empty Enumerator scope in the runtime.
          */
-        JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0);
+        JSObject *obj = js_NewGCObject(cx);
         if (!obj)
             return false;
-        obj->init(cx, &js_IteratorClass, NULL, NULL, NULL, false);
+        obj->init(&js_IteratorClass, NULL, NULL, NullValue(), cx);
         obj->setMap(cx->runtime->emptyEnumeratorShape);
         return obj;
     }
 
     return NewBuiltinClassInstance(cx, &js_IteratorClass);
 }
 
 NativeIterator *
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -502,17 +502,18 @@ static void
 FinishSharingTitle(JSContext *cx, JSTitle *title)
 {
     js_InitLock(&title->lock);
     title->u.count = 0;     /* NULL may not pun as 0 */
 
     JSObject *obj = TITLE_TO_OBJECT(title);
     if (obj) {
         uint32 nslots = obj->slotSpan();
-        for (uint32 i = 0; i != nslots; ++i) {
+        JS_ASSERT(nslots >= JSSLOT_START(obj->getClass()));
+        for (uint32 i = JSSLOT_START(obj->getClass()); i != nslots; ++i) {
             Value v = obj->getSlot(i);
             if (v.isString() &&
                 !js_MakeStringImmutable(cx, v.toString())) {
                 /*
                  * FIXME bug 363059: The following error recovery changes
                  * runtime execution semantics, arbitrarily and silently
                  * ignoring errors except out-of-memory, which should have been
                  * reported through JS_ReportOutOfMemory at this point.
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2605,62 +2605,57 @@ js_NewInstance(JSContext *cx, JSObject *
     JSObject *parent = callee->getParent();
     return NewObject<WithProto::Class>(cx, newclasp, proto, parent);
 }
 
 #ifdef JS_TRACER
 
 static JS_ALWAYS_INLINE JSObject*
 NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
-                        /*JSFinalizeGCThingKind*/ unsigned _kind)
+                        const Value &privateSlotValue)
 {
     JS_ASSERT(clasp->isNative());
-    JSFinalizeGCThingKind kind = JSFinalizeGCThingKind(_kind);
-
-    JSObject* obj = js_NewGCObject(cx, kind);
+
+    JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
-    if (!obj->initSharingEmptyShape(cx, clasp, proto, proto->getParent(), NULL, kind))
-        return NULL;
+    obj->initSharingEmptyShape(clasp, proto, proto->getParent(), privateSlotValue, cx);
     return obj;
 }
 
 JSObject* FASTCALL
 js_Object_tn(JSContext* cx, JSObject* proto)
 {
     JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
-    return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8);
+
+    return NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue());
 }
 
 JS_DEFINE_TRCINFO_1(js_Object,
     (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0,
          nanojit::ACCSET_STORE_ANY)))
 
 JSObject* FASTCALL
 js_NonEmptyObject(JSContext* cx, JSObject* proto)
 {
     JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
 
-    JSObject *obj = NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8);
+    JSObject *obj = NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue());
     return (obj && obj->ensureClassReservedSlotsForEmptyObject(cx)) ? obj : NULL;
 }
 
 JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY, js_NonEmptyObject, CONTEXT, CALLEE_PROTOTYPE, 0,
                      nanojit::ACCSET_STORE_ANY)
 
 JSObject* FASTCALL
 js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
 {
     JS_ASSERT(JS_ON_TRACE(cx));
-    JSObject *obj = NewObjectWithClassProto(cx, &js_StringClass, proto, FINALIZE_OBJECT2);
-    if (!obj)
-        return NULL;
-    obj->setPrimitiveThis(StringValue(str));
-    return obj;
+    return NewObjectWithClassProto(cx, &js_StringClass, proto, StringValue(str));
 }
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0,
                      nanojit::ACCSET_STORE_ANY)
 
 JSObject* FASTCALL
 js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor)
 {
     JS_ASSERT(JS_ON_TRACE(cx));
@@ -2925,23 +2920,21 @@ Class js_WithClass = {
     }
 };
 
 JS_REQUIRES_STACK JSObject *
 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
 {
     JSObject *obj;
 
-    obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
+    obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
-    JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
-
-    obj->init(cx, &js_WithClass, proto, parent, priv, false);
+    obj->init(&js_WithClass, proto, parent, js_FloatingFrameIfGenerator(cx, cx->fp()), cx);
     obj->setMap(cx->runtime->emptyWithShape);
     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
 
     AutoObjectRooter tvr(cx, obj);
     JSObject *thisp = proto->thisObject(cx);
     if (!thisp)
         return NULL;
 
@@ -2951,76 +2944,82 @@ js_NewWithObject(JSContext *cx, JSObject
 
 JSObject *
 js_NewBlockObject(JSContext *cx)
 {
     /*
      * Null obj's proto slot so that Object.prototype.* does not pollute block
      * scopes and to give the block object its own scope.
      */
-    JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2);
+    JSObject *blockObj = js_NewGCObject(cx);
     if (!blockObj)
         return NULL;
 
-    blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false);
+    blockObj->init(&js_BlockClass, NULL, NULL, NullValue(), cx);
     blockObj->setMap(cx->runtime->emptyBlockShape);
     return blockObj;
 }
 
 JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
 {
     JS_ASSERT(proto->isStaticBlock());
 
-    size_t count = OBJ_BLOCK_COUNT(cx, proto);
-    JSFinalizeGCThingKind kind = js_GetGCObjectKind(count + 1);
-
-    JSObject *clone = js_NewGCObject(cx, kind);
+    JSObject *clone = js_NewGCObject(cx);
     if (!clone)
         return NULL;
 
     JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
 
     /* The caller sets parent on its own. */
-    clone->init(cx, &js_BlockClass, proto, NULL, priv, false);
+    clone->init(&js_BlockClass, proto, NULL, priv, cx);
+    clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH];
 
     clone->setMap(proto->map);
-    if (!clone->ensureInstanceReservedSlots(cx, count + 1))
+    if (!clone->ensureInstanceReservedSlots(cx, OBJ_BLOCK_COUNT(cx, proto)))
         return NULL;
 
-    clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
-
     JS_ASSERT(clone->isClonedBlock());
     return clone;
 }
 
 JS_REQUIRES_STACK JSBool
 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
 {
+    /* Blocks have one fixed slot available for the first local.*/
+    JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
+
     JSStackFrame *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->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count);
+    JS_ASSERT(obj->numSlots() == 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 from jsparse.cpp. */
     JS_ASSERT(count >= 1);
 
     if (normalUnwind) {
         uintN slot = JSSLOT_BLOCK_DEPTH + 1;
+        uintN flen = JS_MIN(count, JS_INITIAL_NSLOTS - slot);
+        uintN stop = slot + flen;
+
         depth += fp->numFixed();
-        memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value));
+        while (slot < stop)
+            obj->fslots[slot++] = fp->slots()[depth++];
+        count -= flen;
+        if (count != 0)
+            memcpy(obj->dslots, fp->slots() + depth, count * sizeof(Value));
     }
 
     /* We must clear the private slot even with errors. */
     obj->setPrivate(NULL);
     fp->setScopeChainNoCallObj(*obj->getParent());
     return normalUnwind;
 }
 
@@ -3091,50 +3090,36 @@ JSObject::defineBlockVariable(JSContext 
     return shape;
 }
 
 static size_t
 GetObjectSize(JSObject *obj)
 {
     return (obj->isFunction() && !obj->getPrivate())
            ? sizeof(JSFunction)
-           : JSOBJECT_SIZE + obj->numFixedSlots() * sizeof(Value);
+           : sizeof(JSObject);
 }
 
 /*
  * Use this method with extreme caution. It trades the guts of two objects and updates
  * scope ownership. This operation is not thread-safe, just as fast array to slow array
  * transitions are inherently not thread-safe. Don't perform a swap operation on objects
  * shared across threads or, or bad things will happen. You have been warned.
  */
-bool
-JSObject::swap(JSContext *cx, JSObject *other)
+void
+JSObject::swap(JSObject *other)
 {
     size_t size = GetObjectSize(this);
     JS_ASSERT(size == GetObjectSize(other));
 
-    bool thisInline = !hasSlotsArray();
-    bool otherInline = !other->hasSlotsArray();
-
-    char tmp[tl::Max<sizeof(JSFunction),
-                      JSOBJECT_SIZE + JSOBJECT_FIXED_SLOTS_LIMIT * sizeof(Value)>::result];
-    JS_ASSERT(size <= sizeof(tmp));
-
     /* Trade the guts of the objects. */
+    char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject)>::result];
     memcpy(tmp, this, size);
     memcpy(this, other, size);
     memcpy(other, tmp, size);
-
-    /* Fixup pointers for inline slots on the objects. */
-    if (thisInline)
-        other->slots = other->fixedSlots;
-    if (otherInline)
-        this->slots = this->fixedSlots;
-
-    return true;
 }
 
 #if JS_HAS_XDR
 
 #define NO_PARENT_INDEX ((uint32)-1)
 
 uint32
 FindObjectIndex(JSObjectArray *array, JSObject *obj)
@@ -3310,22 +3295,23 @@ DefineStandardSlot(JSContext *cx, JSObje
         JS_LOCK_OBJ(cx, obj);
         if (!obj->ensureClassReservedSlots(cx)) {
             JS_UNLOCK_OBJ(cx, obj);
             return false;
         }
 
         const Shape *shape = obj->nativeLookup(id);
         if (!shape) {
-            uint32 slot = 2 * JSProto_LIMIT + key;
-            if (!js_SetReservedSlot(cx, obj, slot, v)) {
+            uint32 index = 2 * JSProto_LIMIT + key;
+            if (!js_SetReservedSlot(cx, obj, index, v)) {
                 JS_UNLOCK_OBJ(cx, obj);
                 return false;
             }
 
+            uint32 slot = JSSLOT_START(obj->getClass()) + index;
             shape = obj->addProperty(cx, id, PropertyStub, PropertyStub, slot, attrs, 0, 0);
 
             JS_UNLOCK_OBJ(cx, obj);
             if (!shape)
                 return false;
 
             named = true;
             return true;
@@ -3481,17 +3467,17 @@ js_InitClass(JSContext *cx, JSObject *ob
      *
      * All callers of JSObject::initSharingEmptyShape depend on this.
      *
      * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
      * and make the Array.prototype slow from the start.
      */
     JS_ASSERT_IF(proto->clasp != clasp,
                  clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass);
-    if (!proto->getEmptyShape(cx, proto->clasp, FINALIZE_OBJECT0))
+    if (!proto->getEmptyShape(cx, proto->clasp))
         goto bad;
 
     /* If this is a standard class, cache its prototype. */
     if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
         goto bad;
 
     return proto;
 
@@ -3499,113 +3485,121 @@ bad:
     if (named) {
         Value rval;
         obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, false);
     }
     return NULL;
 }
 
 bool
-JSObject::allocSlots(JSContext *cx, size_t newcap)
-{
-    uint32 oldcap = numSlots();
-
-    JS_ASSERT(newcap >= oldcap && !hasSlotsArray());
-
-    if (newcap > NSLOTS_LIMIT) {
-        if (!JS_ON_TRACE(cx))
-            js_ReportAllocationOverflow(cx);
+JSObject::allocSlots(JSContext *cx, size_t nslots)
+{
+    JS_ASSERT(!dslots);
+    JS_ASSERT(nslots > JS_INITIAL_NSLOTS);
+
+    size_t nwords = slotsToDynamicWords(nslots);
+    dslots = (Value*) cx->malloc(nwords * sizeof(Value));
+    if (!dslots)
         return false;
-    }
-
-    Value *tmpslots = (Value*) cx->malloc(newcap * sizeof(Value));
-    if (!tmpslots)
-        return false;  /* Leave slots at inline buffer. */
-    slots = tmpslots;
-    capacity = newcap;
-
-    /* Copy over anything from the inline buffer. */
-    memcpy(slots, fixedSlots, oldcap * sizeof(Value));
-    ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray());
+
+    dslots++;
+    dslots[-1].setPrivateUint32(nslots);
+    SetValueRangeToUndefined(dslots, nslots - JS_INITIAL_NSLOTS);
     return true;
 }
 
 bool
-JSObject::growSlots(JSContext *cx, size_t newcap)
+JSObject::growSlots(JSContext *cx, size_t nslots)
 {
     /*
-     * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
-     * grow, double its capacity, to add N elements in amortized O(N) time.
-     *
-     * Above this limit, grow by 12.5% each time. Speed is still amortized
-     * O(N), with a higher constant factor, and we waste less space.
+     * Minimal number of dynamic slots to allocate.
      */
-    static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
-    static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
-
-    uint32 oldcap = numSlots();
-    JS_ASSERT(oldcap < newcap);
-
-    uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
-                    ? oldcap * 2
-                    : oldcap + (oldcap >> 3);
-
-    uint32 actualCapacity = JS_MAX(newcap, nextsize);
-    if (actualCapacity >= CAPACITY_CHUNK)
-        actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
-    else if (actualCapacity < SLOT_CAPACITY_MIN)
-        actualCapacity = SLOT_CAPACITY_MIN;
+    const size_t MIN_DYNAMIC_WORDS = 4;
+
+    /*
+     * The limit to switch to linear allocation strategy from the power of 2
+     * growth no to waste too much memory.
+     */
+    const size_t LINEAR_GROWTH_STEP = JS_BIT(16);
+
+    /* If we are allocating fslots, there is nothing to do. */
+    if (nslots <= JS_INITIAL_NSLOTS)
+        return true;
 
     /* Don't let nslots get close to wrapping around uint32. */
-    if (actualCapacity >= NSLOTS_LIMIT) {
+    if (nslots >= NSLOTS_LIMIT) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
-    /* If nothing was allocated yet, treat it as initial allocation. */
-    if (!hasSlotsArray())
-        return allocSlots(cx, actualCapacity);
-
-    Value *tmpslots = (Value*) cx->realloc(slots, actualCapacity * sizeof(Value));
-    if (!tmpslots)
-        return false;    /* Leave dslots as its old size. */
-    slots = tmpslots;
-    capacity = actualCapacity;
+    size_t nwords = slotsToDynamicWords(nslots);
+
+    /*
+     * Round up nslots so the number of bytes in dslots array is power
+     * of 2 to ensure exponential grouth.
+     */
+    uintN log;
+    if (nwords <= MIN_DYNAMIC_WORDS) {
+        nwords = MIN_DYNAMIC_WORDS;
+    } else if (nwords < LINEAR_GROWTH_STEP) {
+        JS_CEILING_LOG2(log, nwords);
+        nwords = JS_BIT(log);
+    } else {
+        nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
+    }
+    nslots = dynamicWordsToSlots(nwords);
+
+    /*
+     * If nothing was allocated yet, treat it as initial allocation (but with
+     * the exponential growth algorithm applied).
+     */
+    if (!dslots)
+        return allocSlots(cx, nslots);
+
+    size_t oldnslots = dslots[-1].toPrivateUint32();
+
+    Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value));
+    if (!tmpdslots)
+        return false;   /* leave dslots at its old size */
+
+    dslots = tmpdslots;
+    dslots++;
+    dslots[-1].setPrivateUint32(nslots);
 
     /* Initialize the additional slots we added. */
-    ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray());
+    JS_ASSERT(nslots > oldnslots);
+    Value *beg = dslots + (oldnslots - JS_INITIAL_NSLOTS);
+    Value *end = dslots + (nslots - JS_INITIAL_NSLOTS);
+    SetValueRangeToUndefined(beg, end);
+
     return true;
 }
 
 void
-JSObject::shrinkSlots(JSContext *cx, size_t newcap)
-{
-    uint32 oldcap = numSlots();
-    JS_ASSERT(newcap <= oldcap);
-    JS_ASSERT(newcap >= slotSpan());
-
-    if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) {
-        /* We won't shrink the slots any more.  Clear excess holes. */
-        ClearValueRange(slots + newcap, oldcap - newcap, isDenseArray());
+JSObject::shrinkSlots(JSContext *cx, size_t nslots)
+{
+    /* Nothing to shrink? */
+    if (!dslots)
         return;
-    }
-
-    uint32 fill = newcap;
-    if (newcap < SLOT_CAPACITY_MIN)
-        newcap = SLOT_CAPACITY_MIN;
-
-    Value *tmpslots = (Value*) cx->realloc(slots, newcap * sizeof(Value));
-    if (!tmpslots)
-        return;  /* Leave slots at its old size. */
-    slots = tmpslots;
-    capacity = newcap;
-
-    if (fill < newcap) {
-        /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
-        ClearValueRange(slots + fill, newcap - fill, isDenseArray());
+
+    JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS);
+    JS_ASSERT(nslots <= dslots[-1].toPrivateUint32());
+
+    if (nslots <= JS_INITIAL_NSLOTS) {
+        freeSlotsArray(cx);
+        dslots = NULL;
+    } else {
+        size_t nwords = slotsToDynamicWords(nslots);
+        Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value));
+        if (!tmpdslots)
+            return;     /* leave dslots at its old size */
+
+        dslots = tmpdslots;
+        dslots++;
+        dslots[-1].setPrivateUint32(nslots);
     }
 }
 
 bool
 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
 {
     JS_ASSERT_IF(isNative(),
                  isBlock() || isCall() || (isFunction() && isBoundFunction()));
@@ -4671,22 +4665,20 @@ js_NativeGet(JSContext *cx, JSObject *ob
 
     uint32 slot;
     int32 sample;
 
     JS_ASSERT(pobj->isNative());
     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
 
     slot = shape->slot;
-    if (slot != SHAPE_INVALID_SLOT) {
+    if (slot != SHAPE_INVALID_SLOT)
         *vp = pobj->lockedGetSlot(slot);
-        JS_ASSERT(!vp->isMagic());
-    } else {
+    else
         vp->setUndefined();
-    }
     if (shape->hasDefaultGetter())
         return true;
 
     if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
         JS_ASSERT(&shape->methodObject() == &vp->toObject());
         return true;
     }
 
@@ -5914,32 +5906,34 @@ js_DumpScopeMeters(JSRuntime *rt)
 #ifdef DEBUG
 void
 js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
 {
     JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
 
     JSObject *obj = (JSObject *)trc->debugPrintArg;
     uint32 slot = (uint32)trc->debugPrintIndex;
+    JS_ASSERT(slot >= JSSLOT_START(obj->getClass()));
 
     const Shape *shape;
     if (obj->isNative()) {
         shape = obj->lastProperty();
         while (shape->previous() && shape->slot != slot)
             shape = shape->previous();
     } else {
         shape = NULL;
     }
 
     if (!shape) {
         const char *slotname = NULL;
         Class *clasp = obj->getClass();
         if (clasp->flags & JSCLASS_IS_GLOBAL) {
+            uint32 key = slot - JSSLOT_START(clasp);
 #define JS_PROTO(name,code,init)                                              \
-    if ((code) == slot) { slotname = js_##name##_str; goto found; }
+    if ((code) == key) { slotname = js_##name##_str; goto found; }
 #include "jsproto.tbl"
 #undef JS_PROTO
         }
       found:
         if (slotname)
             JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
         else
             JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
@@ -5957,17 +5951,17 @@ js_PrintObjectSlotName(JSTracer *trc, ch
 #endif
 
 void
 js_TraceObject(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
     JSContext *cx = trc->context;
-    if (obj->hasSlotsArray() && !obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) {
+    if (!obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) {
         /*
          * Trim overlong dslots allocations from the GC, to avoid thrashing in
          * case of delete-happy code that settles down at a given population.
          * The !obj->nativeEmpty() guard above is due to the bug described by
          * the FIXME comment below.
          */
         size_t slots = obj->slotSpan();
         if (obj->numSlots() != slots)
@@ -6004,18 +5998,19 @@ js_TraceObject(JSTracer *trc, JSObject *
      * FIXME: We minimize nslots against obj->slotSpan because native objects
      * such as Date instances may have failed to advance slotSpan to cover all
      * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
      * the general problem occurs in other built-in class implementations).
      */
     uint32 nslots = obj->numSlots();
     if (!obj->nativeEmpty() && obj->slotSpan() < nslots)
         nslots = obj->slotSpan();
-
-    for (uint32 i = 0; i != nslots; ++i) {
+    JS_ASSERT(nslots >= JSSLOT_START(clasp));
+
+    for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) {
         const Value &v = obj->getSlot(i);
         JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
         MarkValueRaw(trc, v);
     }
 }
 
 void
 js_ClearNative(JSContext *cx, JSObject *obj)
@@ -6034,39 +6029,41 @@ js_ClearNative(JSContext *cx, JSObject *
         uint32 n = obj->numSlots();
         for (uint32 i = freeslot; i < n; ++i)
             obj->setSlot(i, UndefinedValue());
     }
     JS_UNLOCK_OBJ(cx, obj);
 }
 
 bool
-js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp)
+js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, Value *vp)
 {
     if (!obj->isNative()) {
         vp->setUndefined();
         return true;
     }
 
+    uint32 slot = JSSLOT_START(obj->getClass()) + index;
     JS_LOCK_OBJ(cx, obj);
     if (slot < obj->numSlots())
         *vp = obj->getSlot(slot);
     else
         vp->setUndefined();
     JS_UNLOCK_OBJ(cx, obj);
     return true;
 }
 
 bool
-js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v)
+js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const Value &v)
 {
     if (!obj->isNative())
         return true;
 
     Class *clasp = obj->getClass();
+    uint32 slot = JSSLOT_START(clasp) + index;
 
     JS_LOCK_OBJ(cx, obj);
     if (slot >= obj->numSlots()) {
         uint32 nslots = JSSLOT_FREE(clasp);
         JS_ASSERT(slot < nslots);
         if (!obj->allocSlots(cx, nslots)) {
             JS_UNLOCK_OBJ(cx, obj);
             return false;
@@ -6366,18 +6363,21 @@ js_DumpObject(JSObject *obj)
     fprintf(stderr, "proto ");
     dumpValue(ObjectOrNullValue(obj->getProto()));
     fputc('\n', stderr);
 
     fprintf(stderr, "parent ");
     dumpValue(ObjectOrNullValue(obj->getParent()));
     fputc('\n', stderr);
 
-    if (clasp->flags & JSCLASS_HAS_PRIVATE)
+    i = JSSLOT_PRIVATE;
+    if (clasp->flags & JSCLASS_HAS_PRIVATE) {
+        i = JSSLOT_PRIVATE + 1;
         fprintf(stderr, "private %p\n", obj->getPrivate());
+    }
 
     fprintf(stderr, "slots:\n");
     reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp);
     slots = obj->slotSpan();
     for (; i < slots; i++) {
         fprintf(stderr, " %3d ", i);
         if (i < reservedEnd)
             fprintf(stderr, "(reserved) ");
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -230,16 +230,27 @@ extern JS_FRIEND_API(JSBool)
 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
              js::Value *statep, jsid *idp);
 
 extern JSType
 js_TypeOf(JSContext *cx, JSObject *obj);
 
 struct NativeIterator;
 
+const uint32 JS_INITIAL_NSLOTS = 3;
+
+/*
+ * The first available slot to store generic value. For JSCLASS_HAS_PRIVATE
+ * classes the slot stores a pointer to private data stuffed in a Value.
+ * Such pointer is stored as is without an overhead of PRIVATE_TO_JSVAL
+ * tagging and should be accessed using the (get|set)Private methods of
+ * JSObject.
+ */
+const uint32 JSSLOT_PRIVATE = 0;
+
 struct JSFunction;
 
 /*
  * JSObject struct, with members sized to fit in 32 bytes on 32-bit targets,
  * 64 bytes on 64-bit systems. The JSFunction struct is an extension of this
  * struct allocated from a larger GC size-class.
  *
  * The clasp member stores the js::Class pointer for this object. We do *not*
@@ -258,19 +269,19 @@ struct JSFunction;
  * but it has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM
  * and JS_FlagScriptFilenamePrefix (also exported via jsdbgapi.h) are intended
  * to be complementary to this bit, but it is up to the API client to implement
  * any such association.
  *
  * Both these flag bits are initially zero; they may be set or queried using
  * the (is|set)(Delegate|System) inline methods.
  *
- * The dslots member is a pointer to the slot vector for the object,
- * either an inline array starting at data or a dynamically allocated array.
- * In both cases, capacity gives the number of available slots.
+ * The dslots member is null or a pointer into a dynamically allocated vector
+ * of Values for reserved and dynamic slots. If dslots is not null, dslots[-1]
+ * records the number of available slots.
  */
 struct JSObject {
     /*
      * TraceRecorder must be a friend because it generates code that
      * manipulates JSObjects, which requires peeking under any encapsulation.
      */
     friend class js::TraceRecorder;
 
@@ -328,42 +339,37 @@ struct JSObject {
     enum {
         NSLOTS_BITS     = 29,
         NSLOTS_LIMIT    = JS_BIT(NSLOTS_BITS)
     };
 
     uint32      flags;                      /* flags */
     uint32      objShape;                   /* copy of lastProp->shape, or override if different */
 
+    JSObject    *proto;                     /* object's prototype */
+    JSObject    *parent;                    /* object's parent */
+    js::Value   *dslots;                    /* dynamically allocated slots */
+
+    /* Empty shape of kids if prototype, located here to align fslots on 32 bit targets. */
+    js::EmptyShape *emptyShape;
+
+    js::Value   fslots[JS_INITIAL_NSLOTS];  /* small number of fixed slots */
 #ifdef JS_THREADSAFE
     JSTitle     title;
 #endif
 
-    /* If prototype, lazily filled array of empty shapes for each object size. */
-    js::EmptyShape **emptyShapes;
-
-    JSObject    *proto;                     /* object's prototype */
-    JSObject    *parent;                    /* object's parent */
-    void        *privateData;               /* private data */
-    jsuword     capacity;                   /* capacity of slots */
-    js::Value   *slots;                     /* dynamically allocated slots,
-                                               or pointer to fixedSlots */
-    js::Value   fixedSlots[2];              /* inline slots for object. count is not fixed,
-                                               may be less or greater than two */
-
     /*
      * Return an immutable, shareable, empty shape with the same clasp as this
      * and the same slotSpan as this had when empty.
      *
      * If |this| is the scope of an object |proto|, the resulting scope can be
      * used as the scope of a new object whose prototype is |proto|.
      */
     inline bool canProvideEmptyShape(js::Class *clasp);
-    inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp,
-                                         /* JSFinalizeGCThingKind */ unsigned kind);
+    inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp);
 
     bool isNative() const       { return map->isNative(); }
 
     js::Class *getClass() const { return clasp; }
     JSClass *getJSClass() const { return Jsvalify(clasp); }
 
     bool hasClass(const js::Class *c) const {
         return c == clasp;
@@ -519,37 +525,39 @@ struct JSObject {
      * a doubly-linked list.
      */
     inline bool inDictionaryMode() const;
 
     inline uint32 propertyCount() const;
 
     inline bool hasPropertyTable() const;
 
-    uint32 numSlots() const { return capacity; }
+    uint32 numSlots(void) const {
+        return dslots ? dslots[-1].toPrivateUint32() : uint32(JS_INITIAL_NSLOTS);
+    }
 
     size_t slotsAndStructSize(uint32 nslots) const;
     size_t slotsAndStructSize() const { return slotsAndStructSize(numSlots()); }
 
-    inline size_t numFixedSlots() const;
+  private:
+    static size_t slotsToDynamicWords(size_t nslots) {
+        JS_ASSERT(nslots > JS_INITIAL_NSLOTS);
+        return nslots + 1 - JS_INITIAL_NSLOTS;
+    }
+
+    static size_t dynamicWordsToSlots(size_t nwords) {
+        JS_ASSERT(nwords > 1);
+        return nwords - 1 + JS_INITIAL_NSLOTS;
+    }
 
   public:
-    /* Minimum size for dynamically allocated slots. */
-    static const uint32 SLOT_CAPACITY_MIN = 8;
-
     bool allocSlots(JSContext *cx, size_t nslots);
     bool growSlots(JSContext *cx, size_t nslots);
     void shrinkSlots(JSContext *cx, size_t nslots);
 
-    bool ensureSlots(JSContext *cx, size_t nslots) {
-        if (numSlots() < nslots)
-            return growSlots(cx, nslots);
-        return true;
-    }
-
     /*
      * Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp) +
      * nreserved slots.
      *
      * This method may be called only for native objects freshly created using
      * NewObject or one of its variant where the new object will both (a) never
      * escape to script and (b) never be extended with ad-hoc properties that
      * would try to allocate higher slots without the fresh object first having
@@ -559,58 +567,59 @@ struct JSObject {
      * add ad-hoc properties to a Block instance. Call objects satisfy (a) and
      * (b) as well, because the compiler-created Shape path that covers args,
      * vars, and upvars, stored in their callee function in u.i.names, becomes
      * their initial map.
      */
     bool ensureInstanceReservedSlots(JSContext *cx, size_t nreserved);
 
     /*
-     * Get a direct pointer to the object's slots.
-     * This can be reallocated if the object is modified, watch out!
-     */
-    js::Value *getSlots() const {
-        return slots;
-    }
-
-    /*
      * NB: ensureClassReservedSlotsForEmptyObject asserts that nativeEmpty()
      * Use ensureClassReservedSlots for any object, either empty or already
      * extended with properties.
      */
     bool ensureClassReservedSlotsForEmptyObject(JSContext *cx);
 
     inline bool ensureClassReservedSlots(JSContext *cx);
 
     uint32 slotSpan() const { return map->slotSpan; }
 
     bool containsSlot(uint32 slot) const { return slot < slotSpan(); }
 
     js::Value& getSlotRef(uintN slot) {
-        JS_ASSERT(slot < capacity);
-        return slots[slot];
+        return (slot < JS_INITIAL_NSLOTS)
+               ? fslots[slot]
+               : (JS_ASSERT(slot < dslots[-1].toPrivateUint32()),
+                  dslots[slot - JS_INITIAL_NSLOTS]);
     }
 
     const js::Value &getSlot(uintN slot) const {
-        JS_ASSERT(slot < capacity);
-        return slots[slot];
+        return (slot < JS_INITIAL_NSLOTS)
+               ? fslots[slot]
+               : (JS_ASSERT(slot < dslots[-1].toPrivateUint32()),
+                  dslots[slot - JS_INITIAL_NSLOTS]);
     }
 
     void setSlot(uintN slot, const js::Value &value) {
-        JS_ASSERT(slot < capacity);
-        slots[slot] = value;
+        if (slot < JS_INITIAL_NSLOTS) {
+            fslots[slot] = value;
+        } else {
+            JS_ASSERT(slot < dslots[-1].toPrivateUint32());
+            dslots[slot - JS_INITIAL_NSLOTS] = value;
+        }
     }
 
     inline const js::Value &lockedGetSlot(uintN slot) const;
     inline void lockedSetSlot(uintN slot, const js::Value &value);
 
     /*
      * These ones are for multi-threaded ("MT") objects.  Use getSlot(),
      * getSlotRef(), setSlot() to directly manipulate slots in obj when only
-     * one thread can access obj.
+     * one thread can access obj, or when accessing read-only slots within
+     * JS_INITIAL_NSLOTS.
      */
     inline js::Value getSlotMT(JSContext *cx, uintN slot);
     inline void setSlotMT(JSContext *cx, uintN slot, const js::Value &value);
 
     inline js::Value getReservedSlot(uintN index) const;
 
     /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
     inline void updateShape(JSContext *cx);
@@ -651,61 +660,81 @@ struct JSObject {
     JSObject *getGlobal() const;
 
     bool isGlobal() const {
         return !!(getClass()->flags & JSCLASS_IS_GLOBAL);
     }
 
     void *getPrivate() const {
         JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE);
-        return privateData;
+        return *(void **)&fslots[JSSLOT_PRIVATE];
     }
 
     void setPrivate(void *data) {
         JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE);
-        privateData = data;
+        *(void **)&fslots[JSSLOT_PRIVATE] = data;
     }
 
     /*
      * Primitive-specific getters and setters.
      */
 
   private:
-    static const uint32 JSSLOT_PRIMITIVE_THIS = 0;
+    static const uint32 JSSLOT_PRIMITIVE_THIS = JSSLOT_PRIVATE;
 
   public:
     inline const js::Value &getPrimitiveThis() const;
     inline void setPrimitiveThis(const js::Value &pthis);
 
     /*
      * Array-specific getters and setters (for both dense and slow arrays).
      */
 
+    // Used by dense and slow arrays.
+    static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE;
+
+    static const uint32 JSSLOT_DENSE_ARRAY_CAPACITY = JSSLOT_PRIVATE + 1;
+
+    // This assertion must remain true;  see comment in js_MakeArraySlow().
+    // (Nb: This method is never called, it just contains a static assertion.
+    // The static assertion isn't inline because that doesn't work on Mac.)
+    inline void staticAssertArrayLengthIsInPrivateSlot();
+
+  public:
+    static const uint32 DENSE_ARRAY_CLASS_RESERVED_SLOTS = 3;
+
     inline uint32 getArrayLength() const;
     inline void setArrayLength(uint32 length);
 
-    inline uint32 getDenseArrayCapacity();
-    inline js::Value* getDenseArrayElements();
-    inline const js::Value &getDenseArrayElement(uintN idx);
-    inline js::Value* addressOfDenseArrayElement(uintN idx);
-    inline void setDenseArrayElement(uintN idx, const js::Value &val);
-    inline bool ensureDenseArrayElements(JSContext *cx, uintN cap);
-    inline void shrinkDenseArrayElements(JSContext *cx, uintN cap);
+    inline uint32 getDenseArrayCapacity() const;
+    inline void setDenseArrayCapacity(uint32 capacity);
+
+    inline const js::Value &getDenseArrayElement(uint32 i) const;
+    inline js::Value *addressOfDenseArrayElement(uint32 i);
+    inline void setDenseArrayElement(uint32 i, const js::Value &v);
+
+    inline js::Value *getDenseArrayElements() const;   // returns pointer to the Array's elements array
+    bool growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap);
+    bool ensureDenseArrayElements(JSContext *cx, uint32 newcap);
+    bool shrinkDenseArrayElements(JSContext *cx, uint32 newcap);
+    inline void freeDenseArrayElements(JSContext *cx);
+
+    inline void voidDenseOnlyArraySlots();  // used when converting a dense array to a slow array
 
     JSBool makeDenseArraySlow(JSContext *cx);
 
     /*
      * Arguments-specific getters and setters.
      */
 
   private:
     /*
      * Reserved slot structure for Arguments objects:
      *
-     * private              - the function's stack frame until the function
+     * JSSLOT_PRIVATE       - the function's stack frame until the function
      *                        returns; also, JS_ARGUMENTS_OBJECT_ON_TRACE if
      *                        arguments was created on trace
      * JSSLOT_ARGS_LENGTH   - the number of actual arguments and a flag
      *                        indicating whether arguments.length was
      *                        overwritten. This slot is not used to represent
      *                        arguments.length after that property has been
      *                        assigned, even if the new value is integral: it's
      *                        always the original length.
@@ -713,23 +742,23 @@ struct JSObject {
      *                        the arguments.callee value or JSVAL_HOLE if that
      *                        was overwritten, and the values of all arguments
      *                        once the function has returned (or as soon as a
      *                        strict arguments object has been created).
      *
      * Argument index i is stored in ArgumentsData.slots[i], accessible via
      * {get,set}ArgsElement().
      */
-    static const uint32 JSSLOT_ARGS_DATA = 1;
+    static const uint32 JSSLOT_ARGS_DATA   = JSSLOT_PRIVATE + 2;
 
   public:
     /* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */
-    static const uint32 JSSLOT_ARGS_LENGTH = 0;
+    static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
     static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2;
-    static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1;
+    static const uint32 ARGS_FIRST_FREE_SLOT = JSSLOT_PRIVATE + ARGS_CLASS_RESERVED_SLOTS + 1;
 
     /* Lower-order bit stolen from the length slot. */
     static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1;
     static const uint32 ARGS_PACKED_BITS_COUNT = 1;
 
     /*
      * Set the initial length of the arguments, and mark it as not overridden.
      */
@@ -746,58 +775,59 @@ struct JSObject {
 
     inline js::ArgumentsData *getArgsData() const;
     inline void setArgsData(js::ArgumentsData *data);
 
     inline const js::Value &getArgsCallee() const;
     inline void setArgsCallee(const js::Value &callee);
 
     inline const js::Value &getArgsElement(uint32 i) const;
-    inline js::Value *addressOfArgsElement(uint32 i);
+    inline js::Value *addressOfArgsElement(uint32 i) const;
     inline void setArgsElement(uint32 i, const js::Value &v);
 
   private:
     /*
      * Reserved slot structure for Arguments objects:
+     *
      */
-    static const uint32 JSSLOT_CALL_CALLEE = 0;
-    static const uint32 JSSLOT_CALL_ARGUMENTS = 1;
+    static const uint32 JSSLOT_CALL_CALLEE = JSSLOT_PRIVATE + 1;
+    static const uint32 JSSLOT_CALL_ARGUMENTS = JSSLOT_PRIVATE + 2;
 
   public:
-    /* Number of reserved slots. */
+    /* Number of extra fixed slots besides JSSLOT_PRIVATE. */
     static const uint32 CALL_RESERVED_SLOTS = 2;
 
     inline JSObject &getCallObjCallee() const;
     inline JSFunction *getCallObjCalleeFunction() const; 
     inline void setCallObjCallee(JSObject &callee);
 
     inline const js::Value &getCallObjArguments() const;
     inline void setCallObjArguments(const js::Value &v);
 
     /*
      * Date-specific getters and setters.
      */
 
-    static const uint32 JSSLOT_DATE_UTC_TIME = 0;
+    static const uint32 JSSLOT_DATE_UTC_TIME = JSSLOT_PRIVATE;
 
     /*
      * Cached slots holding local properties of the date.
      * These are undefined until the first actual lookup occurs
      * and are reset to undefined whenever the date's time is modified.
      */
-    static const uint32 JSSLOT_DATE_COMPONENTS_START = 1;
+    static const uint32 JSSLOT_DATE_COMPONENTS_START = JSSLOT_PRIVATE + 1;
 
-    static const uint32 JSSLOT_DATE_LOCAL_TIME = 1;
-    static const uint32 JSSLOT_DATE_LOCAL_YEAR = 2;
-    static const uint32 JSSLOT_DATE_LOCAL_MONTH = 3;
-    static const uint32 JSSLOT_DATE_LOCAL_DATE = 4;
-    static const uint32 JSSLOT_DATE_LOCAL_DAY = 5;
-    static const uint32 JSSLOT_DATE_LOCAL_HOURS = 6;
-    static const uint32 JSSLOT_DATE_LOCAL_MINUTES = 7;
-    static const uint32 JSSLOT_DATE_LOCAL_SECONDS = 8;
+    static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1;
+    static const uint32 JSSLOT_DATE_LOCAL_YEAR = JSSLOT_PRIVATE + 2;
+    static const uint32 JSSLOT_DATE_LOCAL_MONTH = JSSLOT_PRIVATE + 3;
+    static const uint32 JSSLOT_DATE_LOCAL_DATE = JSSLOT_PRIVATE + 4;
+    static const uint32 JSSLOT_DATE_LOCAL_DAY = JSSLOT_PRIVATE + 5;
+    static const uint32 JSSLOT_DATE_LOCAL_HOURS = JSSLOT_PRIVATE + 6;
+    static const uint32 JSSLOT_DATE_LOCAL_MINUTES = JSSLOT_PRIVATE + 7;
+    static const uint32 JSSLOT_DATE_LOCAL_SECONDS = JSSLOT_PRIVATE + 8;
 
     static const uint32 DATE_CLASS_RESERVED_SLOTS = 9;
 
     inline const js::Value &getDateUTCTime() const;
     inline void setDateUTCTime(const js::Value &pthis);
 
     /*
      * Function-specific getters and setters.
@@ -806,28 +836,27 @@ struct JSObject {
   private:
     friend struct JSFunction;
     friend class js::mjit::Compiler;
 
     /*
      * Flat closures with one or more upvars snapshot the upvars' values into a
      * vector of js::Values referenced from this slot.
      */
-    static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = 0;
+    static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = JSSLOT_PRIVATE + 1;
 
     /*
      * Null closures set or initialized as methods have these slots. See the
      * "method barrier" comments and methods.
      */
+    static const uint32 JSSLOT_FUN_METHOD_ATOM = JSSLOT_PRIVATE + 1;
+    static const uint32 JSSLOT_FUN_METHOD_OBJ  = JSSLOT_PRIVATE + 2;
 
-    static const uint32 JSSLOT_FUN_METHOD_ATOM = 0;
-    static const uint32 JSSLOT_FUN_METHOD_OBJ  = 1;
-
-    static const uint32 JSSLOT_BOUND_FUNCTION_THIS       = 0;
-    static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
+    static const uint32 JSSLOT_BOUND_FUNCTION_THIS       = JSSLOT_PRIVATE + 1;
+    static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = JSSLOT_PRIVATE + 2;
 
   public:
     static const uint32 FUN_CLASS_RESERVED_SLOTS = 2;
 
     inline JSFunction *getFunctionPrivate() const;
 
     inline js::Value *getFlatClosureUpvars() const;
     inline js::Value getFlatClosureUpvar(uint32 i) const;
@@ -843,17 +872,17 @@ struct JSObject {
     inline const js::Value &getBoundFunctionThis() const;
     inline const js::Value *getBoundFunctionArguments(uintN &argslen) const;
 
     /*
      * RegExp-specific getters and setters.
      */
 
   private:
-    static const uint32 JSSLOT_REGEXP_LAST_INDEX = 0;
+    static const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
 
   public:
     static const uint32 REGEXP_CLASS_RESERVED_SLOTS = 1;
 
     inline const js::Value &getRegExpLastIndex() const;
     inline void setRegExpLastIndex(const js::Value &v);
     inline void setRegExpLastIndex(jsdouble d);
     inline void zeroRegExpLastIndex();
@@ -872,22 +901,22 @@ struct JSObject {
     /*
      * Slots for XML-related classes are as follows:
      * - js_NamespaceClass.base reserves the *_NAME_* and *_NAMESPACE_* slots.
      * - js_QNameClass.base, js_AttributeNameClass, js_AnyNameClass reserve
      *   the *_NAME_* and *_QNAME_* slots.
      * - Others (js_XMLClass, js_XMLFilterClass) don't reserve any slots.
      */
   private:
-    static const uint32 JSSLOT_NAME_PREFIX          = 0;   // shared
-    static const uint32 JSSLOT_NAME_URI             = 1;   // shared
+    static const uint32 JSSLOT_NAME_PREFIX          = JSSLOT_PRIVATE;       // shared
+    static const uint32 JSSLOT_NAME_URI             = JSSLOT_PRIVATE + 1;   // shared
 
-    static const uint32 JSSLOT_NAMESPACE_DECLARED   = 2;
+    static const uint32 JSSLOT_NAMESPACE_DECLARED   = JSSLOT_PRIVATE + 2;
 
-    static const uint32 JSSLOT_QNAME_LOCAL_NAME     = 2;
+    static const uint32 JSSLOT_QNAME_LOCAL_NAME     = JSSLOT_PRIVATE + 2;
 
   public:
     static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3;
     static const uint32 QNAME_CLASS_RESERVED_SLOTS     = 3;
 
     inline jsval getNamePrefix() const;
     inline void setNamePrefix(jsval prefix);
 
@@ -915,43 +944,47 @@ struct JSObject {
     inline void setWithThis(JSObject *thisp);
 
     /*
      * Back to generic stuff.
      */
     inline bool isCallable();
 
     /* The map field is not initialized here and should be set separately. */
-    void init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent,
-              void *priv, bool useHoles);
+    inline void initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent,
+                           JSContext *cx);
+    inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent,
+                     JSContext *cx);
+    inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent,
+                     void *priv, JSContext *cx);
+    inline void init(js::Class *aclasp, JSObject *proto, JSObject *parent,
+                     const js::Value &privateSlotValue, JSContext *cx);
 
     inline void finish(JSContext *cx);
 
     /*
      * Like init, but also initializes map. The catch: proto must be the result
      * of a call to js_InitClass(...clasp, ...).
      */
-    inline bool initSharingEmptyShape(JSContext *cx,
-                                      js::Class *clasp,
+    inline void initSharingEmptyShape(js::Class *clasp,
+                                      JSObject *proto,
+                                      JSObject *parent,
+                                      const js::Value &privateSlotValue,
+                                      JSContext *cx);
+    inline void initSharingEmptyShape(js::Class *clasp,
                                       JSObject *proto,
                                       JSObject *parent,
                                       void *priv,
-                                      /* JSFinalizeGCThingKind */ unsigned kind);
+                                      JSContext *cx);
 
-    inline bool hasSlotsArray() const { return (slots != &fixedSlots[0]); }
+    inline bool hasSlotsArray() const { return !!dslots; }
 
     /* This method can only be called when hasSlotsArray() returns true. */
     inline void freeSlotsArray(JSContext *cx);
 
-    /*
-     * Frees slots array and cleans up object so it may continue to be used,
-     * although slotSpan may exceed numSlots.
-     */
-    inline void removeSlotsArray(JSContext *cx);
-
     inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0);
 
     bool allocSlot(JSContext *cx, uint32 *slotp);
     void freeSlot(JSContext *cx, uint32 slot);
 
   private:
     void reportReadOnlyScope(JSContext *cx);
 
@@ -1053,19 +1086,19 @@ struct JSObject {
     static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp);
 
     inline void dropProperty(JSContext *cx, JSProperty *prop);
 
     JS_FRIEND_API(JSCompartment *) getCompartment(JSContext *cx);
 
     inline JSObject *getThrowTypeError() const;
 
-    bool swap(JSContext *cx, JSObject *obj);
+    const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index);
 
-    const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index);
+    void swap(JSObject *obj);
 
     inline bool canHaveMethodBarrier() const;
 
     inline bool isArguments() const;
     inline bool isNormalArguments() const;
     inline bool isStrictArguments() const;
     inline bool isArray() const;
     inline bool isDenseArray() const;
@@ -1093,27 +1126,33 @@ struct JSObject {
     inline bool isFunctionProxy() const;
 
     JS_FRIEND_API(bool) isWrapper() const;
     JS_FRIEND_API(JSObject *) unwrap(uintN *flagsp = NULL);
 
     inline void initArrayClass();
 };
 
+JS_STATIC_ASSERT(offsetof(JSObject, fslots) % sizeof(js::Value) == 0);
+JS_STATIC_ASSERT(sizeof(JSObject) % JS_GCTHING_ALIGN == 0);
+
+#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE)           \
+                             ? JSSLOT_PRIVATE + 1                             \
+                             : JSSLOT_PRIVATE)
+
+#define JSSLOT_FREE(clasp)  (JSSLOT_START(clasp)                              \
+                             + JSCLASS_RESERVED_SLOTS(clasp))
+
 /*
- * Base size of an object, excluding any fixed slots.  Use instead of sizeof.
- * fixedSlots has a declared capacity of two to match FUN_CLASS_RESERVED_SLOTS,
- * and because some compilers don't like empty arrays in superclasses.
+ * Maximum capacity of the obj->dslots vector, net of the hidden slot at
+ * obj->dslots[-1] that is used to store the length of the vector biased by
+ * JS_INITIAL_NSLOTS (and again net of the slot at index -1).
  */
-static const size_t JSOBJECT_SIZE = offsetof(JSObject, fixedSlots);
-
-JS_STATIC_ASSERT(JSOBJECT_SIZE % sizeof(js::Value) == 0);
-JS_STATIC_ASSERT(JSOBJECT_SIZE % JS_GCTHING_ALIGN == 0);
-
-#define JSSLOT_FREE(clasp)  JSCLASS_RESERVED_SLOTS(clasp)
+#define MAX_DSLOTS_LENGTH   (~size_t(0) / sizeof(js::Value) - 1)
+#define MAX_DSLOTS_LENGTH32 (~uint32(0) / sizeof(js::Value) - 1)
 
 #define OBJ_CHECK_SLOT(obj,slot) JS_ASSERT((obj)->containsSlot(slot))
 
 #ifdef JS_THREADSAFE
 
 /*
  * The GC runs only when all threads except the one on which the GC is active
  * are suspended at GC-safe points, so calling obj->getSlot() from the GC's
@@ -1163,43 +1202,43 @@ extern js::Class js_BlockClass;
 
 inline bool JSObject::isObject() const { return getClass() == &js_ObjectClass; }
 inline bool JSObject::isWith() const   { return getClass() == &js_WithClass; }
 inline bool JSObject::isBlock() const  { return getClass() == &js_BlockClass; }
 
 /*
  * Block scope object macros.  The slots reserved by js_BlockClass are:
  *
- *   private              JSStackFrame *    active frame pointer or null
+ *   JSSLOT_PRIVATE       JSStackFrame *    active frame pointer or null
  *   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 one 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 JSStackFrame in
  * whose activation they were created (or null if the with or block object
  * outlives the frame).
  */
-static const uint32 JSSLOT_BLOCK_DEPTH = 0;
+static const uint32 JSSLOT_BLOCK_DEPTH = JSSLOT_PRIVATE + 1;
 
 inline bool
 JSObject::isStaticBlock() const
 {
     return isBlock() && !getProto();
 }
 
 inline bool
 JSObject::isClonedBlock() const
 {
     return isBlock() && !!getProto();
 }
 
-static const uint32 JSSLOT_WITH_THIS = 1;
+static const uint32 JSSLOT_WITH_THIS = JSSLOT_PRIVATE + 2;
 
 #define OBJ_BLOCK_COUNT(cx,obj)                                               \
     (obj)->propertyCount()
 #define OBJ_BLOCK_DEPTH(cx,obj)                                               \
     (obj)->getSlot(JSSLOT_BLOCK_DEPTH).toInt32()
 #define OBJ_SET_BLOCK_DEPTH(cx,obj,depth)                                     \
     (obj)->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)))
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -219,17 +219,18 @@ JSObject::setSlotMT(JSContext *cx, uintN
 #else
     this->lockedSetSlot(slot, value);
 #endif
 }
 
 inline js::Value
 JSObject::getReservedSlot(uintN index) const
 {
-    return (index < numSlots()) ? getSlot(index) : js::UndefinedValue();
+    uint32 slot = JSSLOT_START(getClass()) + index;
+    return (slot < numSlots()) ? getSlot(slot) : js::UndefinedValue();
 }
 
 inline bool
 JSObject::canHaveMethodBarrier() const
 {
     return isObject() || isFunction() || isPrimitive() || isDate();
 }
 
@@ -238,158 +239,173 @@ JSObject::isPrimitive() const
 {
     return isNumber() || isString() || isBoolean();
 }
 
 inline const js::Value &
 JSObject::getPrimitiveThis() const
 {
     JS_ASSERT(isPrimitive());
-    return getSlot(JSSLOT_PRIMITIVE_THIS);
+    return fslots[JSSLOT_PRIMITIVE_THIS];
 }
 
 inline void
 JSObject::setPrimitiveThis(const js::Value &pthis)
 {
     JS_ASSERT(isPrimitive());
-    setSlot(JSSLOT_PRIMITIVE_THIS, pthis);
+    fslots[JSSLOT_PRIMITIVE_THIS] = pthis;
 }
 
-inline size_t
-JSObject::numFixedSlots() const
+inline void
+JSObject::staticAssertArrayLengthIsInPrivateSlot()
 {
-    if (isFunction())
-        return JSObject::FUN_CLASS_RESERVED_SLOTS;
-    if (!hasSlotsArray())
-        return capacity;
-    JSFinalizeGCThingKind kind = js_KindFromGCThing(this);
-    return js_GetGCKindSlots(kind);
-}
-
-inline size_t
-JSObject::slotsAndStructSize(uint32 nslots) const
-{
-    bool isFun = isFunction() && this == (JSObject*) getPrivate();
-
-    int ndslots = hasSlotsArray() ? nslots : 0;
-    int nfslots = isFun ? 0 : numFixedSlots();
-
-    return sizeof(js::Value) * (ndslots + nfslots)
-           + isFun ? sizeof(JSFunction) : sizeof(JSObject);
+    JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE);
 }
 
 inline uint32
 JSObject::getArrayLength() const
 {
     JS_ASSERT(isArray());
-    return (uint32)(size_t) getPrivate();
+    return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32();
 }
 
 inline void
 JSObject::setArrayLength(uint32 length)
 {
     JS_ASSERT(isArray());
-    setPrivate((void*) length);
+    fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length);
 }
 
 inline uint32
-JSObject::getDenseArrayCapacity()
+JSObject::getDenseArrayCapacity() const
 {
     JS_ASSERT(isDenseArray());
-    return numSlots();
+    return fslots[JSSLOT_DENSE_ARRAY_CAPACITY].toPrivateUint32();
+}
+
+inline void
+JSObject::setDenseArrayCapacity(uint32 capacity)
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(!capacity == !dslots);
+    fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setPrivateUint32(capacity);
 }
 
-inline js::Value*
-JSObject::getDenseArrayElements()
+inline size_t
+JSObject::slotsAndStructSize(uint32 nslots) const
 {
-    JS_ASSERT(isDenseArray());
-    return getSlots();
+    int ndslots;
+    if (isDenseArray())
+        ndslots = getDenseArrayCapacity() + 1;
+    else {
+        ndslots = nslots - JS_INITIAL_NSLOTS;
+        if (ndslots <= 0)
+            ndslots = 0;
+        else
+            ndslots++; /* number of total slots is stored at index -1 */
+    }
+
+    return sizeof(js::Value) * ndslots
+           + (isFunction() && !getPrivate()) ? sizeof(JSFunction) : sizeof(JSObject);
 }
 
 inline const js::Value &
-JSObject::getDenseArrayElement(uintN idx)
+JSObject::getDenseArrayElement(uint32 i) const
 {
     JS_ASSERT(isDenseArray());
-    return getSlot(idx);
+    JS_ASSERT(i < getDenseArrayCapacity());
+    return dslots[i];
 }
 
 inline js::Value *
-JSObject::addressOfDenseArrayElement(uintN idx)
+JSObject::addressOfDenseArrayElement(uint32 i)
 {
     JS_ASSERT(isDenseArray());
-    return &getSlotRef(idx);
+    JS_ASSERT(i < getDenseArrayCapacity());
+    return &dslots[i];
 }
 
 inline void
-JSObject::setDenseArrayElement(uintN idx, const js::Value &val)
+JSObject::setDenseArrayElement(uint32 i, const js::Value &v)
 {
     JS_ASSERT(isDenseArray());
-    setSlot(idx, val);
+    JS_ASSERT(i < getDenseArrayCapacity());
+    dslots[i] = v;
 }
 
-inline bool
-JSObject::ensureDenseArrayElements(JSContext *cx, uintN cap)
+inline js::Value *
+JSObject::getDenseArrayElements() const
 {
     JS_ASSERT(isDenseArray());
-    return ensureSlots(cx, cap);
+    return dslots;
 }
 
 inline void
-JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
+JSObject::freeDenseArrayElements(JSContext *cx)
 {
     JS_ASSERT(isDenseArray());
-    shrinkSlots(cx, cap);
+    if (dslots) {
+        cx->free(dslots - 1);
+        dslots = NULL;
+    }
+}
+
+inline void
+JSObject::voidDenseOnlyArraySlots()
+{
+    JS_ASSERT(isDenseArray());
+    fslots[JSSLOT_DENSE_ARRAY_CAPACITY].setUndefined();
 }
 
 inline void
 JSObject::setArgsLength(uint32 argc)
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
     JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT));
-    getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT);
+    fslots[JSSLOT_ARGS_LENGTH].setInt32(argc << ARGS_PACKED_BITS_COUNT);
     JS_ASSERT(!isArgsLengthOverridden());
 }
 
 inline uint32
 JSObject::getArgsInitialLength() const
 {
     JS_ASSERT(isArguments());
-    uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT;
+    uint32 argc = uint32(fslots[JSSLOT_ARGS_LENGTH].toInt32()) >> ARGS_PACKED_BITS_COUNT;
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
     return argc;
 }
 
 inline void
 JSObject::setArgsLengthOverridden()
 {
     JS_ASSERT(isArguments());
-    getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
+    fslots[JSSLOT_ARGS_LENGTH].getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
 }
 
 inline bool
 JSObject::isArgsLengthOverridden() const
 {
     JS_ASSERT(isArguments());
-    const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH);
+    const js::Value &v = fslots[JSSLOT_ARGS_LENGTH];
     return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
 }
 
 inline js::ArgumentsData *
 JSObject::getArgsData() const
 {
     JS_ASSERT(isArguments());
-    return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate();
+    return (js::ArgumentsData *) fslots[JSSLOT_ARGS_DATA].toPrivate();
 }
 
 inline void
 JSObject::setArgsData(js::ArgumentsData *data)
 {
     JS_ASSERT(isArguments());
-    getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data);
+    fslots[JSSLOT_ARGS_DATA].setPrivate(data);
 }
 
 inline const js::Value &
 JSObject::getArgsCallee() const
 {
     return getArgsData()->callee;
 }
 
@@ -403,17 +419,17 @@ inline const js::Value &
 JSObject::getArgsElement(uint32 i) const
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(i < getArgsInitialLength());
     return getArgsData()->slots[i];
 }
 
 inline js::Value *
-JSObject::addressOfArgsElement(uint32 i)
+JSObject::addressOfArgsElement(uint32 i) const
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(i < getArgsInitialLength());
     return &getArgsData()->slots[i];
 }
 
 inline void
 JSObject::setArgsElement(uint32 i, const js::Value &v)
@@ -423,95 +439,94 @@ JSObject::setArgsElement(uint32 i, const
     getArgsData()->slots[i] = v;
 }
 
 inline void
 JSObject::setCallObjCallee(JSObject &callee)
 {
     JS_ASSERT(isCall());
     JS_ASSERT(callee.isFunction());
-    return getSlotRef(JSSLOT_CALL_CALLEE).setObject(callee);
+    return fslots[JSSLOT_CALL_CALLEE].setObject(callee);
 }
 
 inline JSObject &
 JSObject::getCallObjCallee() const
 {
     JS_ASSERT(isCall());
-    return getSlot(JSSLOT_CALL_CALLEE).toObject();
+    return fslots[JSSLOT_CALL_CALLEE].toObject();
 }
 
 inline JSFunction *
 JSObject::getCallObjCalleeFunction() const
 {
     JS_ASSERT(isCall());
-    return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
+    return fslots[JSSLOT_CALL_CALLEE].toObject().getFunctionPrivate();
 }
 
 inline const js::Value &
 JSObject::getCallObjArguments() const
 {
     JS_ASSERT(isCall());
-    return getSlot(JSSLOT_CALL_ARGUMENTS);
+    return fslots[JSSLOT_CALL_ARGUMENTS];
 }
 
 inline void
 JSObject::setCallObjArguments(const js::Value &v)
 {
     JS_ASSERT(isCall());
-    setSlot(JSSLOT_CALL_ARGUMENTS, v);
+    fslots[JSSLOT_CALL_ARGUMENTS] = v;
 }
 
 inline const js::Value &
 JSObject::getDateUTCTime() const
 {
     JS_ASSERT(isDate());
-    return getSlot(JSSLOT_DATE_UTC_TIME);
+    return fslots[JSSLOT_DATE_UTC_TIME];
 }
 
 inline void 
 JSObject::setDateUTCTime(const js::Value &time)
 {
     JS_ASSERT(isDate());
-    setSlot(JSSLOT_DATE_UTC_TIME, time);
+    fslots[JSSLOT_DATE_UTC_TIME] = time;
 }
 
 inline js::Value *
 JSObject::getFlatClosureUpvars() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate()));
-    return (js::Value *) getSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate();
+    return (js::Value *) fslots[JSSLOT_FLAT_CLOSURE_UPVARS].toPrivate();
 }
 
 inline js::Value
 JSObject::getFlatClosureUpvar(uint32 i) const
 {
     return getFlatClosureUpvars()[i];
 }
 
 inline void
 JSObject::setFlatClosureUpvars(js::Value *upvars)
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate()));
-    getSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars);
+    fslots[JSSLOT_FLAT_CLOSURE_UPVARS].setPrivate(upvars);
 }
 
 inline bool
 JSObject::hasMethodObj(const JSObject& obj) const
 {
-    return JSSLOT_FUN_METHOD_OBJ < numSlots() &&
-        getSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
-        &getSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == &obj;
+    return fslots[JSSLOT_FUN_METHOD_OBJ].isObject() &&
+           &fslots[JSSLOT_FUN_METHOD_OBJ].toObject() == &obj;
 }
 
 inline void
 JSObject::setMethodObj(JSObject& obj)
 {
-    getSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj);
+    fslots[JSSLOT_FUN_METHOD_OBJ].setObject(obj);
 }
 
 inline NativeIterator *
 JSObject::getNativeIterator() const
 {
     return (NativeIterator *) getPrivate();
 }
 
@@ -520,171 +535,189 @@ JSObject::setNativeIterator(NativeIterat
 {
     setPrivate(ni);
 }
 
 inline jsval
 JSObject::getNamePrefix() const
 {
     JS_ASSERT(isNamespace() || isQName());
-    return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX));
+    return js::Jsvalify(fslots[JSSLOT_NAME_PREFIX]);
 }
 
 inline void
 JSObject::setNamePrefix(jsval prefix)
 {
     JS_ASSERT(isNamespace() || isQName());
-    setSlot(JSSLOT_NAME_PREFIX, js::Valueify(prefix));
+    fslots[JSSLOT_NAME_PREFIX] = js::Valueify(prefix);
 }
 
 inline jsval
 JSObject::getNameURI() const
 {
     JS_ASSERT(isNamespace() || isQName());
-    return js::Jsvalify(getSlot(JSSLOT_NAME_URI));
+    return js::Jsvalify(fslots[JSSLOT_NAME_URI]);
 }
 
 inline void
 JSObject::setNameURI(jsval uri)
 {
     JS_ASSERT(isNamespace() || isQName());
-    setSlot(JSSLOT_NAME_URI, js::Valueify(uri));
+    fslots[JSSLOT_NAME_URI] = js::Valueify(uri);
 }
 
 inline jsval
 JSObject::getNamespaceDeclared() const
 {
     JS_ASSERT(isNamespace());
-    return js::Jsvalify(getSlot(JSSLOT_NAMESPACE_DECLARED));
+    return js::Jsvalify(fslots[JSSLOT_NAMESPACE_DECLARED]);
 }
 
 inline void
 JSObject::setNamespaceDeclared(jsval decl)
 {
     JS_ASSERT(isNamespace());
-    setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl));
+    fslots[JSSLOT_NAMESPACE_DECLARED] = js::Valueify(decl);
 }
 
 inline jsval
 JSObject::getQNameLocalName() const
 {
     JS_ASSERT(isQName());
-    return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME));
+    return js::Jsvalify(fslots[JSSLOT_QNAME_LOCAL_NAME]);
 }
 
 inline void
 JSObject::setQNameLocalName(jsval name)
 {
     JS_ASSERT(isQName());
-    setSlot(JSSLOT_QNAME_LOCAL_NAME, js::Valueify(name));
+    fslots[JSSLOT_QNAME_LOCAL_NAME] = js::Valueify(name);
 }
 
 inline JSObject *
 JSObject::getWithThis() const
 {
-    return &getSlot(JSSLOT_WITH_THIS).toObject();
+    return &fslots[JSSLOT_WITH_THIS].toObject();
 }
 
 inline void
 JSObject::setWithThis(JSObject *thisp)
 {
-    getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp);
+    fslots[JSSLOT_WITH_THIS].setObject(*thisp);
 }
 
 inline void
-JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent,
-               void *priv, bool useHoles)
+JSObject::initCommon(js::Class *aclasp, JSObject *proto, JSObject *parent,
+                     JSContext *cx)
 {
+    JS_STATIC_ASSERT(JSSLOT_PRIVATE + 3 == JS_INITIAL_NSLOTS);
+
     clasp = aclasp;
     flags = 0;
 
 #ifdef DEBUG
     /*
      * NB: objShape must not be set here; rather, the caller must call setMap
      * or setSharedNonNativeMap after calling init. To defend this requirement
      * we set map to null in DEBUG builds, and set objShape to a value we then
      * assert obj->shape() never returns.
      */
     map = NULL;
     objShape = JSObjectMap::INVALID_SHAPE;
 #endif
 
     setProto(proto);
     setParent(parent);
-
-    privateData = priv;
-    slots = &fixedSlots[0];
+    fslots[JSSLOT_PRIVATE + 1].setUndefined();
+    fslots[JSSLOT_PRIVATE + 2].setUndefined();
 
-    /*
-     * Fill the fixed slots with undefined or array holes.  This object must
-     * already have its capacity filled in, as by js_NewGCObject.
-     */
-    JS_ASSERT(capacity == numFixedSlots());
-    ClearValueRange(fixedSlots, capacity, useHoles);
+    dslots = NULL;
 
 #ifdef JS_THREADSAFE
     js_InitTitle(cx, &title);
 #endif
 
-    emptyShapes = NULL;
+    emptyShape = NULL;
+}
+
+inline void
+JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent,
+               const js::Value &privateSlotValue, JSContext *cx)
+{
+    initCommon(aclasp, proto, parent, cx);
+    fslots[JSSLOT_PRIVATE] = privateSlotValue;
+}
+
+inline void
+JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent,
+               void *priv, JSContext *cx)
+{
+    initCommon(aclasp, proto, parent, cx);
+    *(void **)&fslots[JSSLOT_PRIVATE] = priv;
+}
+
+inline void
+JSObject::init(js::Class *aclasp, JSObject *proto, JSObject *parent,
+               JSContext *cx)
+{
+    initCommon(aclasp, proto, parent, cx);
+    if (clasp->flags & JSCLASS_HAS_PRIVATE)
+        *(void **)&fslots[JSSLOT_PRIVATE] = NULL;
+    else
+        fslots[JSSLOT_PRIVATE].setUndefined();
 }
 
 inline void
 JSObject::finish(JSContext *cx)
 {
 #ifdef DEBUG
     if (isNative())
         JS_LOCK_RUNTIME_VOID(cx->runtime, cx->runtime->liveObjectProps -= propertyCount());
 #endif
     if (hasSlotsArray())
         freeSlotsArray(cx);
 #ifdef JS_THREADSAFE
     js_FinishTitle(cx, &title);
 #endif
 }
 
-inline bool
-JSObject::initSharingEmptyShape(JSContext *cx,
-                                js::Class *aclasp,
+inline void
+JSObject::initSharingEmptyShape(js::Class *aclasp,
                                 JSObject *proto,
                                 JSObject *parent,
-                                void *privateValue,
-                                /* JSFinalizeGCThingKind */ unsigned kind)
+                                const js::Value &privateSlotValue,
+                                JSContext *cx)
 {
-    init(cx, aclasp, proto, parent, privateValue, false);
+    init(aclasp, proto, parent, privateSlotValue, cx);
 
-    JS_ASSERT(!isDenseArray());
+    js::EmptyShape *empty = proto->emptyShape;
+    JS_ASSERT(empty->getClass() == aclasp);
+    setMap(empty);
+}
 
-    js::EmptyShape *empty = proto->getEmptyShape(cx, aclasp, JSFinalizeGCThingKind(kind));
-    if (!empty)
-        return false;
+inline void
+JSObject::initSharingEmptyShape(js::Class *aclasp,
+                                JSObject *proto,
+                                JSObject *parent,
+                                void *priv,
+                                JSContext *cx)
+{
+    init(aclasp, proto, parent, priv, cx);
 
+    js::EmptyShape *empty = proto->emptyShape;
+    JS_ASSERT(empty->getClass() == aclasp);
     setMap(empty);
-    return true;
 }
 
 inline void
 JSObject::freeSlotsArray(JSContext *cx)
 {
     JS_ASSERT(hasSlotsArray());
-    cx->free(slots);
-}
-
-inline void
-JSObject::removeSlotsArray(JSContext *cx)
-{
-    JS_ASSERT(hasSlotsArray());
-
-    size_t fixed = numFixedSlots();
-    JS_ASSERT(fixed <= capacity);
-    memcpy(fixedSlots, slots, fixed * sizeof(js::Value));
-
-    freeSlotsArray(cx);
-    slots = &fixedSlots[0];
-    capacity = fixed;
+    JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS);
+    cx->free(dslots - 1);
 }
 
 inline bool
 JSObject::hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags)
 {
     JSObject *pobj;
     JSProperty *prop;
     JSAutoResolveFlags rf(cx, flags);
@@ -753,43 +786,44 @@ class AutoPropertyDescriptorRooter : pri
         setter = desc->setter;
         value = desc->value;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 };
 
 static inline bool
-InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto,
-                   JSFinalizeGCThingKind kind)
+InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto)
 {
     JS_ASSERT(clasp->isNative());
     JS_ASSERT(proto == obj->getProto());
 
     /* Share proto's emptyShape only if obj is similar to proto. */
     js::EmptyShape *empty = NULL;
 
     if (proto) {
         JS_LOCK_OBJ(cx, proto);
         if (proto->canProvideEmptyShape(clasp)) {
-            empty = proto->getEmptyShape(cx, clasp, kind);
+            empty = proto->getEmptyShape(cx, clasp);
             JS_UNLOCK_OBJ(cx, proto);
             if (!empty)
                 goto bad;
         } else {
             JS_UNLOCK_OBJ(cx, proto);
         }
     }
 
     if (!empty) {
+        uint32 freeslot = JSSLOT_FREE(clasp);
+        JS_ASSERT(freeslot >= JSSLOT_PRIVATE);
+
         empty = js::EmptyShape::create(cx, clasp);
         if (!empty)
             goto bad;
-        uint32 freeslot = JSSLOT_FREE(clasp);
-        if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot))
+        if (freeslot > JS_INITIAL_NSLOTS && !obj->allocSlots(cx, freeslot))
             goto bad;
     }
 
     obj->setMap(empty);
     return true;
 
   bad:
     /* The GC nulls map initially. It should still be null on error. */
@@ -799,70 +833,61 @@ InitScopeForObject(JSContext* cx, JSObje
 
 /*
  * Helper optimized for creating a native instance of the given class (not the
  * class's prototype object). Use this in preference to NewObject, but use
  * NewBuiltinClassInstance if you need the default class prototype as proto,
  * and its parent global as parent.
  */
 static inline JSObject *
-NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto,
-                       JSObject *parent, JSFinalizeGCThingKind kind)
+NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
 {
     JS_ASSERT(proto);
     JS_ASSERT(proto->isNative());
     JS_ASSERT(parent);
 
     /*
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC.
      */
-    JSObject* obj = js_NewGCObject(cx, kind);
+    JSObject* obj = js_NewGCObject(cx);
 
     if (obj) {
         /*
          * Default parent to the parent of the prototype, which was set from
          * the parent of the prototype's constructor.
          */
-        bool useHoles = (clasp == &js_ArrayClass);
-        obj->init(cx, clasp, proto, parent, NULL, useHoles);
+        obj->init(clasp, proto, parent, cx);
 
         JS_LOCK_OBJ(cx, proto);
         JS_ASSERT(proto->canProvideEmptyShape(clasp));
-        js::EmptyShape *empty = proto->getEmptyShape(cx, clasp, kind);
+        js::EmptyShape *empty = proto->getEmptyShape(cx, clasp);
         JS_UNLOCK_OBJ(cx, proto);
 
         if (empty)
             obj->setMap(empty);
         else
             obj = NULL;
     }
 
     return obj;
 }
 
-static inline JSObject *
-NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
-{
-    JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
-    return NewNativeClassInstance(cx, clasp, proto, parent, kind);
-}
-
 bool
 FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
                    Class *clasp);
 
 /*
  * Helper used to create Boolean, Date, RegExp, etc. instances of built-in
  * classes with class prototypes of the same Class. See, e.g., jsdate.cpp,
  * jsregexp.cpp, and js_PrimitiveToObject in jsobj.cpp. Use this to get the
  * right default proto and parent for clasp in cx.
  */
 static inline JSObject *
-NewBuiltinClassInstance(JSContext *cx, Class *clasp, JSFinalizeGCThingKind kind)
+NewBuiltinClassInstance(JSContext *cx, Class *clasp)
 {
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
     JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
     JS_ASSERT(protoKey != JSProto_Null);
 
     /* NB: inline-expanded and specialized version of js_GetClassPrototype. */
     JSObject *global;
@@ -881,24 +906,17 @@ NewBuiltinClassInstance(JSContext *cx, C
     if (v.isObject()) {
         proto = &v.toObject();
         JS_ASSERT(proto->getParent() == global);
     } else {
         if (!FindClassPrototype(cx, global, protoKey, &proto, clasp))
             return NULL;
     }
 
-    return NewNativeClassInstance(cx, clasp, proto, global, kind);
-}
-
-static inline JSObject *
-NewBuiltinClassInstance(JSContext *cx, Class *clasp)
-{
-    JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
-    return NewBuiltinClassInstance(cx, clasp, kind);
+    return NewNativeClassInstance(cx, clasp, proto, global);
 }
 
 static inline JSProtoKey
 GetClassProtoKey(js::Class *clasp)
 {
     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
     if (key != JSProto_Null)
         return key;
@@ -939,127 +957,77 @@ namespace WithProto {
  *
  * Note that as a template, there will be lots of instantiations, which means
  * the internals will be specialized based on the template parameters.
  */
 namespace detail
 {
 template <bool withProto, bool isFunction>
 static JS_ALWAYS_INLINE JSObject *
-NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
-          JSFinalizeGCThingKind kind)
+NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
 {
     /* Bootstrap the ur-object, and make it the default prototype object. */
     if (withProto == WithProto::Class && !proto) {
         JSProtoKey protoKey = GetClassProtoKey(clasp);
         if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp))
             return NULL;
         if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
             return NULL;
-    }
+     }
+
 
     /*
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC. Functions have a
      * larger non-standard allocation size.
      *
      * The should be specialized by the template.
      */
-    JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind);
+    JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx);
     if (!obj)
         goto out;
 
-    /* This needs to match up with the size of JSFunction::data_padding. */
-    JS_ASSERT_IF(isFunction, kind == FINALIZE_OBJECT2);
-
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
-    obj->init(cx, clasp, proto,
-              (!parent && proto) ? proto->getParent() : parent,
-              NULL, clasp == &js_ArrayClass);
+    obj->init(clasp, proto, (!parent && proto) ? proto->getParent() : parent, cx);
 
     if (clasp->isNative()) {
-        if (!InitScopeForObject(cx, obj, clasp, proto, kind)) {
+        if (!InitScopeForObject(cx, obj, clasp, proto)) {
             obj = NULL;
             goto out;
         }
     } else {
         obj->setSharedNonNativeMap();
     }
 
 out:
     Probes::createObject(cx, obj);
     return obj;
 }
-} /* namespace detail */
+}
 
 static JS_ALWAYS_INLINE JSObject *
 NewFunction(JSContext *cx, JSObject *parent)
 {
-    return detail::NewObject<WithProto::Class, true>(cx, &js_FunctionClass, NULL, parent,
-                                                     FINALIZE_OBJECT2);
-}
-
-template <WithProto::e withProto>
-static JS_ALWAYS_INLINE JSObject *
-NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
-               JSFinalizeGCThingKind kind)
-{
-    return detail::NewObject<withProto, false>(cx, clasp, proto, parent, kind);
+    return detail::NewObject<WithProto::Class, true>(cx, &js_FunctionClass, NULL, parent);
 }
 
 template <WithProto::e withProto>
 static JS_ALWAYS_INLINE JSObject *
 NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
 {
-    JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
-    return detail::NewObject<withProto, false>(cx, clasp, proto, parent, kind);
+    return detail::NewObject<withProto, false>(cx, clasp, proto, parent);
 }
 
 template <WithProto::e withProto>
 static JS_ALWAYS_INLINE JSObject *
 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
 {
-    if (clasp == &js_FunctionClass) {
-        return detail::NewObject<withProto, true>(cx, clasp, proto, parent,
-                                                  FINALIZE_OBJECT2);
-    } else {
-        JSFinalizeGCThingKind kind = js_GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
-        return detail::NewObject<withProto, false>(cx, clasp, proto, parent, kind);
-    }
-}
-
-/* Creates a new array with a zero length and the given finalize kind. */
-static inline JSObject *
-NewArrayWithKind(JSContext* cx, JSFinalizeGCThingKind kind)
-{
-    return NewNonFunction<WithProto::Class>(cx, &js_ArrayClass, NULL, NULL, kind);
-}
-
-/*
- * As for js_GetGCObjectKind, where numSlots is a guess at the final size of
- * the object, zero if the final size is unknown.
- */
-static inline JSFinalizeGCThingKind
-GuessObjectGCKind(size_t numSlots)
-{
-    return numSlots ? js_GetGCObjectKind(numSlots) : FINALIZE_OBJECT8;
-}
-
-/*
- * Get the GC kind to use for scripted 'new' on the given class.
- * FIXME bug 547327: estimate the size from the allocation site.
- */
-static inline JSFinalizeGCThingKind
-NewObjectGCKind(JSContext *cx, js::Class *clasp)
-{
-    if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass)
-        return FINALIZE_OBJECT8;
-    if (clasp == &js_FunctionClass)
-        return FINALIZE_OBJECT2;
-    return FINALIZE_OBJECT4;
+    return (clasp == &js_FunctionClass)
+           ? detail::NewObject<withProto, true>(cx, clasp, proto, parent)
+           : detail::NewObject<withProto, false>(cx, clasp, proto, parent);
 }
 
 } /* namespace js */
 
 #endif /* jsobjinlines_h___ */
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -4478,17 +4478,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 if (sn && SN_TYPE(sn) == SRC_CONTINUE && SprintCString(&ss->sprinter, ", ") < 0)
                     return NULL;
                 if (SprintCString(&ss->sprinter, "]") < 0)
                     return NULL;
                 break;
 
               case JSOP_NEWINIT:
               {
-                i = GET_UINT16(pc);
+                i = GET_INT8(pc);
                 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
 
                 todo = ss->sprinter.offset;
 #if JS_HAS_SHARP_VARS
                 op = (JSOp)pc[len];
                 if (op == JSOP_SHARPINIT)
                     op = (JSOp)pc[len += JSOP_SHARPINIT_LENGTH];
                 if (op == JSOP_DEFSHARP) {
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -239,17 +239,17 @@ OPDEF(JSOP_GETARG,    84, "getarg",     
 OPDEF(JSOP_SETARG,    85, "setarg",     NULL,         3,  1,  1,  3,  JOF_QARG |JOF_NAME|JOF_SET)
 OPDEF(JSOP_GETLOCAL,  86,"getlocal",    NULL,         3,  0,  1, 19,  JOF_LOCAL|JOF_NAME)
 OPDEF(JSOP_SETLOCAL,  87,"setlocal",    NULL,         3,  1,  1,  3,  JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING)
 
 /* Push unsigned 16-bit int constant. */
 OPDEF(JSOP_UINT16,    88, "uint16",     NULL,         3,  0,  1, 16,  JOF_UINT16)
 
 /* Object and array literal support. */
-OPDEF(JSOP_NEWINIT,   89, "newinit",    NULL,         5,  0,  1, 19,  JOF_UINT16PAIR)
+OPDEF(JSOP_NEWINIT,   89, "newinit",    NULL,         2,  0,  1, 19,  JOF_INT8)
 OPDEF(JSOP_ENDINIT,   90, "endinit",    NULL,         1,  0,  0, 19,  JOF_BYTE)
 OPDEF(JSOP_INITPROP,  91, "initprop",   NULL,         3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INITELEM,  92, "initelem",   NULL,         1,  3,  1,  3,  JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_DEFSHARP,  93, "defsharp",   NULL,         5,  0,  0,  0,  JOF_UINT16PAIR|JOF_SHARPSLOT)
 OPDEF(JSOP_USESHARP,  94, "usesharp",   NULL,         5,  0,  1,  0,  JOF_UINT16PAIR|JOF_SHARPSLOT)
 
 /* Fast inc/dec ops for args and locals. */
 OPDEF(JSOP_INCARG,    95, "incarg",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT2)
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1202,48 +1202,48 @@ static JSFunctionSpec static_methods[] =
     JS_FN("isTrapping",     proxy_isTrapping,      1, 0),
     JS_FN("fix",            proxy_fix,             1, 0),
 #endif
     JS_FS_END
 };
 
 extern Class CallableObjectClass;
 
-static const uint32 JSSLOT_CALLABLE_CALL = 0;
-static const uint32 JSSLOT_CALLABLE_CONSTRUCT = 1;
+static const uint32 JSSLOT_CALLABLE_CALL = JSSLOT_PRIVATE;
+static const uint32 JSSLOT_CALLABLE_CONSTRUCT = JSSLOT_PRIVATE + 1;
 
 static JSBool
 callable_Call(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *thisobj = ComputeThisFromVp(cx, vp);
     if (!thisobj)
         return false;
 
     JSObject *callable = &JS_CALLEE(cx, vp).toObject();
     JS_ASSERT(callable->getClass() == &CallableObjectClass);
-    const Value &fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
+    const Value &fval = callable->fslots[JSSLOT_CALLABLE_CALL];
     Value rval;
     bool ok = ExternalInvoke(cx, thisobj, fval, argc, JS_ARGV(cx, vp), &rval);
     *vp = rval;
     return ok;
 }
 
 static JSBool
 callable_Construct(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *thisobj = js_NewInstance(cx, &JS_CALLEE(cx, vp).toObject());
     if (!thisobj)
         return false;
 
     JSObject *callable = &vp[0].toObject();
     JS_ASSERT(callable->getClass() == &CallableObjectClass);
-    Value fval = callable->getSlot(JSSLOT_CALLABLE_CONSTRUCT);
+    Value fval = callable->fslots[JSSLOT_CALLABLE_CONSTRUCT];
     if (fval.isUndefined()) {
         /* We don't have an explicit constructor so allocate a new object and use the call. */
-        fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
+        fval = callable->fslots[JSSLOT_CALLABLE_CALL];
         JS_ASSERT(fval.isObject());
 
         /* callable is the constructor, so get callable.prototype is the proto of the new object. */
         Value protov;
         if (!callable->getProperty(cx, ATOM_TO_JSID(ATOM(classPrototype)), &protov))
             return false;
 
         JSObject *proto;
@@ -1255,17 +1255,17 @@ callable_Construct(JSContext *cx, uintN 
         }
 
         JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
         if (!newobj)
             return false;
 
         /* If the call returns an object, return that, otherwise the original newobj. */
         Value rval;
-        if (!ExternalInvoke(cx, newobj, callable->getSlot(JSSLOT_CALLABLE_CALL),
+        if (!ExternalInvoke(cx, newobj, callable->fslots[JSSLOT_CALLABLE_CALL],
                             argc, vp + 2, &rval)) {
             return false;
         }
         if (rval.isPrimitive())
             vp->setObject(*newobj);
         else
             *vp = rval;
         return true;
@@ -1313,40 +1313,35 @@ FixProxy(JSContext *cx, JSObject *proxy,
     JSObject *props;
     if (!(props = NonNullObject(cx, tvr.value())))
         return false;
 
     JSObject *proto = proxy->getProto();
     JSObject *parent = proxy->getParent();
     Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass;
 
-    /*
-     * Make a blank object from the recipe fix provided to us.  This must have
-     * number of fixed slots as the proxy so that we can swap their contents.
-     */
-    JSFinalizeGCThingKind kind = js_KindFromGCThing(proxy);
-    JSObject *newborn = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, kind);
+    /* Make a blank object from the recipe fix provided to us. */
+    JSObject *newborn = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
     if (!newborn)
         return NULL;
     AutoObjectRooter tvr2(cx, newborn);
 
     if (clasp == &CallableObjectClass) {
-        newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy));
-        newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy));
+        newborn->fslots[JSSLOT_CALLABLE_CALL] = GetCall(proxy);
+        newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = GetConstruct(proxy);
     }
 
     {
         AutoPendingProxyOperation pending(cx, proxy);
         if (!js_PopulateObject(cx, newborn, props))
             return false;
     }
 
     /* Trade spaces between the newborn object and the proxy. */
-    if (!proxy->swap(cx, newborn))
-        return false;
+    proxy->swap(newborn);
 
     /* The GC will dispose of the proxy object. */
 
     *bp = true;
     return true;
 }
 
 }
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -121,21 +121,21 @@ class JSProxy {
     /* Spidermonkey extensions. */
     static bool call(JSContext *cx, JSObject *proxy, uintN argc, js::Value *vp);
     static bool construct(JSContext *cx, JSObject *proxy, uintN argc, js::Value *argv, js::Value *rval);
     static JSString *obj_toString(JSContext *cx, JSObject *proxy);
     static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
 };
 
 /* Shared between object and function proxies. */
-const uint32 JSSLOT_PROXY_HANDLER = 0;
-const uint32 JSSLOT_PROXY_PRIVATE = 1;
+const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0;
+const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 1;
 /* Function proxies only. */
-const uint32 JSSLOT_PROXY_CALL = 2;
-const uint32 JSSLOT_PROXY_CONSTRUCT = 3;
+const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 2;
+const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 3;
 
 extern JS_FRIEND_API(js::Class) ObjectProxyClass;
 extern JS_FRIEND_API(js::Class) FunctionProxyClass;
 extern JS_FRIEND_API(js::Class) OuterWindowProxyClass;
 extern js::Class CallableObjectClass;
 
 }
 
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -169,38 +169,38 @@ VALUE_IS_REGEXP(JSContext *cx, js::Value
 {
     return !v.isPrimitive() && v.toObject().isRegExp();
 }
 
 inline const js::Value &
 JSObject::getRegExpLastIndex() const
 {
     JS_ASSERT(isRegExp());
-    return getSlot(JSSLOT_REGEXP_LAST_INDEX);
+    return fslots[JSSLOT_REGEXP_LAST_INDEX];
 }
 
 inline void
 JSObject::setRegExpLastIndex(const js::Value &v)
 {
     JS_ASSERT(isRegExp());
-    setSlot(JSSLOT_REGEXP_LAST_INDEX, v);
+    fslots[JSSLOT_REGEXP_LAST_INDEX] = v;
 }
 
 inline void
 JSObject::setRegExpLastIndex(jsdouble d)
 {
     JS_ASSERT(isRegExp());
-    setSlot(JSSLOT_REGEXP_LAST_INDEX, js::NumberValue(d));
+    fslots[JSSLOT_REGEXP_LAST_INDEX] = js::NumberValue(d);
 }
 
 inline void
 JSObject::zeroRegExpLastIndex()
 {
     JS_ASSERT(isRegExp());
-    getSlotRef(JSSLOT_REGEXP_LAST_INDEX).setInt32(0);
+    fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0);
 }
 
 namespace js { class AutoStringRooter; }
 
 inline bool
 JSObject::isRegExp() const
 {
     return getClass() == &js_RegExpClass;
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1123,32 +1123,16 @@ JSObject::removeProperty(JSContext *cx, 
     } else {
         /*
          * Non-dictionary-mode property tables are shared immutables, so all we
          * need do is retract lastProp and we'll either get or else lazily make
          * via a later maybeHash the exact table for the new property lineage.
          */
         JS_ASSERT(shape == lastProp);
         removeLastProperty();
-
-        /*
-         * Revert to fixed slots if this was the first dynamically allocated slot,
-         * preserving invariant that objects with the same shape use the fixed
-         * slots in the same way.
-         */
-        size_t fixed = numFixedSlots();
-        if (shape->slot == fixed) {
-            JS_ASSERT(hasSlotsArray());
-            JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(),
-                         lastProp->slot == fixed - 1);
-            memcpy(fixedSlots, slots, fixed * sizeof(Value));
-            freeSlotsArray(cx);
-            slots = fixedSlots;
-            capacity = fixed;
-        }
     }
     updateShape(cx);
 
     /* Last, consider shrinking table if its load factor is <= .25. */
     if (PropertyTable *table = lastProp->table) {
         uint32 size = table->capacity();
         if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2) {
             METER(shrinks);
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -55,55 +55,29 @@ js::Shape::freeTable(JSContext *cx)
 {
     if (table) {
         cx->destroy(table);
         table = NULL;
     }
 }
 
 inline js::EmptyShape *
-JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp,
-                        /* JSFinalizeGCThingKind */ unsigned kind)
+JSObject::getEmptyShape(JSContext *cx, js::Class *aclasp)
 {
-    JS_ASSERT(kind >= FINALIZE_OBJECT0 && kind <= FINALIZE_OBJECT_LAST);
-    int i = kind - FINALIZE_OBJECT0;
-
-    if (!emptyShapes) {
-        int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1;
-        emptyShapes = (js::EmptyShape**) cx->calloc(sizeof(js::EmptyShape*) * count);
-        if (!emptyShapes)
-            return NULL;
-
-        /*
-         * Always fill in emptyShapes[0], so canProvideEmptyShape works.
-         * Other empty shapes are filled in lazily.
-         */
-        emptyShapes[0] = js::EmptyShape::create(cx, aclasp);
-        if (!emptyShapes[0]) {
-            cx->free(emptyShapes);
-            emptyShapes = NULL;
-            return NULL;
-        }
-    }
-
-    JS_ASSERT(aclasp == emptyShapes[0]->getClass());
-
-    if (!emptyShapes[i]) {
-        emptyShapes[i] = js::EmptyShape::create(cx, aclasp);
-        if (!emptyShapes[i])
-            return NULL;
-    }
-
-    return emptyShapes[i];
+    if (emptyShape)
+        JS_ASSERT(aclasp == emptyShape->getClass());
+    else
+        emptyShape = js::EmptyShape::create(cx, aclasp);
+    return emptyShape;
 }
 
 inline bool
 JSObject::canProvideEmptyShape(js::Class *aclasp)
 {
-    return !emptyShapes || emptyShapes[0]->getClass() == aclasp;
+    return !emptyShape || emptyShape->getClass() == aclasp;
 }
 
 inline void
 JSObject::updateShape(JSContext *cx)
 {
     JS_ASSERT(isNative());
     js::LeaveTraceIfGlobalObject(cx, this);
     if (hasOwnShape())
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2668,31 +2668,34 @@ TraceRecorder::p2i(nanojit::LIns* ins)
     return ins;
 #endif
 }
 
 ptrdiff_t
 TraceRecorder::nativeGlobalSlot(const Value* p) const
 {
     JS_ASSERT(isGlobal(p));
-    return ptrdiff_t(p - globalObj->slots);
+    if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS)
+        return ptrdiff_t(p - globalObj->fslots);
+    return ptrdiff_t((p - globalObj->dslots) + JS_INITIAL_NSLOTS);
 }
 
 /* Determine the offset in the native global frame for a jsval we track. */
 ptrdiff_t
 TraceRecorder::nativeGlobalOffset(const Value* p) const
 {
     return nativeGlobalSlot(p) * sizeof(double);
 }
 
 /* Determine whether a value is a global stack slot. */
 bool
 TraceRecorder::isGlobal(const Value* p) const
 {
-    return (size_t(p - globalObj->slots) < globalObj->numSlots());
+    return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) ||
+            (size_t(p - globalObj->dslots) < (globalObj->numSlots() - JS_INITIAL_NSLOTS)));
 }
 
 bool
 TraceRecorder::isVoidPtrGlobal(const void* p) const
 {
     return isGlobal((const Value *)p);
 }
 
@@ -3439,17 +3442,17 @@ struct ArgClosureTraits
 
     // Get the right object slots to use our slot index with.
     static inline Value get_slot(JSObject* obj, unsigned slot) {
         return obj->getSlot(slot_offset(obj) + slot);
     }
 
     // Get the offset of our object slots from the object's dslots pointer.
     static inline uint32 slot_offset(JSObject* obj) {
-        return JSObject::CALL_RESERVED_SLOTS;
+        return JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS;
     }
 
     // Get the maximum slot index of this type that should be allowed
     static inline uint16 slot_count(JSObject* obj) {
         return obj->getCallObjCalleeFunction()->nargs;
     }
 
 private:
@@ -3470,17 +3473,17 @@ struct VarClosureTraits
         return fp->slots()[slot];
     }
 
     static inline Value get_slot(JSObject* obj, unsigned slot) {
         return obj->getSlot(slot_offset(obj) + slot);
     }
 
     static inline uint32 slot_offset(JSObject* obj) {
-        return JSObject::CALL_RESERVED_SLOTS +
+        return JSSLOT_START(&js_CallClass) + JSObject::CALL_RESERVED_SLOTS +
                obj->getCallObjCalleeFunction()->nargs;
     }
 
     static inline uint16 slot_count(JSObject* obj) {
         return obj->getCallObjCalleeFunction()->u.i.nvars;
     }
 
 private:
@@ -3935,35 +3938,35 @@ TraceRecorder::known(const Value* vp)
 
 JS_REQUIRES_STACK inline bool
 TraceRecorder::known(JSObject** p)
 {
     return knownImpl(p);
 }
 
 /*
- * The slots of the global object are sometimes reallocated by the interpreter.
+ * The dslots of the global object are sometimes reallocated by the interpreter.
  * This function check for that condition and re-maps the entries of the tracker
  * accordingly.
  */
 JS_REQUIRES_STACK void
 TraceRecorder::checkForGlobalObjectReallocationHelper()
 {
     debug_only_print0(LC_TMTracer, "globalObj->dslots relocated, updating tracker\n");
     Value* src = global_dslots;
-    Value* dst = globalObj->getSlots();
-    jsuint length = globalObj->capacity;
+    Value* dst = globalObj->dslots;
+    jsuint length = globalObj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS;
     LIns** map = (LIns**)alloca(sizeof(LIns*) * length);
     for (jsuint n = 0; n < length; ++n) {
         map[n] = tracker.get(src);
         tracker.set(src++, NULL);
     }
     for (jsuint n = 0; n < length; ++n)
         tracker.set(dst++, map[n]);
-    global_dslots = globalObj->getSlots();
+    global_dslots = globalObj->dslots;
 }
 
 /* Determine whether the current branch is a loop edge (taken or not taken). */
 static JS_REQUIRES_STACK bool
 IsLoopEdge(jsbytecode* pc, jsbytecode* header)
 {
     switch (*pc) {
       case JSOP_IFEQ:
@@ -8715,17 +8718,17 @@ TraceRecorder::incProp(jsint incr, bool 
 
     if (slot == SHAPE_INVALID_SLOT)
         RETURN_STOP_A("incProp on invalid slot");
 
     Value& v = obj->getSlotRef(slot);
     CHECK_STATUS_A(inc(v, v_ins, incr, pre));
 
     LIns* dslots_ins = NULL;
-    stobj_set_slot(obj, obj_ins, slot, dslots_ins, v, v_ins);
+    stobj_set_slot(obj_ins, slot, dslots_ins, v, v_ins);
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::incElem(jsint incr, bool pre)
 {
     Value& r = stackval(-1);
     Value& l = stackval(-2);
@@ -9453,80 +9456,96 @@ TraceRecorder::guardPropertyCacheHit(LIn
 
     pcval = entry->vword;
     return RECORD_CONTINUE;
 }
 
 void
 TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, const Value &v, LIns* v_ins)
 {
-    box_value_into(v, v_ins, obj_ins, offsetof(JSObject, fixedSlots) + slot * sizeof(Value), ACCSET_OTHER);
+    box_value_into(v, v_ins, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(Value), ACCSET_OTHER);
 }
 
 void
 TraceRecorder::stobj_set_dslot(LIns *obj_ins, unsigned slot, LIns*& dslots_ins, 
                                const Value &v, LIns* v_ins)
 {
     if (!dslots_ins)
-        dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER);
+        dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER);
     box_value_into(v, v_ins, dslots_ins, slot * sizeof(Value), ACCSET_OTHER);
 }
 
 void
-TraceRecorder::stobj_set_slot(JSObject *obj, LIns* obj_ins, unsigned slot, LIns*& dslots_ins,
+TraceRecorder::stobj_set_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins,
                               const Value &v, LIns* v_ins)
 {
-    /*
-     * A shape guard must have already been generated for obj, which will
-     * ensure that future objects have the same number of fixed slots.
-     */
-    if (!obj->hasSlotsArray()) {
-        JS_ASSERT(slot < obj->numSlots());
+    if (slot < JS_INITIAL_NSLOTS)
         stobj_set_fslot(obj_ins, slot, v, v_ins);
-    } else {
-        stobj_set_dslot(obj_ins, slot, dslots_ins, v, v_ins);
-    }
+    else
+        stobj_set_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins, v, v_ins);
 }
 
 #if JS_BITS_PER_WORD == 32 || JS_BITS_PER_WORD == 64
+void
+TraceRecorder::set_array_fslot(LIns *obj_ins, unsigned slot, uint32 val)
+{
+    /*
+     * We can assume the destination fslot has been appropriately tagged so we
+     * can just overwrite the 32-bit payload.
+     */
+    lir->insStore(INS_CONSTU(val), obj_ins,
+                  offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset,
+                  ACCSET_OTHER);
+}
+
 LIns*
-TraceRecorder::stobj_get_slot_uint32(LIns* obj_ins, unsigned slot)
-{
-    LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER);
-    return lir->insLoad(LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER);
+TraceRecorder::stobj_get_fslot_uint32(LIns* obj_ins, unsigned slot)
+{
+    JS_ASSERT(slot < JS_INITIAL_NSLOTS);
+    return lir->insLoad(LIR_ldi, obj_ins,
+                        offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset,
+                        ACCSET_OTHER);
+}
+
+LIns*
+TraceRecorder::stobj_set_fslot_uint32(LIns* value_ins, LIns* obj_ins, unsigned slot)
+{
+    JS_ASSERT(slot < JS_INITIAL_NSLOTS);
+    return lir->insStore(LIR_sti, value_ins, obj_ins,
+                         offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset,
+                         ACCSET_OTHER);
 }
 #endif
 
 LIns*
 TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit *exit)
 {
     LIns *vaddr_ins;
     ptrdiff_t offset;
-
-    /* Same guarantee about fixed slots as stobj_set_slot. */
-    if (!obj->hasSlotsArray()) {
+    if (slot < JS_INITIAL_NSLOTS) {
         vaddr_ins = obj_ins;
-        offset = offsetof(JSObject, fixedSlots) + slot * sizeof(Value);
-    } else {
-        vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER);
-        offset = slot * sizeof(Value);
+        offset = offsetof(JSObject, fslots) + slot * sizeof(Value);
+    } else {
+        vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER);
+        offset = (slot - JS_INITIAL_NSLOTS) * sizeof(Value);
     }
 
     const Value &v = obj->getSlot(slot);
     return unbox_value(v, vaddr_ins, offset, exit);
 }
 
 #if JS_BITS_PER_WORD == 32
 
 LIns*
-TraceRecorder::stobj_get_const_private_ptr(LIns *obj_ins, unsigned slot)
-{
-    LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER);
-    return lir->insLoad(LIR_ldi, vaddr_ins,
-                        slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER, LOAD_CONST);
+TraceRecorder::stobj_get_fslot_private_ptr(LIns *obj_ins, unsigned slot)
+{
+    JS_ASSERT(slot < JS_INITIAL_NSLOTS && slot != JSSLOT_PRIVATE);
+    return lir->insLoad(LIR_ldi, obj_ins,
+                        offsetof(JSObject, fslots) + slot * sizeof(Value) + sPayloadOffset,
+                        ACCSET_OTHER, LOAD_CONST);
 }
 
 void
 TraceRecorder::box_undefined_into(LIns *vaddr_ins, ptrdiff_t offset, AccSet accSet)
 {
     lir->insStore(INS_CONSTU(JSVAL_TAG_UNDEFINED), vaddr_ins, offset + sTagOffset, accSet);
     lir->insStore(INS_CONST(0), vaddr_ins, offset + sPayloadOffset, accSet);
 }
@@ -9673,22 +9692,23 @@ LIns*
 TraceRecorder::box_value_for_native_call(const Value &v, LIns *v_ins)
 {
     return box_value_into_alloc(v, v_ins);
 }
 
 #elif JS_BITS_PER_WORD == 64
 
 LIns*
-TraceRecorder::stobj_get_const_private_ptr(LIns *obj_ins, unsigned slot)
+TraceRecorder::stobj_get_fslot_private_ptr(LIns *obj_ins, unsigned slot)
 {
     /* N.B. On 64-bit, privates are encoded differently from other pointers. */
-    LIns *vaddr_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER);
-    LIns *v_ins = lir->insLoad(LIR_ldq, vaddr_ins,
-                               slot * sizeof(Value) + sPayloadOffset, ACCSET_OTHER, LOAD_CONST);
+    JS_ASSERT(slot < JS_INITIAL_NSLOTS && slot != JSSLOT_PRIVATE);
+    LIns *v_ins = lir->insLoad(LIR_ldq, obj_ins,
+                               offsetof(JSObject, fslots) + slot * sizeof(Value),
+                               ACCSET_OTHER, LOAD_CONST);
     return lir->ins2ImmI(LIR_lshq, v_ins, 1);
 }
 
 void
 TraceRecorder::box_undefined_into(LIns *vaddr_ins, ptrdiff_t offset, AccSet accSet)
 {
     lir->insStore(INS_CONSTQWORD(JSVAL_BITS(JSVAL_VOID)), vaddr_ins, offset, accSet);
 }
@@ -9849,27 +9869,18 @@ LIns*
 TraceRecorder::stobj_get_parent(nanojit::LIns* obj_ins)
 {
     return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, parent), ACCSET_OTHER);
 }
 
 LIns*
 TraceRecorder::stobj_get_private(nanojit::LIns* obj_ins)
 {
-    return lir->insLoad(LIR_ldp, obj_ins,
-                        offsetof(JSObject, privateData),
-                        ACCSET_OTHER);
-}
-
-LIns*
-TraceRecorder::stobj_get_private_uint32(nanojit::LIns* obj_ins)
-{
-    return lir->insLoad(LIR_ldi, obj_ins,
-                        offsetof(JSObject, privateData),
-                        ACCSET_OTHER);
+    JS_STATIC_ASSERT(JSSLOT_PRIVATE == 0);
+    return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots), ACCSET_OTHER);
 }
 
 LIns*
 TraceRecorder::stobj_get_proto(nanojit::LIns* obj_ins)
 {
     return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, proto), ACCSET_OTHER);
 }
 
@@ -10405,17 +10416,17 @@ TraceRecorder::newArguments(LIns* callee
     LIns* global_ins = INS_CONSTOBJ(globalObj);
     LIns* argc_ins = INS_CONST(cx->fp()->numActualArgs());
 
     LIns* args[] = { callee_ins, argc_ins, global_ins, cx_ins };
     LIns* argsobj_ins = lir->insCall(&js_NewArgumentsOnTrace_ci, args);
     guard(false, lir->insEqP_0(argsobj_ins), OOM_EXIT);
 
     if (strict) {
-        LIns* argsData_ins = stobj_get_const_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA);
+        LIns* argsData_ins = stobj_get_fslot_private_ptr(argsobj_ins, JSObject::JSSLOT_ARGS_DATA);
         ptrdiff_t slotsOffset = offsetof(ArgumentsData, slots);
         cx->fp()->forEachCanonicalActualArg(BoxArg(this, slotsOffset, argsData_ins));
     }
 
     return argsobj_ins;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
@@ -10800,17 +10811,17 @@ TraceRecorder::getClassPrototype(JSObjec
     JS_ASSERT(found);
     JS_ASSERT((~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)) == 0);
 #endif
 
     // Since ctor was built by js_InitClass, we can assert (rather than check)
     // that pval is usable.
     JS_ASSERT(!pval.isPrimitive());
     JSObject *proto = &pval.toObject();
-    JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShapes[0]->getClass() == clasp);
+    JS_ASSERT_IF(clasp != &js_ArrayClass, proto->emptyShape->getClass() == clasp);
 
     proto_ins = INS_CONSTOBJ(proto);
     return RECORD_CONTINUE;
 }
 
 RecordingStatus
 TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins)
 {
@@ -10824,18 +10835,17 @@ TraceRecorder::getClassPrototype(JSProto
 
     // This should not have reentered.
     JS_ASSERT(localtm.recorder);
 
 #ifdef DEBUG
     /* Double-check that a native proto has a matching emptyShape. */
     if (key != JSProto_Array) {
         JS_ASSERT(proto->isNative());
-        JS_ASSERT(proto->emptyShapes);
-        EmptyShape *empty = proto->emptyShapes[0];
+        EmptyShape *empty = proto->emptyShape;
         JS_ASSERT(empty);
         JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(empty->getClass()) == key);
     }
 #endif
 
     proto_ins = INS_CONSTOBJ(proto);
     return RECORD_CONTINUE;
 }
@@ -11739,17 +11749,17 @@ TraceRecorder::nativeSet(JSObject* obj, 
         JS_ASSERT(obj->containsSlot(shape->slot));
         JS_ASSERT(shape->hasSlot());
         if (obj == globalObj) {
             if (!lazilyImportGlobalSlot(slot))
                 RETURN_STOP("lazy import of global slot failed");
             set(&obj->getSlotRef(slot), v_ins);
         } else {
             LIns* dslots_ins = NULL;
-            stobj_set_slot(obj, obj_ins, slot, dslots_ins, v, v_ins);
+            stobj_set_slot(obj_ins, slot, dslots_ins, v, v_ins);
         }
     }
 
     return RECORD_CONTINUE;
 }
 
 static JSBool FASTCALL
 MethodWriteBarrier(JSContext* cx, JSObject* obj, Shape* shape, JSObject* funobj)
@@ -11908,17 +11918,17 @@ TraceRecorder::setCallProp(JSObject *cal
         }
 
         // Now assert that the shortid get we did above was ok. Have to do it
         // after the RETURN_STOP above, since in that case we may in fact not
         // have a valid shortid; but we don't use it in that case anyway.
         JS_ASSERT(shape->hasShortID());
 
         LIns* dslots_ins = NULL;
-        stobj_set_dslot(callobj_ins, slot, dslots_ins, v, v_ins);
+        stobj_set_slot(callobj_ins, slot, dslots_ins, v, v_ins);
         return RECORD_CONTINUE;
     }
 
     // This is the hard case: we have a JSStackFrame private, but it's not in
     // range.  During trace execution we may or may not have a JSStackFrame
     // anymore.  Call the standard builtins, which handle that situation.
 
     // Set variables in off-trace-stack call objects by calling standard builtins.
@@ -12861,34 +12871,42 @@ TraceRecorder::setElem(int lval_spindex,
         if (!obj->isDenseArray()) 
             return ARECORD_STOP;
         guardDenseArray(obj_ins, branchExit);
 
         // The index was on the stack and is therefore a LIR float. Force it to
         // be an integer.
         idx_ins = makeNumberInt32(idx_ins);
 
+        if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) {
+            /*
+             * Check for negative values bleeding through on 64-bit machines only,
+             * since we can't allocate large enough arrays for this on 32-bit
+             * machines.
+             */
+            guard(true, lir->ins2ImmI(LIR_gei, idx_ins, 0), mismatchExit);
+        }
+
         if (!js_EnsureDenseArrayCapacity(cx, obj, idx.toInt32()))
             RETURN_STOP_A("couldn't ensure dense array capacity for setelem");
 
         // Grow the array if the index exceeds the capacity.  This happens
         // rarely, eg. less than 1% of the time in SunSpider.
         LIns* capacity_ins =
-            addName(lir->insLoad(LIR_ldi, obj_ins,
-                                 offsetof(JSObject, capacity), ACCSET_OTHER),
+            addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_CAPACITY),
                     "capacity");
         LIns* br = lir->insBranch(LIR_jt, lir->ins2(LIR_ltui, idx_ins, capacity_ins), NULL);
         LIns* args[] = { idx_ins, obj_ins, cx_ins };
         LIns* res_ins = lir->insCall(&js_EnsureDenseArrayCapacity_ci, args);
         guard(false, lir->insEqI_0(res_ins), mismatchExit);
         br->setTarget(lir->ins0(LIR_label));
 
         // Get the address of the element.
         LIns *dslots_ins =
-            addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER), "dslots");
+            addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER), "dslots");
         JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this.
         LIns *addr_ins = lir->ins2(LIR_addp, dslots_ins,
                                    lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3));
 
         // If we are overwriting a hole:
         // - Guard that we don't have any indexed properties along the prototype chain.
         // - Check if the length has changed;  if so, update it to index+1.
         // This happens moderately often, eg. close to 10% of the time in
@@ -13084,17 +13102,17 @@ TraceRecorder::record_JSOP_CALLUPVAR()
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETFCSLOT()
 {
     JSObject& callee = cx->fp()->callee();
     LIns* callee_ins = get(&cx->fp()->calleeValue());
 
-    LIns* upvars_ins = stobj_get_const_private_ptr(callee_ins,
+    LIns* upvars_ins = stobj_get_fslot_private_ptr(callee_ins,
                                                    JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
 
     unsigned index = GET_UINT16(cx->regs->pc);
     LIns *v_ins = unbox_value(callee.getFlatClosureUpvar(index), upvars_ins, index * sizeof(Value),
                               snapshot(BRANCH_EXIT));
     stack(0, v_ins);
     return ARECORD_CONTINUE;
 }
@@ -13344,17 +13362,17 @@ TraceRecorder::record_JSOP_APPLY()
          * We trace dense arrays and arguments objects. The code we generate
          * for apply uses imacros to handle a specific number of arguments.
          */
         if (aobj->isDenseArray()) {
             guardDenseArray(aobj_ins, MISMATCH_EXIT);
             length = aobj->getArrayLength();
             guard(true,
                   lir->ins2ImmI(LIR_eqi,
-                                stobj_get_private_uint32(aobj_ins),
+                                stobj_get_fslot_uint32(aobj_ins, JSObject::JSSLOT_ARRAY_LENGTH),
                                 length),
                   BRANCH_EXIT);
         } else if (aobj->isArguments()) {
             unsigned depth;
             JSStackFrame *afp = guardArguments(aobj, aobj_ins, &depth);
             if (!afp)
                 RETURN_STOP_A("can't reach arguments object's frame");
             length = afp->numActualArgs();
@@ -13721,20 +13739,18 @@ TraceRecorder::denseArrayElement(Value& 
     /*
      * Arrays have both a length and a capacity, but we only need to check
      * |index < capacity|;  in the case where |length < index < capacity|
      * the entries [length..capacity-1] will have already been marked as
      * holes by resizeDenseArrayElements() so we can read them and get
      * the correct value.
      */
     LIns* capacity_ins =
-        addName(lir->insLoad(LIR_ldi, obj_ins,
-                             offsetof(JSObject, capacity), ACCSET_OTHER),
+        addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_CAPACITY),
                 "capacity");
-
     jsuint capacity = obj->getDenseArrayCapacity();
     bool within = (jsuint(idx) < capacity);
     if (!within) {
         /* If not idx < capacity, stay on trace (and read value as undefined). */
         guard(true, lir->ins2(LIR_geui, idx_ins, capacity_ins), branchExit);
 
         CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, snapshot(MISMATCH_EXIT)));
 
@@ -13744,18 +13760,18 @@ TraceRecorder::denseArrayElement(Value& 
         return RECORD_CONTINUE;
     }
 
     /* Guard that index is within capacity. */
     guard(true, lir->ins2(LIR_ltui, idx_ins, capacity_ins), branchExit);
 
     /* Load the value and guard on its type to unbox it. */
     LIns* dslots_ins =
-        addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, slots), ACCSET_OTHER), "dslots");
-    vp = &obj->slots[jsuint(idx)];
+        addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACCSET_OTHER), "dslots");
+    vp = &obj->dslots[jsuint(idx)];
 	JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this.
     addr_ins = lir->ins2(LIR_addp, dslots_ins,
                          lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3));
     v_ins = unbox_value(*vp, addr_ins, 0, branchExit);
 
     /* Don't let the hole value escape. Turn it into an undefined. */
     if (vp->isMagic()) {
         CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, snapshot(MISMATCH_EXIT)));
@@ -14056,17 +14072,17 @@ TraceRecorder::record_JSOP_UINT16()
 {
     stack(0, lir->insImmD(GET_UINT16(cx->regs->pc)));
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_NEWINIT()
 {
-    JSProtoKey key = JSProtoKey(GET_UINT16(cx->regs->pc));
+    JSProtoKey key = JSProtoKey(GET_INT8(cx->regs->pc));
     LIns* proto_ins;
     CHECK_STATUS_A(getClassPrototype(key, proto_ins));
 
     LIns *v_ins;
     if (key == JSProto_Array) {
         LIns *args[] = { lir->insImmI(0), proto_ins, cx_ins };
         v_ins = lir->insCall(&js_NewEmptyArray_ci, args);
     } else {
@@ -15017,17 +15033,17 @@ TraceRecorder::record_JSOP_LAMBDA_FC()
     LIns* closure_ins = lir->insCall(&js_AllocFlatClosure_ci, args);
     guard(false,
           addName(lir->ins2(LIR_eqp, closure_ins, INS_NULL()),
                   "guard(js_AllocFlatClosure)"),
           OOM_EXIT);
 
     if (fun->u.i.nupvars) {
         JSUpvarArray *uva = fun->u.i.script->upvars();
-        LIns* upvars_ins = stobj_get_const_private_ptr(closure_ins,
+        LIns* upvars_ins = stobj_get_fslot_private_ptr(closure_ins,
                                                        JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
 
         for (uint32 i = 0, n = uva->length; i < n; i++) {
             Value v;
             LIns* v_ins = upvar(fun->u.i.script, uva, i, v);
             if (!v_ins)
                 return ARECORD_STOP;
 
@@ -15101,17 +15117,17 @@ TraceRecorder::record_JSOP_ARGSUB()
     RETURN_STOP_A("can't trace JSOP_ARGSUB hard case");
 }
 
 JS_REQUIRES_STACK LIns*
 TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
 {
     // The following implements JSObject::isArgsLengthOverridden on trace.
     // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
-    LIns *len_ins = stobj_get_slot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH);
+    LIns *len_ins = stobj_get_fslot_uint32(argsobj_ins, JSObject::JSSLOT_ARGS_LENGTH);
     LIns *ovr_ins = lir->ins2(LIR_andi, len_ins, INS_CONST(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT));
     guard(true, lir->insEqI_0(ovr_ins), snapshot(BRANCH_EXIT));
     return len_ins;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_ARGCNT()
 {
@@ -15808,17 +15824,17 @@ TraceRecorder::record_JSOP_LENGTH()
     LIns* v_ins;
     if (obj->isArray()) {
         if (obj->isDenseArray()) {
             guardDenseArray(obj_ins, BRANCH_EXIT);
         } else {
             JS_ASSERT(obj->isSlowArray());
             guardClass(obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL);
         }
-        v_ins = lir->ins1(LIR_i2d, stobj_get_private_uint32(obj_ins));
+        v_ins = lir->ins1(LIR_i2d, stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_ARRAY_LENGTH));
     } else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) {
         // Ensure array is a typed array and is the same type as what was written
         guardClass(obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), LOAD_NORMAL);
         v_ins = lir->ins1(LIR_i2d, lir->insLoad(LIR_ldi,
                                                 stobj_get_private(obj_ins),
                                                 js::TypedArray::lengthOffset(),
                                                 ACCSET_OTHER, LOAD_CONST));
     } else {
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1062,17 +1062,17 @@ class TraceRecorder
     JS_REQUIRES_STACK bool known(const Value* p);
     JS_REQUIRES_STACK bool known(JSObject** p);
     /*
      * The dslots of the global object are sometimes reallocated by the
      * interpreter.  This function checks for that condition and re-maps the
      * entries of the tracker accordingly.
      */
     JS_REQUIRES_STACK void checkForGlobalObjectReallocation() {
-        if (global_dslots != globalObj->getSlots())
+        if (global_dslots != globalObj->dslots)
             checkForGlobalObjectReallocationHelper();
     }
     JS_REQUIRES_STACK void checkForGlobalObjectReallocationHelper();
 
     JS_REQUIRES_STACK TypeConsensus selfTypeStability(SlotMap& smap);
     JS_REQUIRES_STACK TypeConsensus peerTypeStability(SlotMap& smap, const void* ip,
                                                       TreeFragment** peer);
 
@@ -1168,30 +1168,31 @@ class TraceRecorder
                                                             JSObject* obj2,
                                                             PropertyCacheEntry* entry,
                                                             PCVal& pcval);
 
     void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot, const Value &v,
                          nanojit::LIns* v_ins);
     void stobj_set_dslot(nanojit::LIns *obj_ins, unsigned slot,
                          nanojit::LIns*& dslots_ins, const Value &v, nanojit::LIns* v_ins);
-    void stobj_set_slot(JSObject *obj, nanojit::LIns* obj_ins, unsigned slot,
+    void stobj_set_slot(nanojit::LIns* obj_ins, unsigned slot,
                         nanojit::LIns*& dslots_ins, const Value &v, nanojit::LIns* v_ins);
+    void set_array_fslot(nanojit::LIns *obj_ins, unsigned slot, uint32 val);
 
-    nanojit::LIns* stobj_get_slot_uint32(nanojit::LIns* obj_ins, unsigned slot);
+    nanojit::LIns* stobj_get_fslot_private_ptr(nanojit::LIns* obj_ins,
+                                               unsigned slot);
+    nanojit::LIns* stobj_get_fslot_uint32(nanojit::LIns* obj_ins, unsigned slot);
+    nanojit::LIns* stobj_set_fslot_uint32(nanojit::LIns* value_ins, nanojit::LIns* obj_ins,
+                                          unsigned slot);
     nanojit::LIns* unbox_slot(JSObject *obj, nanojit::LIns *obj_ins, uint32 slot,
                               VMSideExit *exit);
     nanojit::LIns* stobj_get_parent(nanojit::LIns* obj_ins);
     nanojit::LIns* stobj_get_private(nanojit::LIns* obj_ins);
-    nanojit::LIns* stobj_get_private_uint32(nanojit::LIns* obj_ins);
     nanojit::LIns* stobj_get_proto(nanojit::LIns* obj_ins);
 
-    /* For slots holding private pointers. */
-    nanojit::LIns* stobj_get_const_private_ptr(nanojit::LIns *obj_ins, unsigned slot);
-
     JS_REQUIRES_STACK AbortableRecordingStatus name(Value*& vp, nanojit::LIns*& ins, NameResult& nr);
     JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins,
                                                     uint32 *slotp, nanojit::LIns** v_insp,
                                                     Value* outp);
     JS_REQUIRES_STACK RecordingStatus propTail(JSObject* obj, nanojit::LIns* obj_ins,
                                                JSObject* obj2, PCVal pcval,
                                                uint32 *slotp, nanojit::LIns** v_insp,
                                                Value* outp);
--- a/js/src/jsvalue.h
+++ b/js/src/jsvalue.h
@@ -813,28 +813,16 @@ ObjectOrNullValue(JSObject *obj)
 static JS_ALWAYS_INLINE Value
 PrivateValue(void *ptr)
 {
     Value v;
     v.setPrivate(ptr);
     return v;
 }
 
-static JS_ALWAYS_INLINE void
-ClearValueRange(Value *vec, uintN len, bool useHoles)
-{
-    if (useHoles) {
-        for (uintN i = 0; i < len; i++)
-            vec[i].setMagic(JS_ARRAY_HOLE);
-    } else {
-        for (uintN i = 0; i < len; i++)
-            vec[i].setUndefined();
-    }
-}
-
 /******************************************************************************/
 
 /*
  * As asserted above, js::Value and jsval are layout equivalent. This means:
  *  - an instance of jsval may be reinterpreted as a js::Value and vice versa;
  *  - a pointer to a function taking jsval arguments may be reinterpreted as a
  *    function taking the same arguments, s/jsval/js::Value/, and vice versa;
  *  - a struct containing jsval members may be reinterpreted as a struct with
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -161,19 +161,24 @@ static const JSC::MacroAssembler::Regist
         return branchPtr(cond, Address(fun, offsetof(JSObject, clasp)),
                          ImmPtr(&js_FunctionClass));
     }
 
     /*
      * Finds and returns the address of a known object and slot.
      */
     Address objSlotRef(JSObject *obj, RegisterID reg, uint32 slot) {
-        move(ImmPtr(&obj->slots), reg);
+        if (slot < JS_INITIAL_NSLOTS) {
+            void *vp = &obj->getSlotRef(slot);
+            move(ImmPtr(vp), reg);
+            return Address(reg, 0);
+        }
+        move(ImmPtr(&obj->dslots), reg);
         loadPtr(reg, reg);
-        return Address(reg, slot * sizeof(Value));
+        return Address(reg, (slot - JS_INITIAL_NSLOTS) * sizeof(Value));
     }
 
 #ifdef JS_CPU_X86
     void idiv(RegisterID reg) {
         m_assembler.cdq();
         m_assembler.idivl_r(reg);
     }
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -45,17 +45,16 @@
 #include "Compiler.h"
 #include "StubCalls.h"
 #include "MonoIC.h"
 #include "PolyIC.h"
 #include "Retcon.h"
 #include "assembler/jit/ExecutableAllocator.h"
 #include "assembler/assembler/LinkBuffer.h"
 #include "FrameState-inl.h"
-#include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 #include "InlineFrameAssembler.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::mjit;
 #if defined JS_POLYIC
@@ -1221,23 +1220,20 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_SETLOCAL)
 
           BEGIN_CASE(JSOP_UINT16)
             frame.push(Value(Int32Value((int32_t) GET_UINT16(PC))));
           END_CASE(JSOP_UINT16)
 
           BEGIN_CASE(JSOP_NEWINIT)
           {
-            jsint i = GET_UINT16(PC);
-            uint32 count = GET_UINT16(PC + UINT16_LEN);
-
+            jsint i = GET_INT8(PC);
             JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
 
             prepareStubCall(Uses(0));
-            masm.move(Imm32(count), Registers::ArgReg1);
             if (i == JSProto_Array)
                 stubCall(stubs::NewInitArray);
             else
                 stubCall(stubs::NewInitObject);
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
           }
           END_CASE(JSOP_NEWINIT)
@@ -1437,18 +1433,18 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_GETFCSLOT)
           BEGIN_CASE(JSOP_CALLFCSLOT)
           {
             uintN index = GET_UINT16(PC);
             // JSObject *obj = &fp->argv[-2].toObject();
             RegisterID reg = frame.allocReg();
             masm.loadPayload(Address(JSFrameReg, JSStackFrame::offsetOfCallee(fun)), reg);
             // obj->getFlatClosureUpvars()
-            masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg);
-            Address upvarAddress(reg, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value));
+            Address upvarAddress(reg, offsetof(JSObject, fslots) + 
+                                      JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value));
             masm.loadPrivate(upvarAddress, reg);
             // push ((Value *) reg)[index]
             frame.freeReg(reg);
             frame.push(Address(reg, index * sizeof(Value)));
             if (op == JSOP_CALLFCSLOT)
                 frame.push(NullValue());
           }
           END_CASE(JSOP_CALLFCSLOT)
@@ -2401,17 +2397,17 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
     pic.callReturn = stubcc.call(ic::GetProp);
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslotsLoad);
 #elif defined JS_PUNBOX64
     Label dslotsLoadLabel = masm.label();
 #endif
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
     frame.pop();
 
 #if defined JS_NUNBOX32
     masm.loadTypeTag(slot, shapeReg);
     DBGLABEL(dbgTypeLoad);
@@ -2502,17 +2498,17 @@ mjit::Compiler::jsop_getelem_pic(FrameEn
     pic.callReturn = stubcc.call(ic::GetElem);
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslotsLoad);
 #elif defined JS_PUNBOX64
     Label dslotsLoadLabel = masm.label();
 #endif
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 #if defined JS_NUNBOX32
     masm.loadTypeTag(slot, shapeReg);
     DBGLABEL(dbgTypeLoad);
     masm.loadPayload(slot, objReg);
     DBGLABEL(dbgDataLoad);
@@ -2634,17 +2630,17 @@ mjit::Compiler::jsop_callprop_generic(JS
     frame.pushSynced();
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslotsLoad);
 #elif defined JS_PUNBOX64
     Label dslotsLoadLabel = masm.label();
 #endif
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 
 #if defined JS_NUNBOX32
     masm.loadTypeTag(slot, shapeReg);
     DBGLABEL(dbgTypeLoad);
 
@@ -2792,17 +2788,17 @@ mjit::Compiler::jsop_callprop_obj(JSAtom
     pic.callReturn = stubcc.call(ic::CallProp);
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslotsLoad);
 #elif defined JS_PUNBOX64
     Label dslotsLoadLabel = masm.label();
 #endif
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 
 #if defined JS_NUNBOX32
     masm.loadTypeTag(slot, shapeReg);
     DBGLABEL(dbgTypeLoad);
 
@@ -2961,17 +2957,17 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
     }
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslots);
 #elif defined JS_PUNBOX64
     Label dslotsLoadLabel = masm.label();
 #endif
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
 
     /* Store RHS into object slot. */
     Address slot(objReg, 1 << 24);
 #if defined JS_NUNBOX32
     Label dbgInlineStoreType;
     DBGLABEL(dbgInlineStoreData);
 
     if (vr.isConstant) {
@@ -3505,17 +3501,18 @@ mjit::Compiler::iter(uintN flags)
     masm.loadPtr(Address(T1, offsetof(JSRuntime, threadData.lastNativeIterator)), ioreg);
 #endif
 
     /* Test for NULL. */
     Jump nullIterator = masm.branchTest32(Assembler::Zero, ioreg, ioreg);
     stubcc.linkExit(nullIterator, Uses(1));
 
     /* Get NativeIterator from iter obj. :FIXME: X64, also most of this function */
-    masm.loadPtr(Address(ioreg, offsetof(JSObject, privateData)), nireg);
+    Address privSlot(ioreg, offsetof(JSObject, fslots) + sizeof(Value) * JSSLOT_PRIVATE);
+    masm.loadPtr(privSlot, nireg);
 
     /* Test for active iterator. */
     Address flagsAddr(nireg, offsetof(NativeIterator, flags));
     masm.load32(flagsAddr, T1);
     Jump activeIterator = masm.branchTest32(Assembler::NonZero, T1, Imm32(JSITER_ACTIVE));
     stubcc.linkExit(activeIterator, Uses(1));
 
     /* Compare shape of object with iterator. */
@@ -3689,17 +3686,18 @@ mjit::Compiler::iterEnd()
     frame.unpinReg(reg);
 
     /* Test clasp */
     masm.loadPtr(Address(reg, offsetof(JSObject, clasp)), T1);
     Jump notIterator = masm.branchPtr(Assembler::NotEqual, T1, ImmPtr(&js_IteratorClass));
     stubcc.linkExit(notIterator, Uses(1));
 
     /* Get private from iter obj. :FIXME: X64 */
-    masm.loadPtr(Address(reg, offsetof(JSObject, privateData)), T1);
+    Address privSlot(reg, offsetof(JSObject, fslots) + sizeof(Value) * JSSLOT_PRIVATE);
+    masm.loadPtr(privSlot, T1);
 
     RegisterID T2 = frame.allocReg();
 
     /* Load flags. */
     Address flagAddr(T1, offsetof(NativeIterator, flags));
     masm.loadPtr(flagAddr, T2);
 
     /* Test for (flags == ENUMERATE | ACTIVE). */
@@ -3803,17 +3801,17 @@ mjit::Compiler::jsop_getgname(uint32 ind
     stubcc.leave();
     stubcc.masm.move(Imm32(mics.length()), Registers::ArgReg1);
     mic.stubEntry = stubcc.masm.label();
     mic.call = stubcc.call(ic::GetGlobalName);
 
     /* Garbage value. */
     uint32 slot = 1 << 24;
 
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
     Address address(objReg, slot);
     
     /*
      * On x86_64, the length of the movq instruction used is variable
      * depending on the registers used. For example, 'movq $0x5(%r12), %r12'
      * is one byte larger than 'movq $0x5(%r14), %r14'. This means that
      * the constant '0x5' that we want to write is at a variable position.
      *
@@ -3924,17 +3922,17 @@ mjit::Compiler::jsop_setgname(uint32 ind
         if (!mic.u.name.typeConst)
             typeReg = frame.ownRegForType(fe);
         else
             typeTag = fe->getKnownType();
     } else {
         v = fe->getValue();
     }
 
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
     Address address(objReg, slot);
 
     mic.load = masm.label();
 
 #if defined JS_NUNBOX32
 # if defined JS_CPU_ARM
     DataLabel32 offsetAddress;
     if (mic.u.name.dataConst) {
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1195,31 +1195,36 @@ mjit::Compiler::jsop_setelem()
     /* obj.isDenseArray() */
     RegisterID objReg = frame.copyDataIntoReg(obj);
     Jump guardDense = masm.branchPtr(Assembler::NotEqual,
                                       Address(objReg, offsetof(JSObject, clasp)),
                                       ImmPtr(&js_ArrayClass));
     stubcc.linkExit(guardDense, Uses(3));
 
     /* guard within capacity */
-    Address capacity(objReg, offsetof(JSObject, capacity));
+    Address capacity(objReg, offsetof(JSObject, fslots) +
+                             JSObject::JSSLOT_DENSE_ARRAY_CAPACITY * sizeof(Value));
 
     Jump inRange;
     MaybeRegisterID maybeIdReg;
     if (id->isConstant()) {
-        inRange = masm.branch32(Assembler::LessThanOrEqual, capacity,
+        inRange = masm.branch32(Assembler::LessThanOrEqual,
+                                masm.payloadOf(capacity),
                                 Imm32(id->getValue().toInt32()));
     } else {
         maybeIdReg = frame.copyDataIntoReg(id);
-        inRange = masm.branch32(Assembler::AboveOrEqual, maybeIdReg.reg(), capacity);
+        inRange = masm.branch32(Assembler::AboveOrEqual, maybeIdReg.reg(),
+                                masm.payloadOf(capacity));
     }
     stubcc.linkExit(inRange, Uses(3));
 
-    /* load dslots */
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    /* dslots non-NULL */
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
+    Jump guardSlots = masm.branchTestPtr(Assembler::Zero, objReg, objReg);
+    stubcc.linkExit(guardSlots, Uses(3));
 
     /* guard within capacity */
     if (id->isConstant()) {
         /* guard not a hole */
         Address slot(objReg, id->getValue().toInt32() * sizeof(Value));
 #if defined JS_NUNBOX32
         Jump notHole = masm.branch32(Assembler::Equal, masm.tagOf(slot), ImmType(JSVAL_TYPE_MAGIC));
 #elif defined JS_PUNBOX64
@@ -1288,27 +1293,27 @@ mjit::Compiler::jsop_setelem()
         /* Test for indexed properties in Object.prototype. */
         stubcc.masm.loadPtr(Address(baseReg, offsetof(JSObject, proto)), T1);
         stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, proto)), T1);
         stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, flags)), T1);
         Jump extendedObject = stubcc.masm.branchTest32(Assembler::NonZero, T1, Imm32(JSObject::INDEXED));
         extendedObject.linkTo(syncTarget, &stubcc.masm);
 
         /* Update the array length if needed. Don't worry about overflow. */
-        Address arrayLength(baseReg, offsetof(JSObject, privateData));
-        stubcc.masm.load32(arrayLength, T1);
+        Address arrayLength(baseReg, offsetof(JSObject, fslots[JSObject::JSSLOT_ARRAY_LENGTH]));
+        stubcc.masm.loadPayload(arrayLength, T1);
         Jump underLength = stubcc.masm.branch32(Assembler::LessThan, idReg, T1);
         stubcc.masm.move(idReg, T1);
         stubcc.masm.add32(Imm32(1), T1);
-        stubcc.masm.store32(T1, arrayLength);
+        stubcc.masm.storePayload(T1, arrayLength);
         underLength.linkTo(stubcc.masm.label(), &stubcc.masm);
 
         /* Restore the dslots register if we clobbered it with the object. */
         if (baseReg == objReg)
-            stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+            stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
 
         /* Rejoin OOL path with inline path to do the store itself. */
         Jump jmpHoleExit = stubcc.masm.jump();
         Label lblRejoin = masm.label();
         stubcc.crossJump(jmpHoleExit, lblRejoin);
 
         stubcc.leave();
         stubcc.call(STRICT_VARIANT(stubs::SetElem));
@@ -1344,27 +1349,32 @@ mjit::Compiler::jsop_getelem_dense(Frame
     /* Note: idReg is only valid if id is not a constant. */
     Jump guardDense = masm.branchPtr(Assembler::NotEqual,
                                      Address(objReg, offsetof(JSObject, clasp)),
                                      ImmPtr(&js_ArrayClass));
     stubcc.linkExit(guardDense, Uses(2));
 
     /* Guard within capacity. */
     Jump inRange;
-    Address capacity(objReg, offsetof(JSObject, capacity));
+    Address capacity(objReg, offsetof(JSObject, fslots) +
+                             JSObject::JSSLOT_DENSE_ARRAY_CAPACITY * sizeof(Value));
     if (id->isConstant()) {
-        inRange = masm.branch32(Assembler::LessThanOrEqual, capacity,
+        inRange = masm.branch32(Assembler::LessThanOrEqual,
+                                masm.payloadOf(capacity),
                                 Imm32(id->getValue().toInt32()));
     } else {
-        inRange = masm.branch32(Assembler::AboveOrEqual, idReg.reg(), capacity);
+        inRange = masm.branch32(Assembler::AboveOrEqual, idReg.reg(),
+                                masm.payloadOf(capacity));
     }
     stubcc.linkExit(inRange, Uses(2));
 
-    /* load dslots */
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    /* dslots non-NULL */
+    masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
+    Jump guardSlots = masm.branchTestPtr(Assembler::Zero, objReg, objReg);
+    stubcc.linkExit(guardSlots, Uses(2));
 
     /* guard within capacity */
     if (id->isConstant()) {
         /* guard not a hole */
         Address slot(objReg, id->getValue().toInt32() * sizeof(Value));
 #if defined JS_NUNBOX32
         masm.loadTypeTag(slot, tmpReg);
         Jump notHole = masm.branchPtr(Assembler::Equal, tmpReg, ImmType(JSVAL_TYPE_MAGIC));
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -232,18 +232,17 @@ stubs::NewObject(VMFrame &f, uint32 argc
     JSObject *funobj = &vp[0].toObject();
     JS_ASSERT(funobj->isFunction());
 
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
     if (!funobj->getProperty(cx, id, &vp[1]))
         THROWV(JS_FALSE);
 
     JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL;
-    JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, funobj->getParent(),
-                                                     FINALIZE_OBJECT4);
+    JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, funobj->getParent());
     if (!obj)
         THROWV(JS_FALSE);
 
     vp[1].setObject(*obj);
 
     return JS_TRUE;
 }
 
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -104,16 +104,18 @@ ic::GetGlobalName(VMFrame &f, uint32 ind
 
     mic.u.name.touched = true;
 
     /* Patch shape guard. */
     JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50);
     repatch.repatch(mic.shape, obj->shape());
 
     /* Patch loads. */
+    JS_ASSERT(slot >= JS_INITIAL_NSLOTS);
+    slot -= JS_INITIAL_NSLOTS;
     slot *= sizeof(Value);
     JSC::RepatchBuffer loads(mic.load.executableAddress(), 32, false);
 #if defined JS_CPU_X86
     loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot);
     loads.repatch(mic.load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4);
 #elif defined JS_CPU_ARM
     // mic.load actually points to the LDR instruction which fetches the offset, but 'repatch'
     // knows how to dereference it to find the integer value.
@@ -184,16 +186,18 @@ ic::SetGlobalName(VMFrame &f, uint32 ind
 
     mic.u.name.touched = true;
 
     /* Patch shape guard. */
     JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50);
     repatch.repatch(mic.shape, obj->shape());
 
     /* Patch loads. */
+    JS_ASSERT(slot >= JS_INITIAL_NSLOTS);
+    slot -= JS_INITIAL_NSLOTS;
     slot *= sizeof(Value);
 
     JSC::RepatchBuffer stores(mic.load.executableAddress(), 32, false);
 #if defined JS_CPU_X86
     stores.repatch(mic.load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4);
 
     uint32 dataOffset;
     if (mic.u.name.typeConst)
--- a/js/src/methodjit/NunboxAssembler.h
+++ b/js/src/methodjit/NunboxAssembler.h
@@ -84,24 +84,23 @@ class Assembler : public BaseAssembler
     Address tagOf(Address address) {
         return Address(address.base, address.offset + TAG_OFFSET);
     }
 
     BaseIndex tagOf(BaseIndex address) {
         return BaseIndex(address.base, address.index, address.scale, address.offset + TAG_OFFSET);
     }
 
-    void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess,
-                  RegisterID type, RegisterID data) {
+    void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) {
         JS_ASSERT(type != data);
-        Address address(obj, offsetof(JSObject, fixedSlots) + slot * sizeof(Value));
+        Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value));
         RegisterID activeAddressReg = obj;
-        if (!inlineAccess) {
-            loadPtr(Address(obj, offsetof(JSObject, slots)), clobber);
-            address = Address(clobber, slot * sizeof(Value));
+        if (slot >= JS_INITIAL_NSLOTS) {
+            loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber);
+            address = Address(clobber, (slot - JS_INITIAL_NSLOTS) * sizeof(Value));
             activeAddressReg = clobber;
         }
         if (activeAddressReg == type) {
             loadPayload(address, data);
             loadTypeTag(address, type);
         } else {
             loadTypeTag(address, type);
             loadPayload(address, data);
@@ -200,18 +199,19 @@ class Assembler : public BaseAssembler
         return l;
     }
 
     void loadPrivate(Address privAddr, RegisterID to) {
         loadPtr(privAddr, to);
     }
 
     void loadFunctionPrivate(RegisterID base, RegisterID to) {
-        Address priv(base, offsetof(JSObject, privateData));
-        loadPtr(priv, to);
+        Address privSlot(base, offsetof(JSObject, fslots) +
+                               JSSLOT_PRIVATE * sizeof(Value));
+        loadPtr(privSlot, to);
     }
 
     Jump testNull(Assembler::Condition cond, RegisterID reg) {
         return branch32(cond, reg, ImmTag(JSVAL_TAG_NULL));
     }
 
     Jump testNull(Assembler::Condition cond, Address address) {
         return branch32(cond, tagOf(address), ImmTag(JSVAL_TAG_NULL));
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -248,42 +248,42 @@ class SetPropCompiler : public PICStubCo
                          pic.slowPathStart);
 
         RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
         ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
         MacroAssemblerCodePtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp));
         repatcher.relinkCallerToTrampoline(retPtr, target);
     }
 
-    bool patchInline(const Shape *shape, bool inlineSlot)
+    bool patchInline(const Shape *shape)
     {
         JS_ASSERT(!pic.inlinePathPatched);
         JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress());
 
         PICRepatchBuffer repatcher(pic, pic.fastPathStart);
 
         int32 offset;
-        if (inlineSlot) {
+        if (shape->slot < JS_INITIAL_NSLOTS) {
             JSC::CodeLocationInstruction istr;
             istr = pic.storeBack.instructionAtOffset(dslotsLoadOffset());
             repatcher.repatchLoadPtrToLEA(istr);
 
             // 
             // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
             // To:           | lea fslots, [obj + DSLOTS_OFFSET]
             //
             // Because the offset is wrong, it's necessary to correct it
             // below.
             //
-            int32 diff = int32(offsetof(JSObject, fixedSlots)) -
-                         int32(offsetof(JSObject, slots));
+            int32 diff = int32(offsetof(JSObject, fslots)) -
+                         int32(offsetof(JSObject, dslots));
             JS_ASSERT(diff != 0);
             offset  = (int32(shape->slot) * sizeof(Value)) + diff;
         } else {
-            offset = shape->slot * sizeof(Value);
+            offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
         }
 
         uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset();
         repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffs), obj->shape());
 #if defined JS_NUNBOX32
         repatcher.repatch(pic.storeBack.dataLabel32AtOffset(inlineTypeOffset()), offset + 4);
         repatcher.repatch(pic.storeBack.dataLabel32AtOffset(inlineDataOffset()), offset);
 #elif defined JS_PUNBOX64
@@ -321,17 +321,17 @@ class SetPropCompiler : public PICStubCo
             if (pic.u.vr.u.s.isTypeKnown)
                 masm.storeTypeTag(ImmType(pic.u.vr.u.s.type.knownType), address);
             else
                 masm.storeTypeTag(pic.u.vr.u.s.type.reg, address);
             masm.storePayload(pic.u.vr.u.s.data, address);
         }
     }
 
-    bool generateStub(uint32 initialShape, const Shape *shape, bool adding, bool inlineSlot)
+    bool generateStub(uint32 initialShape, const Shape *shape, bool adding)
     {
         /* Exits to the slow path. */
         Vector<Jump, 8> slowExits(f.cx);
 
         Assembler masm;
 
         // Shape guard.
         if (pic.shapeNeedsRemat()) {
@@ -401,31 +401,38 @@ class SetPropCompiler : public PICStubCo
                 } else {
                     Jump mismatchedFunction =
                         masm.branchPtr(Assembler::NotEqual, pic.u.vr.u.s.data, ImmPtr(funobj));
                     if (!slowExits.append(mismatchedFunction))
                         return false;
                 }
             }
 
-            if (inlineSlot) {
+            if (shape->slot < JS_INITIAL_NSLOTS) {
                 Address address(pic.objReg,
-                                offsetof(JSObject, fixedSlots) + shape->slot * sizeof(Value));
+                                offsetof(JSObject, fslots) + shape->slot * sizeof(Value));
                 emitStore(masm, address);
             } else {
+                /* Check dslots non-zero. */
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg);
+                Jump emptyDslots = masm.branchPtr(Assembler::Equal, pic.shapeReg, ImmPtr(0));
+                if (!slowExits.append(emptyDslots))
+                    return false;
+
                 /* Check capacity. */
-                Address capacity(pic.objReg, offsetof(JSObject, capacity));
+                Address capacity(pic.shapeReg, -ptrdiff_t(sizeof(Value)));
                 masm.load32(masm.payloadOf(capacity), pic.shapeReg);
                 Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg,
                                                   Imm32(shape->slot));
                 if (!slowExits.append(overCapacity))
                     return false;
 
-                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.shapeReg);
-                Address address(pic.shapeReg, shape->slot * sizeof(Value));
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg);
+                Address address(pic.shapeReg,
+                                (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value));
                 emitStore(masm, address);
             }
 
             uint32 newShape = obj->shape();
             JS_ASSERT(newShape != initialShape);
 
             /* Write the object's new shape. */
             masm.storePtr(ImmPtr(shape), Address(pic.objReg, offsetof(JSObject, lastProp)));
@@ -436,20 +443,20 @@ class SetPropCompiler : public PICStubCo
                 Address flags(pic.objReg, offsetof(JSObject, flags));
 
                 /* Use shapeReg to load, bitwise-or, and store flags. */
                 masm.load32(flags, pic.shapeReg);
                 masm.or32(Imm32(JSObject::METHOD_BARRIER), pic.shapeReg);
                 masm.store32(pic.shapeReg, flags);
             }
         } else if (shape->hasDefaultSetter()) {
-            Address address(pic.objReg, offsetof(JSObject, fixedSlots) + shape->slot * sizeof(Value));
-            if (!inlineSlot) {
-                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-                address = Address(pic.objReg, shape->slot * sizeof(Value));
+            Address address(pic.objReg, offsetof(JSObject, fslots) + shape->slot * sizeof(Value));
+            if (shape->slot >= JS_INITIAL_NSLOTS) {
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
+                address = Address(pic.objReg, (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value));
             }
 
             // If the scope is branded, or has a method barrier. It's now necessary
             // to guard that we're not overwriting a function-valued property.
             if (obj->brandedOrHasMethodBarrier()) {
                 masm.loadTypeTag(address, pic.shapeReg);
                 Jump skip = masm.testObject(Assembler::NotEqual, pic.shapeReg);
                 masm.loadPayload(address, pic.shapeReg);
@@ -481,19 +488,19 @@ class SetPropCompiler : public PICStubCo
                 emitStore(masm, addr);
                 skipOver = masm.jump();
             }
 
             escapedFrame.linkTo(masm.label(), &masm);
             {
                 if (shape->setterOp() == SetCallVar)
                     slot += fun->nargs;
-                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
 
-                Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value));
+                Address dslot(pic.objReg, slot * sizeof(Value));
                 emitStore(masm, dslot);
             }
 
             pic.shapeRegHasBaseShape = false;
         }
         Jump done = masm.jump();
 
         JSC::ExecutablePool *ep = getExecPool(masm.size());
@@ -663,17 +670,17 @@ class SetPropCompiler : public PICStubCo
              * Doing this would cause us to walk down this same update path
              * every time a reallocation is needed, however, which will
              * usually be a slowdown even if there *are* other shapes that
              * don't realloc.
              */
             if (obj->numSlots() != slots)
                 return disable("insufficient slot capacity");
 
-            return generateStub(initialShape, shape, true, !obj->hasSlotsArray());
+            return generateStub(initialShape, shape, true);
         }
 
         AutoPropertyDropper dropper(f.cx, holder, prop);
 
         const Shape *shape = (const Shape *) prop;
         if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod())
             return disable("set method on non-method shape");
         if (!shape->writable())
@@ -691,20 +698,20 @@ class SetPropCompiler : public PICStubCo
             }
         }
 
         JS_ASSERT(obj == holder);
         if (!pic.inlinePathPatched &&
             !obj->brandedOrHasMethodBarrier() &&
             shape->hasDefaultSetter() &&
             !obj->isDenseArray()) {
-            return patchInline(shape, !obj->hasSlotsArray());
+            return patchInline(shape);
         } 
 
-        return generateStub(obj->shape(), shape, false, !obj->hasSlotsArray());
+        return generateStub(obj->shape(), shape, false);
     }
 };
 
 class GetPropCompiler : public PICStubCompiler
 {
     JSObject *obj;
     JSAtom *atom;
     void   *stub;
@@ -798,18 +805,19 @@ class GetPropCompiler : public PICStubCo
 
     bool generateArgsLengthStub()
     {
         Assembler masm;
 
         Address clasp(pic.objReg, offsetof(JSObject, clasp));
         Jump notArgs = masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(obj->getClass()));
 
-        masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-        masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)),
+        masm.load32(Address(pic.objReg,
+                            offsetof(JSObject, fslots)
+                            + JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)),
                     pic.objReg);
         masm.move(pic.objReg, pic.shapeReg);
         Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, Imm32(1));
         masm.rshift32(Imm32(JSObject::ARGS_PACKED_BITS_COUNT), pic.objReg);
         
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
@@ -843,17 +851,20 @@ class GetPropCompiler : public PICStubCo
         Assembler masm;
 
         masm.loadPtr(Address(pic.objReg, offsetof(JSObject, clasp)), pic.shapeReg);
         Jump isDense = masm.branchPtr(Assembler::Equal, pic.shapeReg, ImmPtr(&js_ArrayClass));
         Jump notArray = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
                                        ImmPtr(&js_SlowArrayClass));
 
         isDense.linkTo(masm.label(), &masm);
-        masm.load32(Address(pic.objReg, offsetof(JSObject, privateData)), pic.objReg);
+        masm.load32(Address(pic.objReg,
+                            offsetof(JSObject, fslots)
+                            + JSObject::JSSLOT_ARRAY_LENGTH * sizeof(Value)),
+                    pic.objReg);
         Jump oob = masm.branch32(Assembler::Above, pic.objReg, Imm32(JSVAL_INT_MAX));
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
         JSC::ExecutablePool *ep = getExecPool(masm.size());
         if (!ep || !pic.execPools.append(ep)) {
             if (ep)
                 ep->release();
@@ -928,18 +939,17 @@ class GetPropCompiler : public PICStubCo
          * really this is now a MIC, except it won't ever be patched, so we
          * just disable the PIC at the end. :FIXME:? String.prototype probably
          * does not get random shape changes.
          */
         masm.move(ImmPtr(obj), pic.objReg);
         masm.loadShape(pic.objReg, pic.shapeReg);
         Jump shapeMismatch = masm.branch32(Assembler::NotEqual, pic.shapeReg,
                                            Imm32(obj->shape()));
-        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !obj->hasSlotsArray(),
-                      pic.shapeReg, pic.objReg);
+        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg);
 
         Jump done = masm.jump();
 
         JSC::ExecutablePool *ep = getExecPool(masm.size());
         if (!ep || !pic.execPools.append(ep)) {
             if (ep)
                 ep->release();
             js_ReportOutOfMemory(f.cx);
@@ -1008,34 +1018,34 @@ class GetPropCompiler : public PICStubCo
     }
 
     bool patchInline(JSObject *holder, const Shape *shape)
     {
         spew("patch", "inline");
         PICRepatchBuffer repatcher(pic, pic.fastPathStart);
 
         int32 offset;
-        if (!holder->hasSlotsArray()) {
+        if (shape->slot < JS_INITIAL_NSLOTS) {
             JSC::CodeLocationInstruction istr;
             istr = pic.storeBack.instructionAtOffset(dslotsLoad());
             repatcher.repatchLoadPtrToLEA(istr);
 
             // 
             // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
             // To:           | lea fslots, [obj + DSLOTS_OFFSET]
             //
             // Because the offset is wrong, it's necessary to correct it
             // below.
             //
-            int32 diff = int32(offsetof(JSObject, fixedSlots)) -
-                         int32(offsetof(JSObject, slots));
+            int32 diff = int32(offsetof(JSObject, fslots)) -
+                         int32(offsetof(JSObject, dslots));
             JS_ASSERT(diff != 0);
             offset  = (int32(shape->slot) * sizeof(Value)) + diff;
         } else {
-            offset = shape->slot * sizeof(Value);
+            offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
         }
 
         uint32 shapeOffs = pic.shapeGuard + inlineShapeOffset();
         repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffs), obj->shape());
 #if defined JS_NUNBOX32
         repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETPROP_TYPE_LOAD), offset + 4);
         repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETPROP_DATA_LOAD), offset);
 #elif defined JS_PUNBOX64
@@ -1131,18 +1141,17 @@ class GetPropCompiler : public PICStubCo
                 return false;
             pic.u.get.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
         } else {
             JS_ASSERT(holder->isNative()); /* Precondition: already checked. */
             pic.u.get.secondShapeGuard = 0;
         }
 
         /* Load the value out of the object. */
-        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !holder->hasSlotsArray(),
-                      pic.shapeReg, pic.objReg);
+        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg);
         Jump done = masm.jump();
 
         JSC::ExecutablePool *ep = getExecPool(masm.size());
         if (!ep) {
             js_ReportOutOfMemory(f.cx);
             return false;
         }
 
@@ -1344,32 +1353,32 @@ class GetElemCompiler : public PICStubCo
     }
 
     bool patchInline(JSObject *holder, const Shape *shape)
     {
         spew("patch", "inline");
         PICRepatchBuffer repatcher(pic, pic.fastPathStart);
 
         int32 offset;
-        if (!holder->hasSlotsArray()) {
+        if (shape->slot < JS_INITIAL_NSLOTS) {
             JSC::CodeLocationInstruction istr = pic.storeBack.instructionAtOffset(dslotsLoad());
             repatcher.repatchLoadPtrToLEA(istr);
 
             // 
             // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
             // To:           | lea fslots, [obj + DSLOTS_OFFSET]
             //
             // Because the offset is wrong, it's necessary to correct it
             // below.
             //
-            int32 diff = int32(offsetof(JSObject, fixedSlots)) - int32(offsetof(JSObject, slots));
+            int32 diff = int32(offsetof(JSObject, fslots)) - int32(offsetof(JSObject, dslots));
             JS_ASSERT(diff != 0);
             offset  = (int32(shape->slot) * sizeof(Value)) + diff;
         } else {
-            offset = shape->slot * sizeof(Value);
+            offset = (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
         }
         
         uint32 shapeOffset = pic.shapeGuard + inlineShapeOffset();
         repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffset), obj->shape());
         uint32 idOffset = pic.shapeGuard + inlineAtomOffset();
         repatcher.repatch(pic.fastPathStart.dataLabelPtrAtOffset(idOffset), id);
 #if defined JS_NUNBOX32
         repatcher.repatch(pic.storeBack.dataLabel32AtOffset(GETELEM_TYPE_LOAD), offset + 4);
@@ -1488,18 +1497,17 @@ class GetElemCompiler : public PICStubCo
                 return false;
             pic.u.get.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
         } else {
             JS_ASSERT(holder->isNative()); /* Precondition: already checked. */
             pic.u.get.secondShapeGuard = 0;
         }
 
         /* Load the value out of the object. */
-        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, !holder->hasSlotsArray(),
-                      pic.shapeReg, pic.objReg);
+        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg);
         Jump done = masm.jump();
 
         JSC::ExecutablePool *ep = getExecPool(masm.size());
         if (!ep) {
             js_ReportOutOfMemory(f.cx);
             return false;
         }
 
@@ -1678,17 +1686,17 @@ class ScopeNameCompiler : public PICStub
             return false;
         if (!found)
             return disable("scope chain walk terminated early");
 
         Jump finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
         masm.loadShape(pic.objReg, pic.shapeReg);
         Jump finalShape = masm.branch32(Assembler::NotEqual, pic.shapeReg, Imm32(holder->shape()));
 
-        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, false, pic.shapeReg, pic.objReg);
+        masm.loadSlot(pic.objReg, pic.objReg, shape->slot, pic.shapeReg, pic.objReg);
 
         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)
             pj->linkTo(masm.label(), &masm);
         finalNull.linkTo(masm.label(), &masm);
         finalShape.linkTo(masm.label(), &masm);
@@ -1781,21 +1789,21 @@ class ScopeNameCompiler : public PICStub
             masm.loadPayload(addr, pic.objReg);
             masm.loadTypeTag(addr, pic.shapeReg);
             skipOver = masm.jump();
         }
 
         escapedFrame.linkTo(masm.label(), &masm);
 
         {
-            masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
+            masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
 
             if (kind == VAR)
                 slot += fun->nargs;
-            Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value));
+            Address dslot(pic.objReg, slot * sizeof(Value));
 
             /* Safe because type is loaded first. */
             masm.loadValueAsComponents(dslot, pic.shapeReg, pic.objReg);
         }
 
         skipOver.linkTo(masm.label(), &masm);
         Jump done = masm.jump();
 
--- a/js/src/methodjit/PunboxAssembler.h
+++ b/js/src/methodjit/PunboxAssembler.h
@@ -88,23 +88,22 @@ class Assembler : public BaseAssembler
     Address valueOf(Address address) {
         return address;
     }
 
     BaseIndex valueOf(BaseIndex address) {
         return address;
     }
 
-    void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, bool inlineAccess,
-                  RegisterID type, RegisterID data) {
+    void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) {
         JS_ASSERT(type != data);
-        Address address(obj, offsetof(JSObject, fixedSlots) + slot * sizeof(Value));
-        if (!inlineAccess) {
-            loadPtr(Address(obj, offsetof(JSObject, slots)), clobber);
-            address = Address(clobber, slot * sizeof(Value));
+        Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value));
+        if (slot >= JS_INITIAL_NSLOTS) {
+            loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber);
+            address = Address(clobber, (slot - JS_INITIAL_NSLOTS) * sizeof(Value));
         }
         
         loadValueAsComponents(address, type, data);
     }
 
     void loadValue(Address address, RegisterID dst) {
         loadPtr(address, dst);
     }
@@ -242,18 +241,19 @@ class Assembler : public BaseAssembler
     }
 
     void loadPrivate(Address privAddr, RegisterID to) {
         loadPtr(privAddr, to);
         lshiftPtr(Imm32(1), to);
     }
 
     void loadFunctionPrivate(RegisterID base, RegisterID to) {
-        Address priv(base, offsetof(JSObject, privateData));
-        loadPtr(priv, to);
+        Address privSlot(base, offsetof(JSObject, fslots) +
+                               JSSLOT_PRIVATE * sizeof(Value));
+        loadPtr(privSlot, to);
     }
 
     Jump testNull(Assembler::Condition cond, RegisterID reg) {
         return branchPtr(cond, reg, ImmShiftedTag(JSVAL_SHIFTED_TAG_NULL));
     }
 
     Jump testNull(Assembler::Condition cond, Address address) {
         loadValue(address, Registers::ValueReg);
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1427,35 +1427,31 @@ stubs::Neg(VMFrame &f)
     double d;
     if (!ValueToNumber(f.cx, f.regs.sp[-1], &d))
         THROW();
     d = -d;
     f.regs.sp[-1].setNumber(d);
 }
 
 JSObject * JS_FASTCALL
-stubs::NewInitArray(VMFrame &f, uint32 count)
+stubs::NewInitArray(VMFrame &f)
 {
-    JSContext *cx = f.cx;
-    JSFinalizeGCThingKind kind = GuessObjectGCKind(count);
-
-    JSObject *obj = NewArrayWithKind(cx, kind);
-    if (!obj || !obj->ensureSlots(cx, count))
+    JSObject *obj = js_NewArrayObject(f.cx, 0, NULL);
+    if (!obj)
         THROWV(NULL);
     return obj;
 }
 
 JSObject * JS_FASTCALL
-stubs::NewInitObject(VMFrame &f, uint32 count)
+stubs::NewInitObject(VMFrame &f)
 {
     JSContext *cx = f.cx;
-    JSFinalizeGCThingKind kind = GuessObjectGCKind(count);
 
-    JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
-    if (!obj || !obj->ensureSlots(cx, count))
+    JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass); 
+    if (!obj)
         THROWV(NULL);
 
     return obj;
 }
 
 void JS_FASTCALL
 stubs::InitElem(VMFrame &f, uint32 last)
 {
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -44,18 +44,18 @@
 #include "MethodJIT.h"
 
 namespace js {
 namespace mjit {
 namespace stubs {
 
 void JS_FASTCALL This(VMFrame &f);
 void JS_FASTCALL ComputeThis(VMFrame &f);
-JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count);
-JSObject * JS_FASTCALL NewInitObject(VMFrame &f, uint32 count);
+JSObject * JS_FASTCALL NewInitArray(VMFrame &f);
+JSObject * JS_FASTCALL NewInitObject(VMFrame &f);
 JSObject * JS_FASTCALL NewArray(VMFrame &f, uint32 len);
 void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc);
 void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc);
 void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
 void JS_FASTCALL InitElem(VMFrame &f, uint32 last);
 void JS_FASTCALL InitProp(VMFrame &f, JSAtom *atom);
 void JS_FASTCALL InitMethod(VMFrame &f, JSAtom *atom);
 
--- a/js/src/trace-test/tests/basic/testHoleInDenseArray.js
+++ b/js/src/trace-test/tests/basic/testHoleInDenseArray.js
@@ -3,16 +3,15 @@ var s;
 function f(i) {
     if (i > 4) /* side exit when arr[i] changes from bool to undefined (via a hole) */
         assertEq(s, undefined);
     else
         assertEq(s, false);
     return 1;
 }
 
-/* trailing 'true' ensures array has capacity >= 10 */
-var arr = [ false, false, false, false, false, , , , , , true ];
+var arr = [ false, false, false, false, false, , , , ];
 
 for (var i = 0; i < 10; ++i) {
     (s = arr[i]) + f(i);
 }
 
 checkStats({ traceTriggered: 2, sideExitIntoInterpreter: 2 })
--- a/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js
+++ b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js
@@ -1,10 +1,10 @@
 setDebug(true);
 
 function nop(){}
 function caller(obj) {
   assertJit();
   var x = ({ dana : "zuul" });
   return x;
 }
-trap(caller, 23, "x = 'success'; nop()");
+trap(caller, 20, "x = 'success'; nop()");
 assertEq(caller(this), "success");
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -1368,20 +1368,20 @@ DebugCheckWrapperClass(JSObject* obj)
 // a slim wrapper, holding a native in its private slot, or a wrappednative
 // wrapper, holding the XPCWrappedNative in its private slot. A slim wrapper
 // also holds a pointer to its XPCWrappedNativeProto in a reserved slot, we can
 // check that slot for a non-void value to distinguish between the two.
 
 // Only use these macros if IS_WRAPPER_CLASS(obj->getClass()) is true.
 #define IS_WN_WRAPPER_OBJECT(obj)                                             \
     (DebugCheckWrapperClass(obj) &&                                           \
-     obj->getSlot(0).isUndefined())
+     obj->getSlot(JSSLOT_START(obj->getClass())).isUndefined())
 #define IS_SLIM_WRAPPER_OBJECT(obj)                                           \
     (DebugCheckWrapperClass(obj) &&                                           \
-     !obj->getSlot(0).isUndefined())
+     !obj->getSlot(JSSLOT_START(obj->getClass())).isUndefined())
 
 // Use these macros if IS_WRAPPER_CLASS(obj->getClass()) might be false.
 // Avoid calling them if IS_WRAPPER_CLASS(obj->getClass()) can only be
 // true, as we'd do a redundant call to IS_WRAPPER_CLASS.
 #define IS_WN_WRAPPER(obj)                                                    \
     (IS_WRAPPER_CLASS(obj->getClass()) && IS_WN_WRAPPER_OBJECT(obj))
 #define IS_SLIM_WRAPPER(obj)                                                  \
     (IS_WRAPPER_CLASS(obj->getClass()) && IS_SLIM_WRAPPER_OBJECT(obj))
@@ -2279,17 +2279,17 @@ extern JSBool ConstructSlimWrapper(XPCCa
                                    xpcObjectHelper &aHelper,
                                    XPCWrappedNativeScope* xpcScope,
                                    jsval *rval);
 extern JSBool MorphSlimWrapper(JSContext *cx, JSObject *obj);
 
 static inline XPCWrappedNativeProto*
 GetSlimWrapperProto(JSObject *obj)
 {
-  const js::Value &v = obj->getSlot(0);
+  const js::Value &v = obj->getSlot(JSSLOT_START(obj->getClass()));
   return static_cast<XPCWrappedNativeProto*>(v.toPrivate());
 }
 
 
 /***********************************************/
 // XPCWrappedNativeTearOff represents the info needed to make calls to one
 // interface on the underlying native object of a XPCWrappedNative.
 
--- a/js/src/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp
@@ -47,17 +47,17 @@
 
 #include "XPCWrapper.h"
 #include "xpcprivate.h"
 
 namespace xpc {
 
 using namespace js;
 
-static const uint32 JSSLOT_WN_OBJ = 0;
+static const uint32 JSSLOT_WN_OBJ = JSSLOT_PRIVATE;
 
 static JSBool
 holder_get(JSContext *cx, JSObject *holder, jsid id, jsval *vp);
 
 static JSBool
 holder_set(JSContext *cx, JSObject *holder, jsid id, jsval *vp);
 
 static JSBool