Remove JSObject::capacity and JSObject::initializedLength, split JSObject::slots into slots and elements, bug 693221.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 10 Oct 2011 11:41:03 -0700
changeset 82043 838464854ec63ca8663333d94b7593c37ebe2dc6
parent 82042 40f829990c8299591fbc3349fb08e7041c0c0587
child 82044 db8e6de6f68f1e01d1e643c87ecf2c05adf068a4
push idunknown
push userunknown
push dateunknown
bugs693221
milestone10.0a1
Remove JSObject::capacity and JSObject::initializedLength, split JSObject::slots into slots and elements, bug 693221.
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsarrayinlines.h
js/src/jsbuiltins.cpp
js/src/jscntxt.h
js/src/jsdate.cpp
js/src/jsdbgapi.h
js/src/jsemit.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgcinlines.h
js/src/jsgcmark.cpp
js/src/jsgcstats.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsprobes.h
js/src/jsproxy.cpp
js/src/jsregexpinlines.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jstypedarray.cpp
js/src/jstypedarrayinlines.h
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastBuiltins.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
js/src/vm/CallObject-inl.h
js/src/vm/CallObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -43,19 +43,18 @@ BEGIN_TEST(testConservativeGC)
     CHECK(!memcmp(&str2Copy, str2, sizeof(str2Copy)));
 
     return true;
 }
 
 bool checkObjectFields(JSObject *savedCopy, JSObject *obj)
 {
     /* Ignore fields which are unstable across GCs. */
-    CHECK(savedCopy->lastProp == obj->lastProp);
+    CHECK(savedCopy->lastProperty() == obj->lastProperty());
     CHECK(savedCopy->flags == obj->flags);
-    CHECK(savedCopy->initializedLength == obj->initializedLength);
     CHECK(savedCopy->getProto() == obj->getProto());
     CHECK(savedCopy->parent == obj->parent);
     CHECK(savedCopy->privateData == obj->privateData);
     return true;
 }
 
 END_TEST(testConservativeGC)
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -274,17 +274,17 @@ BigIndexToId(JSContext *cx, JSObject *ob
 }
 
 bool
 JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint)
 {
     JS_ASSERT(isDenseArray());
     JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
 
-    uintN cap = numSlots();
+    uintN cap = getDenseArrayCapacity();
     JS_ASSERT(requiredCapacity >= cap);
 
     if (requiredCapacity >= JSObject::NSLOTS_LIMIT)
         return true;
 
     uintN minimalDenseCount = requiredCapacity / 4;
     if (newElementsHint >= minimalDenseCount)
         return false;
@@ -626,23 +626,20 @@ array_length_setter(JSContext *cx, JSObj
         /*
          * Don't reallocate if we're not actually shrinking our slots. If we do
          * shrink slots here, shrink the initialized length too.  This permits us
          * us to disregard length when reading from arrays as long we are within
          * the initialized capacity.
          */
         jsuint oldcap = obj->getDenseArrayCapacity();
         if (oldcap > newlen)
-            obj->shrinkDenseArrayElements(cx, newlen);
+            obj->shrinkElements(cx, newlen);
         jsuint oldinit = obj->getDenseArrayInitializedLength();
-        if (oldinit > newlen) {
+        if (oldinit > newlen)
             obj->setDenseArrayInitializedLength(newlen);
-            if (!cx->typeInferenceEnabled())
-                obj->backfillDenseArrayHoles(cx);
-        }
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
                 obj->setArrayLength(cx, oldlen + 1);
                 return false;
             }
             int deletion = DeleteArrayElement(cx, obj, oldlen, strict);
@@ -1201,17 +1198,17 @@ array_fix(JSContext *cx, JSObject *obj, 
         return false;
 
     *success = true;
     return true;
 }
 
 Class js::ArrayClass = {
     "Array",
-    Class::NON_NATIVE | JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
+    Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     NULL,
@@ -1268,114 +1265,137 @@ Class js::SlowArrayClass = {
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
+bool
+JSObject::allocateSlowArrayElements(JSContext *cx)
+{
+    JS_ASSERT(hasClass(&js::SlowArrayClass));
+    JS_ASSERT(elements == emptyObjectElements);
+
+    ObjectElements *header = cx->new_<ObjectElements>(0);
+    if (!header)
+        return false;
+
+    elements = header->elements();
+    return true;
+}
+
 static bool
 AddLengthProperty(JSContext *cx, JSObject *obj)
 {
+    /*
+     * Add the 'length' property for a newly created or converted slow array,
+     * and update the elements to be an empty array owned by the object.
+     * The shared emptyObjectElements singleton cannot be used for slow arrays,
+     * as accesses to 'length' will use the elements header.
+     */
+
     const jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
     JS_ASSERT(!obj->nativeLookup(cx, lengthId));
 
+    if (!obj->allocateSlowArrayElements(cx))
+        return false;
+
     return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter,
                             SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
 }
 
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 JSBool
 JSObject::makeDenseArraySlow(JSContext *cx)
 {
     JS_ASSERT(isDenseArray());
 
     MarkTypeObjectFlags(cx, this,
                         OBJECT_FLAG_NON_PACKED_ARRAY |
                         OBJECT_FLAG_NON_DENSE_ARRAY);
-    markDenseArrayNotPacked(cx);
-    backfillDenseArrayHoles(cx);
 
     uint32 arrayCapacity = getDenseArrayCapacity();
     uint32 arrayInitialized = getDenseArrayInitializedLength();
 
     /*
-     * Adjust the slots to account for the different layout between dense
-     * arrays and other objects. The slots must be dynamic, and the fixed slots
-     * are now available for newly added properties.
+     * Get an allocated array of the existing elements, evicting from the fixed
+     * slots if necessary.
      */
-    if (denseArrayHasInlineSlots()) {
-        if (!allocSlots(cx, numSlots()))
+    if (!hasDynamicElements()) {
+        if (!growElements(cx, arrayCapacity))
             return false;
-        JS_ASSERT(!denseArrayHasInlineSlots());
+        JS_ASSERT(hasDynamicElements());
     }
 
     /*
      * Save old map now, before calling InitScopeForObject. We'll have to undo
      * on error. This is gross, but a better way is not obvious. Note: the
      * exact contents of the array are not preserved on error.
      */
-    js::Shape *oldMap = lastProp;
+    js::Shape *oldShape = lastProperty();
+    shape_ = NULL;
 
     /* Create a native scope. */
     gc::AllocKind kind = getAllocKind();
-    if (!InitScopeForObject(cx, this, &SlowArrayClass, getProto()->getNewType(cx), kind))
+    if (!InitScopeForObject(cx, this, &SlowArrayClass, getProto()->getNewType(cx), kind)) {
+        setLastPropertyInfallible(oldShape);
         return false;
-
-    capacity = numFixedSlots() + arrayCapacity;
-
-    /*
-     * Root all values in the array during conversion, as SlowArrayClass only
-     * protects up to its slot span.
-     */
-    AutoValueArray autoArray(cx, slots, arrayInitialized);
-
-    /* The initialized length is used iff this is a dense array. */
-    initializedLength = 0;
+    }
+
+    /* Take ownership of the dense elements, reset to an empty dense array. */
+    Value *elems = elements;
+    elements = emptyObjectElements;
+
+    /* Root all values in the array during conversion. */
+    AutoValueArray autoArray(cx, elements, arrayInitialized);
 
     /*
      * Begin with the length property to share more of the property tree.
      * The getter/setter here will directly access the object's private value.
      */
     if (!AddLengthProperty(cx, this)) {
-        setMap(oldMap);
-        capacity = arrayCapacity;
-        initializedLength = arrayInitialized;
+        setLastPropertyInfallible(oldShape);
+        cx->free_(getElementsHeader());
+        elements = elems;
         return false;
     }
 
     /*
      * Create new properties pointing to existing elements. Pack the array to
      * remove holes, so that shapes use successive slots (as for other objects).
      */
     uint32 next = 0;
-    for (uint32 i = 0; i < arrayCapacity; i++) {
+    for (uint32 i = 0; i < arrayInitialized; i++) {
         /* Dense array indexes can always fit in a jsid. */
         jsid id;
         JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id));
 
-        if (slots[i].isMagic(JS_ARRAY_HOLE))
+        if (elems[i].isMagic(JS_ARRAY_HOLE))
             continue;
 
-        setSlot(next, slots[i]);
-
         if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
-            setMap(oldMap);
-            capacity = arrayCapacity;
-            initializedLength = arrayInitialized;
+            JS_ALWAYS_TRUE(setLastProperty(cx, oldShape));
+            cx->free_(getElementsHeader());
+            elements = elems;
             return false;
         }
 
+        setSlot(next, elems[i]);
+
         next++;
     }
 
-    clearSlotRange(next, capacity - next);
+    ObjectElements *oldheader = ObjectElements::fromElements(elems);
+
+    getElementsHeader()->length = oldheader->length;
+    cx->free_(oldheader);
 
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 class ArraySharpDetector
 {
     JSContext *cx;
@@ -1791,23 +1811,20 @@ InitArrayObject(JSContext *cx, JSObject 
     obj->setArrayLength(cx, length);
     if (!vector || !length)
         return true;
 
     if (!InitArrayTypes(cx, obj->getType(cx), vector, length))
         return false;
 
     /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
-    if (!obj->ensureSlots(cx, length))
+    if (!obj->ensureElements(cx, length))
         return false;
 
-    if (cx->typeInferenceEnabled())
-        obj->setDenseArrayInitializedLength(length);
-    else
-        obj->backfillDenseArrayHoles(cx);
+    obj->setDenseArrayInitializedLength(length);
 
     bool hole = false;
     for (jsuint i = 0; i < length; i++) {
         obj->setDenseArrayElement(i, vector[i]);
         hole |= vector[i].isMagic(JS_ARRAY_HOLE);
     }
     if (hole)
         obj->markDenseArrayNotPacked(cx);
@@ -2440,21 +2457,20 @@ NewbornArrayPushImpl(JSContext *cx, JSOb
         jsid id;
         return IndexToId(cx, length, &id) &&
                js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE);
     }
 
     JS_ASSERT(obj->isDenseArray());
     JS_ASSERT(length <= obj->getDenseArrayCapacity());
 
-    if (length == obj->getDenseArrayCapacity() && !obj->ensureSlots(cx, length + 1))
+    if (!obj->ensureElements(cx, length + 1))
         return false;
 
-    if (cx->typeInferenceEnabled())
-        obj->setDenseArrayInitializedLength(length + 1);
+    obj->setDenseArrayInitializedLength(length + 1);
     obj->setDenseArrayLength(length + 1);
     obj->setDenseArrayElementWithType(cx, length, v);
     return true;
 }
 
 JSBool
 js_NewbornArrayPush(JSContext *cx, JSObject *obj, const Value &vp)
 {
@@ -2526,17 +2542,17 @@ array_pop_dense(JSContext *cx, JSObject*
         return JS_TRUE;
     }
     index--;
     if (!GetElement(cx, obj, index, &hole, vp))
         return JS_FALSE;
     if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
         return JS_FALSE;
 
-    if (cx->typeInferenceEnabled() && obj->getDenseArrayInitializedLength() > index)
+    if (obj->getDenseArrayInitializedLength() > index)
         obj->setDenseArrayInitializedLength(index);
     obj->setArrayLength(cx, index);
     return JS_TRUE;
 }
 
 JSBool
 js::array_pop(JSContext *cx, uintN argc, Value *vp)
 {
@@ -2566,20 +2582,17 @@ array_shift(JSContext *cx, uintN argc, V
 
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length < obj->getDenseArrayCapacity() &&
             0 < obj->getDenseArrayInitializedLength()) {
             *vp = obj->getDenseArrayElement(0);
             if (vp->isMagic(JS_ARRAY_HOLE))
                 vp->setUndefined();
             obj->moveDenseArrayElements(0, 1, length);
-            if (cx->typeInferenceEnabled())
-                obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
-            else
-                obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
+            obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
             obj->setArrayLength(cx, length);
             if (!js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(length)))
                 return JS_FALSE;
             return JS_TRUE;
         }
 
         /* Get the to-be-deleted property's value into vp ASAP. */
         JSBool hole;
@@ -2856,18 +2869,16 @@ array_concat(JSContext *cx, uintN argc, 
         length = aobj->getArrayLength();
         const Value *vector = aobj->getDenseArrayElements();
         jsuint initlen = aobj->getDenseArrayInitializedLength();
         nobj = NewDenseCopiedArray(cx, initlen, vector);
         if (!nobj)
             return JS_FALSE;
         TryReuseArrayType(aobj, nobj);
         nobj->setArrayLength(cx, length);
-        if (!aobj->isPackedDenseArray())
-            nobj->markDenseArrayNotPacked(cx);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         p++;
     } else {
         nobj = NewDenseEmptyArray(cx);
         if (!nobj)
@@ -2963,18 +2974,16 @@ array_slice(JSContext *cx, uintN argc, V
         begin = end;
 
     if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
         nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
         if (!nobj)
             return JS_FALSE;
         TryReuseArrayType(obj, nobj);
-        if (!obj->isPackedDenseArray())
-            nobj->markDenseArrayNotPacked(cx);
         vp->setObject(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
     nobj = NewDenseAllocatedArray(cx, end - begin);
     if (!nobj)
         return JS_FALSE;
@@ -3472,29 +3481,24 @@ js_InitArrayClass(JSContext *cx, JSObjec
 namespace js {
 
 template<bool allocateCapacity>
 static JS_ALWAYS_INLINE JSObject *
 NewArray(JSContext *cx, jsuint length, JSObject *proto)
 {
     JS_ASSERT_IF(proto, proto->isArray());
 
-    gc::AllocKind kind = GuessObjectGCKind(length, true);
+    gc::AllocKind kind = GuessArrayGCKind(length);
     JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &ArrayClass, proto, NULL, kind);
     if (!obj)
         return NULL;
 
     obj->setArrayLength(cx, length);
 
-    if (!cx->typeInferenceEnabled()) {
-        obj->markDenseArrayNotPacked(cx);
-        obj->backfillDenseArrayHoles(cx);
-    }
-
-    if (allocateCapacity && !obj->ensureSlots(cx, length))
+    if (allocateCapacity && !obj->ensureElements(cx, length))
         return NULL;
 
     return obj;
 }
 
 JSObject * JS_FASTCALL
 NewDenseEmptyArray(JSContext *cx, JSObject *proto)
 {
@@ -3537,18 +3541,17 @@ JSObject *
 NewDenseCopiedArray(JSContext *cx, uintN length, const Value *vp, JSObject *proto)
 {
     JSObject* obj = NewArray<true>(cx, length, proto);
     if (!obj)
         return NULL;
 
     JS_ASSERT(obj->getDenseArrayCapacity() >= length);
 
-    if (cx->typeInferenceEnabled())
-        obj->setDenseArrayInitializedLength(vp ? length : 0);
+    obj->setDenseArrayInitializedLength(vp ? length : 0);
 
     if (vp)
         obj->copyDenseArrayElements(0, vp, length);
 
     return obj;
 }
 
 #ifdef JS_TRACER
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -50,32 +50,25 @@
 
 /* Small arrays are dense, no matter what. */
 const uintN MIN_SPARSE_INDEX = 256;
 
 inline uint32
 JSObject::getDenseArrayInitializedLength()
 {
     JS_ASSERT(isDenseArray());
-    return initializedLength;
+    return getElementsHeader()->initializedLength;
 }
 
 inline void
 JSObject::setDenseArrayInitializedLength(uint32 length)
 {
     JS_ASSERT(isDenseArray());
     JS_ASSERT(length <= getDenseArrayCapacity());
-    initializedLength = length;
-}
-
-inline bool
-JSObject::isPackedDenseArray()
-{
-    JS_ASSERT(isDenseArray());
-    return flags & PACKED_ARRAY;
+    getElementsHeader()->initializedLength = length;
 }
 
 namespace js {
 /* 2^32-2, inclusive */
 const uint32 MAX_ARRAY_INDEX = 4294967294u;
     
 extern bool
 StringIsArrayIndex(JSLinearString *str, jsuint *indexp);
--- a/js/src/jsarrayinlines.h
+++ b/js/src/jsarrayinlines.h
@@ -41,58 +41,43 @@
 #define jsarrayinlines_h___
 
 #include "jsinferinlines.h"
 
 inline void
 JSObject::markDenseArrayNotPacked(JSContext *cx)
 {
     JS_ASSERT(isDenseArray());
-    if (flags & PACKED_ARRAY) {
-        flags ^= PACKED_ARRAY;
-        MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
-    }
-}
-
-inline void
-JSObject::backfillDenseArrayHoles(JSContext *cx)
-{
-    /* Ensure an array's elements are fully initialized. */
-    ensureDenseArrayInitializedLength(cx, getDenseArrayCapacity(), 0);
+    MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED_ARRAY);
 }
 
 inline void
 JSObject::ensureDenseArrayInitializedLength(JSContext *cx, uint32 index, uint32 extra)
 {
     /*
      * Ensure that the array's contents have been initialized up to index, and
      * mark the elements through 'index + extra' as initialized in preparation
      * for a write.
      */
-    JS_ASSERT(index + extra <= capacity);
-    if (initializedLength < index) {
+    JS_ASSERT(index + extra <= getDenseArrayCapacity());
+    uint32 &initlen = getElementsHeader()->initializedLength;
+    if (initlen < index) {
         markDenseArrayNotPacked(cx);
-        js::ClearValueRange(slots + initializedLength, index - initializedLength, true);
+        js::SetValueRangeToHoles(elements + initlen, index - initlen);
     }
-    if (initializedLength < index + extra)
-        initializedLength = index + extra;
+    if (initlen < index + extra)
+        initlen = index + extra;
 }
 
 inline JSObject::EnsureDenseResult
 JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
 {
     JS_ASSERT(isDenseArray());
 
-    uintN currentCapacity = numSlots();
-
-    /*
-     * Don't take excessive slow paths when inference is disabled, due to
-     * uninitialized slots between initializedLength and capacity.
-     */
-    JS_ASSERT_IF(!cx->typeInferenceEnabled(), currentCapacity == getDenseArrayInitializedLength());
+    uintN currentCapacity = getDenseArrayCapacity();
 
     uintN requiredCapacity;
     if (extra == 1) {
         /* Optimize for the common case. */
         if (index < currentCapacity) {
             ensureDenseArrayInitializedLength(cx, index, 1);
             return ED_OK;
         }
@@ -116,16 +101,16 @@ JSObject::ensureDenseArrayElements(JSCon
     /*
      * We use the extra argument also as a hint about number of non-hole
      * elements to be inserted.
      */
     if (requiredCapacity > MIN_SPARSE_INDEX &&
         willBeSparseDenseArray(requiredCapacity, extra)) {
         return ED_SPARSE;
     }
-    if (!growSlots(cx, requiredCapacity))
+    if (!growElements(cx, requiredCapacity))
         return ED_FAILED;
 
     ensureDenseArrayInitializedLength(cx, index, extra);
     return ED_OK;
 }
 
 #endif /* jsarrayinlines_h___ */
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -201,16 +201,19 @@ js_StringToInt32(JSContext* cx, JSString
 }
 JS_DEFINE_CALLINFO_3(extern, INT32, js_StringToInt32, CONTEXT, STRING, BOOLPTR,
                      0, ACCSET_STORE_ANY)
 
 /* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */
 static inline JSBool
 AddPropertyHelper(JSContext* cx, JSObject* obj, Shape* shape, bool isDefinitelyAtom)
 {
+    JS_NOT_REACHED("FIXME");
+    return true;
+#if 0
     JS_ASSERT(shape->previous() == obj->lastProperty());
 
     if (obj->nativeEmpty()) {
         if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
             return false;
     }
 
     uint32 slot;
@@ -222,16 +225,17 @@ AddPropertyHelper(JSContext* cx, JSObjec
     } else {
         if (!obj->allocSlot(cx, &slot))
             return false;
         JS_ASSERT(slot == shape->slot());
     }
 
     obj->extend(cx, shape, isDefinitelyAtom);
     return true;
+#endif
 }
 
 JSBool FASTCALL
 js_AddProperty(JSContext* cx, JSObject* obj, Shape* shape)
 {
     return AddPropertyHelper(cx, obj, shape, /* isDefinitelyAtom = */false);
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SHAPE, 0, ACCSET_STORE_ANY)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2245,25 +2245,27 @@ enum FrameExpandKind {
     FRAME_EXPAND_ALL = 1
 };
 
 namespace js {
 
 /************************************************************************/
 
 static JS_ALWAYS_INLINE void
-ClearValueRange(Value *vec, uintN len, bool useHoles)
+ClearValueRange(Value *vec, uintN len)
 {
-    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();
-    }
+    for (uintN i = 0; i < len; i++)
+        vec[i].setUndefined();
+}
+
+static JS_ALWAYS_INLINE void
+SetValueRangeToHoles(Value *vec, uintN len)
+{
+    for (uintN i = 0; i < len; i++)
+        vec[i].setMagic(JS_ARRAY_HOLE);
 }
 
 static JS_ALWAYS_INLINE void
 MakeRangeGCSafe(Value *vec, size_t len)
 {
     PodZero(vec, len);
 }
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1223,19 +1223,21 @@ date_now_tn(JSContext*)
 /*
  * Set UTC time to a given time and invalidate cached local time.
  */
 static JSBool
 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
 {
     JS_ASSERT(obj->isDate());
 
-    size_t slotCap = JS_MIN(obj->numSlots(), JSObject::DATE_CLASS_RESERVED_SLOTS);
-    for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START; ind < slotCap; ind++)
+    for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
+         ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
+         ind++) {
         obj->setSlot(ind, UndefinedValue());
+    }
 
     obj->setDateUTCTime(DoubleValue(t));
     if (vp)
         vp->setDouble(t);
     return true;
 }
 
 static void
@@ -1252,22 +1254,16 @@ SetDateToNaN(JSContext *cx, JSObject *ob
  */
 static bool
 FillLocalTimes(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isDate());
 
     jsdouble utcTime = obj->getDateUTCTime().toNumber();
 
-    /* Make sure there are slots to store the cached information. */
-    if (obj->numSlots() < JSObject::DATE_CLASS_RESERVED_SLOTS) {
-        if (!obj->growSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
-            return false;
-    }
-
     if (!JSDOUBLE_IS_FINITE(utcTime)) {
         for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
              ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
              ind++) {
             obj->setSlot(ind, DoubleValue(utcTime));
         }
         return true;
     }
@@ -2701,17 +2697,17 @@ js_InitDateClass(JSContext *cx, JSObject
 
     return dateProto;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &DateClass);
-    if (!obj || !obj->ensureSlots(cx, JSObject::DATE_CLASS_RESERVED_SLOTS))
+    if (!obj)
         return NULL;
     if (!SetUTCTime(cx, obj, msec_time))
         return NULL;
     return obj;
 }
 
 JS_FRIEND_API(JSObject *)
 js_NewDateObject(JSContext* cx, int year, int mon, int mday,
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -465,19 +465,17 @@ JS_GetFunctionTotalSize(JSContext *cx, J
 
 extern JS_PUBLIC_API(size_t)
 JS_GetScriptTotalSize(JSContext *cx, JSScript *script);
 
 /*
  * Return true if obj is a "system" object, that is, one created by
  * JS_NewSystemObject with the system flag set and not JS_NewObject.
  *
- * What "system" means is up to the API client, but it can be used to implement
- * access control policies based on script filenames and their prefixes, using
- * JS_FlagScriptFilenamePrefix and JS_GetTopScriptFilenameFlags.
+ * What "system" means is up to the API client.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_IsSystemObject(JSContext *cx, JSObject *obj);
 
 /*
  * Mark an object as being a system object. This should be called immediately
  * after allocating the object. A system object is an object for which
  * JS_IsSystemObject returns true.
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2024,18 +2024,20 @@ EmitEnterBlock(JSContext *cx, JSParseNod
 
     /*
      * If clones of this block will have any extensible parents, then the clones
      * must get unique shapes; see the comments for js::Bindings::
      * extensibleParents.
      */
     if ((cg->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
         cg->bindings.extensibleParents()) {
-        if (!Shape::setExtensibleParents(cx, &blockObj->lastProp))
+        Shape *shape = Shape::setExtensibleParents(cx, blockObj->lastProperty());
+        if (!shape)
             return false;
+        blockObj->setLastPropertyInfallible(shape);
     }
 
     return true;
 }
 
 static JSBool
 EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op,
                JSObjectBox *box)
@@ -4853,17 +4855,17 @@ JSParseNode::getConstantValue(JSContext 
 
         types::FixArrayType(cx, obj);
         vp->setObject(*obj);
         return true;
       }
       case TOK_RC: {
         JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
 
-        gc::AllocKind kind = GuessObjectGCKind(pn_count, false);
+        gc::AllocKind kind = GuessObjectGCKind(pn_count);
         JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
         if (!obj)
             return false;
 
         for (JSParseNode *pn = pn_head; pn; pn = pn->pn_next) {
             Value value;
             if (!pn->pn_right->getConstantValue(cx, strictChecks, &value))
                 return false;
@@ -7138,17 +7140,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
             return JS_FALSE;
 
         /*
          * Try to construct the shape of the object as we go, so we can emit a
          * JSOP_NEWOBJECT with the final shape instead.
          */
         JSObject *obj = NULL;
         if (!cg->hasSharps() && cg->compileAndGo()) {
-            gc::AllocKind kind = GuessObjectGCKind(pn->pn_count, false);
+            gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
             obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
             if (!obj)
                 return JS_FALSE;
         }
 
         uintN methodInits = 0, slowMethodInits = 0;
         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
             /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -115,19 +115,17 @@ JS_NewObjectWithUniqueType(JSContext *cx
     if (!obj || !obj->setSingletonType(cx))
         return NULL;
     return obj;
 }
 
 JS_FRIEND_API(uint32)
 JS_ObjectCountDynamicSlots(JSObject *obj)
 {
-    if (obj->hasSlotsArray())
-        return obj->numDynamicSlots(obj->numSlots());
-    return 0;
+    return (obj->slotsAndStructSize() - obj->structSize()) / sizeof(Value);
 }
 
 JS_FRIEND_API(JSPrincipals *)
 JS_GetCompartmentPrincipals(JSCompartment *compartment)
 {
     return compartment->principals;
 }
 
@@ -167,25 +165,16 @@ AutoSwitchCompartment::AutoSwitchCompart
 }
 
 AutoSwitchCompartment::~AutoSwitchCompartment()
 {
     /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */
     cx->compartment = oldCompartment;
 }
 
-#ifdef DEBUG
-JS_FRIEND_API(void)
-js::CheckReservedSlot(const JSObject *obj, size_t slot)
-{
-    JS_ASSERT(slot < obj->numSlots());
-    JS_ASSERT(slot < JSSLOT_FREE(obj->getClass()));
-}
-#endif
-
 /*
  * The below code is for temporary telemetry use. It can be removed when
  * sufficient data has been harvested.
  */
 
 extern size_t sE4XObjectsCreated;
 
 JS_FRIEND_API(size_t)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -182,24 +182,26 @@ struct BaseShape {
     js::Class   *clasp;
 };
 
 struct Shape {
     BaseShape   *base;
 };
 
 struct Object {
-    Shape       *lastProp;
+    Shape       *shape;
+    TypeObject  *type;
     uint32      flags;
-    void        *_2;
     JSObject    *parent;
     void        *privateData;
-    jsuword     _3;
     js::Value   *slots;
-    TypeObject  *type;
+    js::Value   *_1;
+#if JS_BITS_PER_WORD == 32
+    js::Value   *_2;
+#endif
 
     static const uint32 FIXED_SLOTS_SHIFT = 27;
 
     js::Value &slotRef(size_t slot) const {
         size_t nfixed = flags >> FIXED_SLOTS_SHIFT;
         if (slot < nfixed)
             return ((Value *)((jsuword) this + sizeof(shadow::Object)))[slot];
         return slots[slot - nfixed];
@@ -219,17 +221,17 @@ extern JS_FRIEND_DATA(js::Class) OuterWi
 extern JS_FRIEND_DATA(js::Class) ObjectProxyClass;
 extern JS_FRIEND_DATA(js::Class) QNameClass;
 extern JS_FRIEND_DATA(js::Class) ScriptClass;
 extern JS_FRIEND_DATA(js::Class) XMLClass;
 
 inline js::Class *
 GetObjectClass(const JSObject *obj)
 {
-    return reinterpret_cast<const shadow::Object*>(obj)->lastProp->base->clasp;
+    return reinterpret_cast<const shadow::Object*>(obj)->shape->base->clasp;
 }
 
 inline JSClass *
 GetObjectJSClass(const JSObject *obj)
 {
     return js::Jsvalify(GetObjectClass(obj));
 }
 
@@ -246,37 +248,31 @@ GetObjectProto(const JSObject *obj)
 }
 
 inline void *
 GetObjectPrivate(const JSObject *obj)
 {
     return reinterpret_cast<const shadow::Object*>(obj)->privateData;
 }
 
-#ifdef DEBUG
-extern JS_FRIEND_API(void) CheckReservedSlot(const JSObject *obj, size_t slot);
-#else
-inline void CheckReservedSlot(const JSObject *obj, size_t slot) {}
-#endif
-
 /*
  * Get a slot that is both reserved for object's clasp *and* is fixed (fits
  * within the maximum capacity for the object's fixed slots).
  */
 inline const Value &
 GetReservedSlot(const JSObject *obj, size_t slot)
 {
-    CheckReservedSlot(obj, slot);
+    JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
     return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
 }
 
 inline void
 SetReservedSlot(JSObject *obj, size_t slot, const Value &value)
 {
-    CheckReservedSlot(obj, slot);
+    JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
     reinterpret_cast<shadow::Object *>(obj)->slotRef(slot) = value;
 }
 
 static inline js::PropertyOp
 CastAsJSPropertyOp(JSObject *object)
 {
     return JS_DATA_TO_FUNC_PTR(js::PropertyOp, object);
 }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -211,17 +211,17 @@ ArgumentsObject::create(JSContext *cx, u
     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. */
     obj->init(cx, type, proto->getParent(), NULL, false);
-    obj->setMap(emptyArgumentsShape);
+    obj->setInitialPropertyInfallible(emptyArgumentsShape);
 
     ArgumentsObject *argsobj = obj->asArguments();
 
     JS_ASSERT(UINT32_MAX > (uint64(argc) << PACKED_BITS_COUNT));
     argsobj->setInitialLength(argc);
 
     argsobj->setCalleeAndData(callee, data);
 
@@ -764,17 +764,17 @@ NewDeclEnvObject(JSContext *cx, StackFra
     types::TypeObject *type = cx->compartment->getEmptyType(cx);
     if (!type)
         return NULL;
 
     EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx);
     if (!emptyDeclEnvShape)
         return NULL;
     envobj->init(cx, type, &fp->scopeChain(), fp, false);
-    envobj->setMap(emptyDeclEnvShape);
+    envobj->setInitialPropertyInfallible(emptyDeclEnvShape);
 
     return envobj;
 }
 
 namespace js {
 
 CallObject *
 CreateFunCallObject(JSContext *cx, StackFrame *fp)
@@ -863,18 +863,16 @@ js_PutCallObject(StackFrame *fp)
         callobj.copyValues(0, NULL, bindings.countVars(), fp->slots());
     } else {
         JSFunction *fun = fp->fun();
         JS_ASSERT(fun == callobj.getCalleeFunction());
         JS_ASSERT(script == fun->script());
 
         uintN n = bindings.countArgsAndVars();
         if (n > 0) {
-            JS_ASSERT(CallObject::RESERVED_SLOTS + n <= callobj.numSlots());
-
             uint32 nvars = bindings.countVars();
             uint32 nargs = bindings.countArgs();
             JS_ASSERT(fun->nargs == nargs);
             JS_ASSERT(nvars + nargs == n);
 
             JSScript *script = fun->script();
             if (script->usesEval
 #ifdef JS_METHODJIT
@@ -1904,22 +1902,19 @@ JSObject::initBoundFunction(JSContext *c
     flags |= JSObject::BOUND_FUNCTION;
     setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
     setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
     if (argslen != 0) {
         /* Convert to a dictionary to increase the slot span and cover the arguments. */
         if (!toDictionaryMode(cx))
             return false;
         JS_ASSERT(slotSpan() == JSSLOT_FREE(&FunctionClass));
-        lastProp->base()->setSlotSpan(slotSpan() + argslen);
-
-        if (!ensureInstanceReservedSlots(cx, argslen))
+        if (!setSlotSpan(cx, slotSpan() + argslen))
             return false;
 
-        JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS);
         copySlotRange(FUN_CLASS_RESERVED_SLOTS, args, argslen);
     }
     return true;
 }
 
 inline JSObject *
 JSObject::getBoundFunctionTarget() const
 {
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -63,32 +63,43 @@ GetGCThingTraceKind(const void *thing)
     return MapAllocToTraceKind(cell->getAllocKind());
 }
 
 /* Capacity for slotsToThingKind */
 const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
 
 /* Get the best kind to use when making an object with the given slot count. */
 static inline AllocKind
-GetGCObjectKind(size_t numSlots, bool isArray = false)
+GetGCObjectKind(size_t numSlots)
 {
     extern AllocKind slotsToThingKind[];
 
-    if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) {
-        /*
-         * If the object will definitely want more than the maximum number of
-         * fixed slots, use zero fixed slots for arrays and the maximum for
-         * other objects. Arrays do not use their fixed slots anymore when
-         * they have a slots array, while other objects will continue to do so.
-         */
-        return isArray ? FINALIZE_OBJECT0 : FINALIZE_OBJECT16;
-    }
+    if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
+        return FINALIZE_OBJECT16;
     return slotsToThingKind[numSlots];
 }
 
+/* As for GetGCObjectKind, but for dense array allocation. */
+static inline AllocKind
+GetGCArrayKind(size_t numSlots)
+{
+    extern AllocKind slotsToThingKind[];
+
+    /*
+     * Dense arrays can use their fixed slots to hold their elements array
+     * (less two Values worth of ObjectElements header), but if more than the
+     * maximum number of fixed slots is needed then the fixed slots will be
+     * unused.
+     */
+    JS_STATIC_ASSERT(sizeof(ObjectElements) == 2 * sizeof(Value));
+    if (numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
+        return FINALIZE_OBJECT2;
+    return slotsToThingKind[numSlots + 2];
+}
+
 static inline AllocKind
 GetGCObjectFixedSlotsKind(size_t numFixedSlots)
 {
     extern AllocKind slotsToThingKind[];
 
     JS_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
     return slotsToThingKind[numFixedSlots];
 }
@@ -383,20 +394,19 @@ js_NewGCExternalString(JSContext *cx)
     return NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
                                         sizeof(JSExternalString));
 }
 
 inline JSFunction*
 js_NewGCFunction(JSContext *cx)
 {
     JSFunction *fun = NewGCThing<JSFunction>(cx, js::gc::FINALIZE_FUNCTION, sizeof(JSFunction));
-    if (fun) {
-        fun->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS;
-        fun->lastProp = NULL; /* Stops fun from being scanned until initializated. */
-    }
+    if (fun)
+        fun->earlyInit(JSObject::FUN_CLASS_RESERVED_SLOTS);
+
     return fun;
 }
 
 inline JSScript *
 js_NewGCScript(JSContext *cx)
 {
     return NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
 }
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -729,22 +729,21 @@ ScanObject(GCMarker *gcmarker, JSObject 
             } else {
                 clasp->trace(gcmarker, obj);
             }
         } else {
             clasp->trace(gcmarker, obj);
         }
     }
 
-    js::Shape *shape = obj->lastProp;
+    js::Shape *shape = obj->lastProperty();
     PushMarkStack(gcmarker, shape);
 
     if (shape->isNative()) {
         uint32 nslots = obj->slotSpan();
-        JS_ASSERT(obj->slotSpan() <= obj->numSlots());
         if (nslots > LARGE_OBJECT_CHUNK_SIZE) {
             if (gcmarker->largeStack.push(LargeMarkItem(obj)))
                 return;
         }
 
         obj->scanSlots(gcmarker);
     }
 }
@@ -792,17 +791,16 @@ MarkChildren(JSTracer *trc, JSObject *ob
 
     Class *clasp = obj->getClass();
     if (clasp->trace)
         clasp->trace(trc, obj);
 
     MarkShape(trc, obj->lastProperty(), "shape");
 
     if (obj->lastProperty()->isNative()) {
-        JS_ASSERT(obj->slotSpan() <= obj->numSlots());
         uint32 nslots = obj->slotSpan();
         for (uint32 i = 0; i < nslots; i++) {
             JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
             MarkValueRaw(trc, obj->nativeGetSlot(i));
         }
     }
 }
 
@@ -1050,17 +1048,16 @@ JS_TraceChildren(JSTracer *trc, void *th
 
 inline void
 JSObject::scanSlots(GCMarker *gcmarker)
 {
     /*
      * Scan the fixed slots and the dynamic slots separately, to avoid
      * branching inside nativeGetSlot().
      */
-    JS_ASSERT(slotSpan() <= numSlots());
     unsigned i, nslots = slotSpan();
     if (slots) {
         unsigned nfixed = numFixedSlots();
         if (nslots > nfixed) {
             Value *vp = fixedSlots();
             for (i = 0; i < nfixed; i++, vp++)
                 ScanValue(gcmarker, *vp);
             vp = slots;
--- a/js/src/jsgcstats.cpp
+++ b/js/src/jsgcstats.cpp
@@ -40,16 +40,17 @@
 #include "jscntxt.h"
 #include "jsgcstats.h"
 #include "jsgc.h"
 #include "jsxml.h"
 #include "jsbuiltins.h"
 #include "jscompartment.h"
 
 #include "jsgcinlines.h"
+#include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 #define UL(x)       ((unsigned long)(x))
 #define PERCENT(x,y)  (100.0 * (double) (x) / (double) (y))
 
 namespace js {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2438,17 +2438,17 @@ TypeCompartment::fixArrayType(JSContext 
     }
 
     /*
      * If the array is of homogenous type, pick a type object which will be
      * shared with all other singleton/JSON arrays of the same type.
      * If the array is heterogenous, keep the existing type object, which has
      * unknown properties.
      */
-    JS_ASSERT(obj->isPackedDenseArray());
+    JS_ASSERT(obj->isDenseArray());
 
     unsigned len = obj->getDenseArrayInitializedLength();
     if (len == 0)
         return;
 
     Type type = GetValueTypeForTable(cx, obj->getDenseArrayElement(0));
 
     for (unsigned i = 1; i < len; i++) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4769,17 +4769,17 @@ BEGIN_CASE(JSOP_NEWINIT)
     jsint i = regs.pc[1];
 
     JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
     JSObject *obj;
 
     if (i == JSProto_Array) {
         obj = NewDenseEmptyArray(cx);
     } else {
-        gc::AllocKind kind = GuessObjectGCKind(0, false);
+        gc::AllocKind kind = GuessObjectGCKind(0);
         obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
     }
 
     if (!obj)
         goto error;
 
     TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, (JSProtoKey) i);
     if (!type)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -413,17 +413,17 @@ NewIteratorObject(JSContext *cx, uintN f
         if (!type)
             return NULL;
 
         EmptyShape *emptyEnumeratorShape = EmptyShape::getEmptyEnumeratorShape(cx);
         if (!emptyEnumeratorShape)
             return NULL;
 
         obj->init(cx, type, NULL, NULL, false);
-        obj->setMap(emptyEnumeratorShape);
+        obj->setInitialPropertyInfallible(emptyEnumeratorShape);
         return obj;
     }
 
     return NewBuiltinClassInstance(cx, &IteratorClass);
 }
 
 NativeIterator *
 NativeIterator::allocateIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2709,25 +2709,16 @@ obj_preventExtensions(JSContext *cx, uin
     vp->setObject(*obj);
     if (!obj->isExtensible())
         return true;
 
     AutoIdVector props(cx);
     return obj->preventExtensions(cx, &props);
 }
 
-size_t
-JSObject::sizeOfSlotsArray(JSUsableSizeFun usf)
-{
-    if (!hasSlotsArray())
-        return 0;
-    size_t usable = usf((void *)slots);
-    return usable ? usable : numSlots() * sizeof(js::Value);
-}
-
 bool
 JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
 {
     assertSameCompartment(cx, this);
     JS_ASSERT(it == SEAL || it == FREEZE);
 
     AutoIdVector props(cx);
     if (isExtensible()) {
@@ -2999,17 +2990,17 @@ CreateThisForFunctionWithType(JSContext 
         /*
          * Make an object with the type's associated finalize kind and shape,
          * which reflects any properties that will definitely be added to the
          * object before it is read from.
          */
         gc::AllocKind kind = type->newScript->allocKind;
         JSObject *res = NewObjectWithType(cx, type, parent, kind);
         if (res)
-            res->setMap((Shape *) type->newScript->shape);
+            JS_ALWAYS_TRUE(res->setLastProperty(cx, (Shape *) type->newScript->shape));
         return res;
     }
 
     gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
     return NewObjectWithType(cx, type, parent, kind);
 }
 
 JSObject *
@@ -3074,17 +3065,17 @@ js_Object_tn(JSContext* cx, JSObject* pr
 JS_DEFINE_TRCINFO_1(js_Object,
     (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0,
          nanojit::ACCSET_STORE_ANY)))
 
 JSObject* FASTCALL
 js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj)
 {
     if (!baseobj) {
-        gc::AllocKind kind = GuessObjectGCKind(0, false);
+        gc::AllocKind kind = GuessObjectGCKind(0);
         return NewObjectWithClassProto(cx, &ObjectClass, proto, kind);
     }
 
     /* :FIXME: bug 637856 new Objects do not have the right type when created on trace. */
     TypeObject *type = proto->getNewType(cx);
     if (!type)
         return NULL;
 
@@ -3480,17 +3471,17 @@ js_NewWithObject(JSContext *cx, JSObject
     StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
 
     obj->init(cx, type, parent, priv, false);
 
     EmptyShape *emptyWithShape = EmptyShape::getEmptyWithShape(cx);
     if (!emptyWithShape)
         return NULL;
 
-    obj->setMap(emptyWithShape);
+    obj->setInitialPropertyInfallible(emptyWithShape);
     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
 
     AutoObjectRooter tvr(cx, obj);
     JSObject *thisp = proto->thisObject(cx);
     if (!thisp)
         return NULL;
 
     assertSameCompartment(cx, obj, thisp);
@@ -3514,17 +3505,17 @@ js_NewBlockObject(JSContext *cx)
     if (!type)
         return NULL;
 
     EmptyShape *emptyBlockShape = EmptyShape::getEmptyBlockShape(cx);
     if (!emptyBlockShape)
         return NULL;
 
     blockObj->init(cx, type, NULL, NULL, false);
-    blockObj->setMap(emptyBlockShape);
+    blockObj->setInitialPropertyInfallible(emptyBlockShape);
 
     return blockObj;
 }
 
 JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp)
 {
     JS_ASSERT(proto->isStaticBlock());
@@ -3541,18 +3532,17 @@ js_CloneBlockObject(JSContext *cx, JSObj
         return NULL;
 
     StackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
 
     /* The caller sets parent on its own. */
     if (!clone->initClonedBlock(cx, type, priv))
         return NULL;
 
-    if (!clone->ensureInstanceReservedSlots(cx, count + 1))
-        return NULL;
+    JS_ASSERT(clone->slotSpan() >= count + 1);
 
     clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
 
     JS_ASSERT(clone->isClonedBlock());
     return clone;
 }
 
 JS_REQUIRES_STACK JSBool
@@ -3560,17 +3550,17 @@ js_PutBlockObject(JSContext *cx, JSBool 
 {
     StackFrame *const fp = cx->fp();
     JSObject *obj = &fp->scopeChain();
     JS_ASSERT(obj->isClonedBlock());
     JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
 
     /* Block objects should have all reserved slots allocated early. */
     uintN count = OBJ_BLOCK_COUNT(cx, obj);
-    JS_ASSERT(obj->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count);
+    JS_ASSERT(obj->slotSpan() >= JSSLOT_BLOCK_DEPTH + 1 + count);
 
     /* The block and its locals must be on the current stack for GC safety. */
     uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
     JS_ASSERT(depth <= size_t(cx->regs().sp - fp->base()));
     JS_ASSERT(count <= size_t(cx->regs().sp - fp->base() - depth));
 
     /* See comments in CheckDestructuring from jsparse.cpp. */
     JS_ASSERT(count >= 1);
@@ -3648,18 +3638,16 @@ JSObject::defineBlockVariable(JSContext 
     uint32 slot = JSSLOT_FREE(&BlockClass) + index;
     const Shape *shape = addProperty(cx, id,
                                      block_getProperty, block_setProperty,
                                      slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
                                      Shape::HAS_SHORTID, index,
                                      /* allowDictionary = */ false);
     if (!shape)
         return NULL;
-    if (slot >= numSlots() && !growSlots(cx, slot + 1))
-        return NULL;
     return shape;
 }
 
 JSBool
 JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict)
 {
     if (JS_UNLIKELY(watched())) {
         id = js_CheckForStringIndex(id);
@@ -3717,29 +3705,28 @@ JS_CopyPropertiesFrom(JSContext *cx, JSO
     }
     return true;
 }
 
 static bool
 CopySlots(JSContext *cx, JSObject *from, JSObject *to)
 {
     JS_ASSERT(!from->isNative() && !to->isNative());
-    size_t nslots = from->numSlots();
-    if (to->ensureSlots(cx, nslots))
-        return false;
+    JS_ASSERT(from->getClass() == to->getClass());
 
     size_t n = 0;
     if (from->isWrapper() &&
         (Wrapper::wrapperHandler(from)->flags() & Wrapper::CROSS_COMPARTMENT)) {
         to->setSlot(0, from->getSlot(0));
         to->setSlot(1, from->getSlot(1));
         n = 2;
     }
 
-    for (; n < nslots; ++n) {
+    size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
+    for (; n < span; ++n) {
         Value v = from->getSlot(n);
         if (!cx->compartment->wrap(cx, &v))
             return false;
         to->setSlot(n, v);
     }
     return true;
 }
 
@@ -3821,42 +3808,47 @@ JSObject::ReserveForTradeGuts(JSContext 
      */
     if (a->isNative() && !a->generateOwnShape(cx))
         return false;
     if (b->isNative() && !b->generateOwnShape(cx))
         return false;
 
     /* The avals/bvals vectors hold all original values from the objects. */
 
-    unsigned acap = a->numSlots();
-    unsigned bcap = b->numSlots();
-
-    if (!reserved.avals.reserve(acap))
+    if (!reserved.avals.reserve(a->slotSpan()))
+        return false;
+    if (!reserved.bvals.reserve(b->slotSpan()))
         return false;
-    if (!reserved.bvals.reserve(bcap))
-        return false;
+
+    JS_ASSERT(a->elements == emptyObjectElements);
+    JS_ASSERT(b->elements == emptyObjectElements);
 
     /*
      * The newaslots/newbslots arrays hold any dynamic slots for the objects
      * if they do not have enough fixed slots to accomodate the slots in the
      * other object.
      */
 
     unsigned afixed = a->numFixedSlots();
     unsigned bfixed = b->numFixedSlots();
 
-    if (afixed < bcap) {
-        reserved.newaslots = (Value *) cx->malloc_(sizeof(Value) * (bcap - afixed));
+    unsigned adynamic = dynamicSlotsCount(afixed, b->slotSpan());
+    unsigned bdynamic = dynamicSlotsCount(bfixed, a->slotSpan());
+
+    if (adynamic) {
+        reserved.newaslots = (Value *) cx->malloc_(sizeof(Value) * adynamic);
         if (!reserved.newaslots)
             return false;
-    }
-    if (bfixed < acap) {
-        reserved.newbslots = (Value *) cx->malloc_(sizeof(Value) * (acap - bfixed));
+        Debug_SetValueRangeToCrashOnTouch(reserved.newaslots, adynamic);
+    }
+    if (bdynamic) {
+        reserved.newbslots = (Value *) cx->malloc_(sizeof(Value) * bdynamic);
         if (!reserved.newbslots)
             return false;
+        Debug_SetValueRangeToCrashOnTouch(reserved.newbslots, bdynamic);
     }
 
     return true;
 }
 
 void
 JSObject::updateFixedSlots(uintN fixed)
 {
@@ -3902,50 +3894,46 @@ JSObject::TradeGuts(JSContext *cx, JSObj
         memcpy(b, tmp, size);
     } else {
         /*
          * If the objects are of differing sizes, use the space we reserved
          * earlier to save the slots from each object and then copy them into
          * the new layout for the other object.
          */
 
-        unsigned acap = a->numSlots();
-        unsigned bcap = b->numSlots();
+        unsigned acap = a->slotSpan();
+        unsigned bcap = b->slotSpan();
 
         for (size_t i = 0; i < acap; i++)
             reserved.avals.infallibleAppend(a->getSlot(i));
 
         for (size_t i = 0; i < bcap; i++)
             reserved.bvals.infallibleAppend(b->getSlot(i));
 
         /* Done with the dynamic slots. */
-        if (a->hasSlotsArray())
+        if (a->hasDynamicSlots())
             cx->free_(a->slots);
-        if (b->hasSlotsArray())
+        if (b->hasDynamicSlots())
             cx->free_(b->slots);
 
         unsigned afixed = a->numFixedSlots();
         unsigned bfixed = b->numFixedSlots();
 
         JSObject tmp;
         memcpy(&tmp, a, sizeof tmp);
         memcpy(a, b, sizeof tmp);
         memcpy(b, &tmp, sizeof tmp);
 
         a->updateFixedSlots(afixed);
         a->slots = reserved.newaslots;
-        a->capacity = Max(afixed, bcap);
         a->copySlotRange(0, reserved.bvals.begin(), bcap);
-        a->clearSlotRange(bcap, a->capacity - bcap);
 
         b->updateFixedSlots(bfixed);
         b->slots = reserved.newbslots;
-        b->capacity = Max(bfixed, acap);
         b->copySlotRange(0, reserved.avals.begin(), acap);
-        b->clearSlotRange(acap, b->capacity - acap);
 
         /* Make sure the destructor for reserved doesn't free the slots. */
         reserved.newaslots = NULL;
         reserved.newbslots = NULL;
     }
 }
 
 /*
@@ -4152,19 +4140,16 @@ DefineStandardSlot(JSContext *cx, JSObje
         /*
          * Initializing an actual standard class on a global object. If the
          * property is not yet present, force it into a new one bound to a
          * reserved slot. Otherwise, go through the normal property path.
          */
         JS_ASSERT(obj->isGlobal());
         JS_ASSERT(obj->isNative());
 
-        if (!obj->ensureClassReservedSlots(cx))
-            return false;
-
         const Shape *shape = obj->nativeLookup(cx, id);
         if (!shape) {
             uint32 slot = 2 * JSProto_LIMIT + key;
             if (!js_SetReservedSlot(cx, obj, slot, v))
                 return false;
             if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
                 return false;
             AddTypePropertyId(cx, obj, id, v);
@@ -4423,255 +4408,387 @@ js_InitClass(JSContext *cx, JSObject *ob
         return NULL;
     }
 
     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
                                          ps, fs, static_ps, static_fs, ctorp);
 }
 
 void
-JSObject::clearSlotRange(size_t start, size_t length)
-{
-    JS_ASSERT(start + length <= capacity);
-    if (isDenseArray()) {
-        ClearValueRange(slots + start, length, true);
+JSObject::copySlotRange(size_t start, const Value *vector, size_t length)
+{
+    JS_ASSERT(!isDenseArray());
+    JS_ASSERT(slotInRange(start + length, /* sentinelAllowed = */ true));
+    size_t fixed = numFixedSlots();
+    if (start < fixed) {
+        if (start + length < fixed) {
+            memcpy(fixedSlots() + start, vector, length * sizeof(Value));
+        } else {
+            size_t localCopy = fixed - start;
+            memcpy(fixedSlots() + start, vector, localCopy * sizeof(Value));
+            memcpy(slots, vector + localCopy, (length - localCopy) * sizeof(Value));
+        }
     } else {
-        size_t fixed = numFixedSlots();
-        if (start < fixed) {
-            if (start + length < fixed) {
-                ClearValueRange(fixedSlots() + start, length, false);
-            } else {
-                size_t localClear = fixed - start;
-                ClearValueRange(fixedSlots() + start, localClear, false);
-                ClearValueRange(slots, length - localClear, false);
-            }
+        memcpy(slots + start - fixed, vector, length * sizeof(Value));
+    }
+}
+
+inline void
+JSObject::clearSlotRange(size_t start, size_t length)
+{
+    JS_ASSERT(!isDenseArray());
+    JS_ASSERT(slotInRange(start + length, /* sentinelAllowed = */ true));
+    size_t fixed = numFixedSlots();
+    if (start < fixed) {
+        if (start + length < fixed) {
+            ClearValueRange(fixedSlots() + start, length);
         } else {
-            ClearValueRange(slots + start - fixed, length, false);
+            size_t localClear = fixed - start;
+            ClearValueRange(fixedSlots() + start, localClear);
+            ClearValueRange(slots, length - localClear);
         }
-    }
-}
-
-void
-JSObject::copySlotRange(size_t start, const Value *vector, size_t length)
-{
-    JS_ASSERT(start + length <= capacity);
-    if (isDenseArray()) {
-        memcpy(slots + start, vector, length * sizeof(Value));
     } else {
-        size_t fixed = numFixedSlots();
-        if (start < fixed) {
-            if (start + length < fixed) {
-                memcpy(fixedSlots() + start, vector, length * sizeof(Value));
-            } else {
-                size_t localCopy = fixed - start;
-                memcpy(fixedSlots() + start, vector, localCopy * sizeof(Value));
-                memcpy(slots, vector + localCopy, (length - localCopy) * sizeof(Value));
-            }
+        ClearValueRange(slots + start - fixed, length);
+    }
+}
+
+inline void
+JSObject::invalidateSlotRange(size_t start, size_t length)
+{
+#ifdef DEBUG
+    JS_ASSERT(!isDenseArray());
+
+    size_t fixed = numFixedSlots();
+    size_t numSlots = fixed + numDynamicSlots();
+
+    /* Make sure we don't invalidate slots in memory that has been released. */
+    JS_ASSERT(start <= numSlots);
+    if (start + length > numSlots)
+        length = numSlots - start;
+
+    JS_ASSERT(slotInRange(start + length, /* sentinelAllowed = */ true));
+    if (start < fixed) {
+        if (start + length < fixed) {
+            Debug_SetValueRangeToCrashOnTouch(fixedSlots() + start, length);
         } else {
-            memcpy(slots + start - fixed, vector, length * sizeof(Value));
+            size_t localClear = fixed - start;
+            Debug_SetValueRangeToCrashOnTouch(fixedSlots() + start, localClear);
+            Debug_SetValueRangeToCrashOnTouch(slots, length - localClear);
         }
-    }
+    } else {
+        Debug_SetValueRangeToCrashOnTouch(slots + start - fixed, length);
+    }
+#endif /* DEBUG */
+}
+
+inline void
+JSObject::updateSlotsForSpan(size_t oldSpan, size_t newSpan)
+{
+    JS_ASSERT(oldSpan != newSpan);
+
+    if (newSpan == oldSpan + 1) {
+        setSlot(oldSpan, UndefinedValue());
+        return;
+    }
+
+    if (oldSpan < newSpan)
+        clearSlotRange(oldSpan, newSpan - oldSpan);
+    else
+        invalidateSlotRange(newSpan, oldSpan - newSpan);
 }
 
 bool
-JSObject::allocSlots(JSContext *cx, size_t newcap)
-{
-    JS_ASSERT(newcap >= numSlots() && !hasSlotsArray());
-    size_t oldSize = slotsAndStructSize();
-
-    /*
-     * If we are allocating slots for an object whose type is always created
-     * by calling 'new' on a particular script, bump the GC kind for that
-     * type to give these objects a larger number of fixed slots when future
-     * objects are constructed.
-     */
-    if (!hasLazyType() && type()->newScript) {
-        gc::AllocKind kind = type()->newScript->allocKind;
-        unsigned newScriptSlots = gc::GetGCKindSlots(kind);
-        if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) {
-            JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind,
-                                              type()->newScript->shape);
-            if (!obj)
-                return false;
-
-            type()->newScript->allocKind = kind;
-            type()->newScript->shape = obj->lastProperty();
-            type()->markStateChange(cx);
-        }
-    }
-
-    if (newcap > NSLOTS_LIMIT) {
-        if (!JS_ON_TRACE(cx))
-            js_ReportAllocationOverflow(cx);
+JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape)
+{
+    JS_ASSERT(isNewborn());
+    JS_ASSERT(shape->compartment() == compartment());
+    JS_ASSERT(!shape->inDictionary());
+
+    size_t span = shape->slotSpan();
+
+    if (!span) {
+        shape_ = const_cast<js::Shape *>(shape);
+        return true;
+    }
+
+    size_t count = dynamicSlotsCount(numFixedSlots(), span);
+    if (count && !growSlots(cx, 0, count))
         return false;
-    }
-
-    uint32 allocCount = numDynamicSlots(newcap);
-
-    Value *tmpslots = (Value*) cx->malloc_(allocCount * sizeof(Value));
-    if (!tmpslots)
-        return false;  /* Leave slots at inline buffer. */
-    slots = tmpslots;
-    capacity = newcap;
-
-    if (isDenseArray()) {
-        /* Copy over anything from the inline buffer. */
-        memcpy(slots, fixedSlots(), getDenseArrayInitializedLength() * sizeof(Value));
-        if (!cx->typeInferenceEnabled())
-            backfillDenseArrayHoles(cx);
-    } else {
-        /* Clear out the new slots without copying. */
-        ClearValueRange(slots, allocCount, false);
-    }
-
-    Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
+
+    shape_ = const_cast<js::Shape *>(shape);
+    updateSlotsForSpan(0, span);
+    return true;
+}
+
+void
+JSObject::setInitialPropertyInfallible(const js::Shape *shape)
+{
+    JS_ASSERT(isNewborn());
+    JS_ASSERT(shape->compartment() == compartment());
+    JS_ASSERT(!shape->inDictionary());
+    JS_ASSERT(dynamicSlotsCount(numFixedSlots(), shape->slotSpan()) == 0);
+
+    shape_ = const_cast<js::Shape *>(shape);
+
+    size_t span = shape->slotSpan();
+    if (span)
+        updateSlotsForSpan(0, span);
+}
+
+bool
+JSObject::setLastProperty(JSContext *cx, const js::Shape *shape)
+{
+    JS_ASSERT(!isNewborn());
+    JS_ASSERT(!inDictionaryMode());
+    JS_ASSERT(!shape->inDictionary());
+    JS_ASSERT(shape->compartment() == compartment());
+
+    size_t oldSpan = lastProperty()->slotSpan();
+    size_t newSpan = shape->slotSpan();
+
+    if (oldSpan == newSpan) {
+        shape_ = const_cast<js::Shape *>(shape);
+        return true;
+    }
+
+    size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan);
+    size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan);
+    if (!changeSlots(cx, oldCount, newCount))
+        return false;
+
+    shape_ = const_cast<js::Shape *>(shape);
+    updateSlotsForSpan(oldSpan, newSpan);
 
     return true;
 }
 
 bool
-JSObject::growSlots(JSContext *cx, size_t newcap)
-{
+JSObject::setSlotSpan(JSContext *cx, uint32 span)
+{
+    JS_ASSERT(inDictionaryMode());
+    js::BaseShape *base = lastProperty()->base();
+
+    size_t oldSpan = base->slotSpan();
+
+    if (oldSpan == span)
+        return true;
+
+    size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan);
+    size_t newCount = dynamicSlotsCount(numFixedSlots(), span);
+    if (!changeSlots(cx, oldCount, newCount))
+        return false;
+
+    base->setSlotSpan(span);
+    updateSlotsForSpan(oldSpan, span);
+
+    return true;
+}
+
+bool
+JSObject::growSlots(JSContext *cx, uint32 oldCount, uint32 newCount)
+{
+    JS_ASSERT(newCount > oldCount);
+    JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
+    JS_ASSERT_IF(!isNewborn(), !isDenseArray());
+
     /*
      * Slots are only allocated for call objects when new properties are
      * added to them, which can only happen while the call is still on the
      * stack (and an eval, DEFFUN, etc. happens). We thus do not need to
      * worry about updating any active outer function args/vars.
      */
-    JS_ASSERT_IF(isCall(), asCall().maybeStackFrame() != NULL);
+    JS_ASSERT_IF(!isNewborn() && isCall(), asCall().maybeStackFrame() != NULL);
+
+    /* Don't let nslots get close to wrapping around uint32. */
+    if (newCount >= NSLOTS_LIMIT) {
+        JS_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    size_t oldSize = Probes::objectResizeActive() ? slotsAndStructSize() : 0;
+    size_t newSize = oldSize + (newCount - oldCount) * sizeof(Value);
+
+    if (!oldCount) {
+        slots = (Value *) cx->malloc_(newCount * sizeof(Value));
+        if (!slots)
+            return false;
+        Debug_SetValueRangeToCrashOnTouch(slots, newCount);
+        if (Probes::objectResizeActive())
+            Probes::resizeObject(cx, this, oldSize, newSize);
+        return true;
+    }
+
+    Value *tmpslots = (Value*) cx->realloc_(slots, oldCount * sizeof(Value),
+                                            newCount * sizeof(Value));
+    if (!tmpslots)
+        return false;  /* Leave slots at its old size. */
+
+    bool changed = slots != tmpslots;
+    slots = tmpslots;
+
+    Debug_SetValueRangeToCrashOnTouch(slots + oldCount, newCount - oldCount);
+
+    /* Changes in the slots of global objects can trigger recompilation. */
+    if (changed && isGlobal())
+        types::MarkObjectStateChange(cx, this);
+
+    if (Probes::objectResizeActive())
+        Probes::resizeObject(cx, this, oldSize, newSize);
+
+    return true;
+}
+
+void
+JSObject::shrinkSlots(JSContext *cx, uint32 oldCount, uint32 newCount)
+{
+    JS_ASSERT(newCount < oldCount);
+    JS_ASSERT(!isDenseArray());
 
     /*
-     * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
+     * Refuse to shrink slots for call objects. This only happens in a very
+     * obscure situation (deleting names introduced by a direct 'eval') and
+     * allowing the slots pointer to change may require updating pointers in
+     * the function's active args/vars information.
+     */
+    if (isCall())
+        return;
+
+    size_t oldSize = Probes::objectResizeActive() ? slotsAndStructSize() : 0;
+    size_t newSize = oldSize - (oldCount - newCount) * sizeof(Value);
+
+    if (newCount == 0) {
+        cx->free_(slots);
+        slots = NULL;
+        if (Probes::objectResizeActive())
+            Probes::resizeObject(cx, this, oldSize, newSize);
+        return;
+    }
+
+    JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
+
+    Value *tmpslots = (Value*) cx->realloc_(slots, newCount * sizeof(Value));
+    if (!tmpslots)
+        return;  /* Leave slots at its old size. */
+
+    bool changed = slots != tmpslots;
+    slots = tmpslots;
+
+    /* Watch for changes in global object slots, as for growSlots. */
+    if (changed && isGlobal())
+        types::MarkObjectStateChange(cx, this);
+
+    if (Probes::objectResizeActive())
+        Probes::resizeObject(cx, this, oldSize, newSize);
+}
+
+bool
+JSObject::growElements(JSContext *cx, uintN newcap)
+{
+    JS_ASSERT(isDenseArray());
+
+    /*
+     * When an object with CAPACITY_DOUBLING_MAX or fewer elements 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.
      */
     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);
-
-    size_t oldSize = slotsAndStructSize();
+    uint32 oldcap = getDenseArrayCapacity();
+    JS_ASSERT(oldcap <= newcap);
+
+    size_t oldSize = Probes::objectResizeActive() ? slotsAndStructSize() : 0;
 
     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;
 
     /* Don't let nslots get close to wrapping around uint32. */
-    if (actualCapacity >= NSLOTS_LIMIT) {
+    if (actualCapacity >= NSLOTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
-    /* If nothing was allocated yet, treat it as initial allocation. */
-    if (!hasSlotsArray())
-        return allocSlots(cx, actualCapacity);
-
-    uint32 oldAllocCount = numDynamicSlots(oldcap);
-    uint32 allocCount = numDynamicSlots(actualCapacity);
-
-    Value *tmpslots = (Value*) cx->realloc_(slots, oldAllocCount * sizeof(Value),
-                                            allocCount * sizeof(Value));
-    if (!tmpslots)
-        return false;    /* Leave dslots as its old size. */
-
-    bool changed = slots != tmpslots;
-    slots = tmpslots;
-    capacity = actualCapacity;
-
-    if (isDenseArray()) {
-        if (!cx->typeInferenceEnabled())
-            backfillDenseArrayHoles(cx);
+    JS_STATIC_ASSERT(sizeof(ObjectElements) == 2 * sizeof(js::Value));
+
+    uint32 initlen = getDenseArrayInitializedLength();
+
+    ObjectElements *tmpheader;
+    if (hasDynamicElements()) {
+        tmpheader = (ObjectElements *)
+            cx->realloc_(getElementsHeader(), (oldcap + 2) * sizeof(Value),
+                         (actualCapacity + 2) * sizeof(Value));
+        if (!tmpheader)
+            return false;  /* Leave elements as its old size. */
     } else {
-        /* Clear the new slots we added. */
-        ClearValueRange(slots + oldAllocCount, allocCount - oldAllocCount, false);
-    }
-
-    if (changed && isGlobal())
-        types::MarkObjectStateChange(cx, this);
-
-    Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
+        tmpheader = (ObjectElements *) cx->malloc_((actualCapacity + 2) * sizeof(Value));
+        if (!tmpheader)
+            return false;  /* Ditto. */
+        memcpy(tmpheader, getElementsHeader(), (initlen + 2) * sizeof(Value));
+    }
+
+    tmpheader->capacity = actualCapacity;
+    elements = tmpheader->elements();
+
+    Debug_SetValueRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
+
+    if (Probes::objectResizeActive())
+        Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
 
     return true;
 }
 
 void
-JSObject::shrinkSlots(JSContext *cx, size_t newcap)
-{
-    /*
-     * Refuse to shrink slots for call objects. This only happens in a very
-     * obscure situation (deleting names introduced by a direct 'eval') and
-     * allowing the slots pointer to change may require updating pointers in
-     * the function's active args/vars information.
-     */
-    if (isCall())
-        return;
-
-    uint32 oldcap = numSlots();
+JSObject::shrinkElements(JSContext *cx, uintN newcap)
+{
+    JS_ASSERT(isDenseArray());
+
+    uint32 oldcap = getDenseArrayCapacity();
     JS_ASSERT(newcap <= oldcap);
-    JS_ASSERT(newcap >= slotSpan());
-
-    size_t oldSize = slotsAndStructSize();
-
-    if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) {
-        /*
-         * We won't shrink the slots any more. Clear excess entries. When
-         * shrinking dense arrays, make sure to update the initialized length
-         * afterwards.
-         */
-        if (!isDenseArray())
-            clearSlotRange(newcap, oldcap - newcap);
+
+    size_t oldSize = Probes::objectResizeActive() ? slotsAndStructSize() : 0;
+
+    /* Don't shrink elements below the minimum capacity. */
+    if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
         return;
-    }
-
-    uint32 fill = newcap;
-    newcap = Max(newcap, size_t(SLOT_CAPACITY_MIN));
-    newcap = Max(newcap, numFixedSlots());
-
-    Value *tmpslots = (Value*) cx->realloc_(slots, newcap * sizeof(Value));
-    if (!tmpslots)
-        return;  /* Leave slots at its old size. */
-
-    bool changed = slots != tmpslots;
-    slots = tmpslots;
-    capacity = newcap;
-
-    if (fill < newcap) {
-        /*
-         * Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN
-         * or numFixedSlots(). As above, caller must update the initialized
-         * length for dense arrays.
-         */
-        if (!isDenseArray())
-            clearSlotRange(fill, newcap - fill);
-    }
-
-    if (changed && isGlobal())
-        types::MarkObjectStateChange(cx, this);
-
-    Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
-}
-
+
+    newcap = Max(newcap, SLOT_CAPACITY_MIN);
+
+    JS_STATIC_ASSERT(sizeof(ObjectElements) == 2 * sizeof(js::Value));
+
+    ObjectElements *tmpheader = (ObjectElements *)
+        cx->realloc_(getElementsHeader(), (newcap + 2) * sizeof(Value));
+    if (!tmpheader)
+        return;  /* Leave elements at its old size. */
+
+    tmpheader->capacity = newcap;
+    elements = tmpheader->elements();
+
+    if (Probes::objectResizeActive())
+        Probes::resizeObject(cx, this, oldSize, slotsAndStructSize());
+}
+
+#ifdef DEBUG
 bool
-JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
-{
-    JS_ASSERT_IF(isNative(),
-                 isBlock() || isCall() || (isFunction() && isBoundFunction()));
-
-    uintN nslots = JSSLOT_FREE(getClass()) + nreserved;
-    return nslots <= numSlots() || allocSlots(cx, nslots);
-}
+JSObject::slotInRange(uintN slot, bool sentinelAllowed) const
+{
+    size_t capacity = numFixedSlots() + numDynamicSlots();
+    if (sentinelAllowed)
+        return slot <= capacity;
+    return slot < capacity;
+}
+#endif /* DEBUG */
 
 static JSObject *
 js_InitNullClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(0);
     return NULL;
 }
 
@@ -4688,21 +4805,16 @@ static JSObjectOp lazy_prototype_init[JS
 namespace js {
 
 bool
 SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
 {
     JS_ASSERT_IF(!checkForCycles, obj != proto);
     JS_ASSERT(obj->isExtensible());
 
-    if (obj->isNative()) {
-        if (!obj->ensureClassReservedSlots(cx))
-            return false;
-    }
-
     if (proto && proto->isXML()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
         return false;
     }
 
     /*
      * Regenerate shapes for all of the scopes along the old prototype chain,
      * in case any entries were filled by looking up through obj.
@@ -4937,17 +5049,17 @@ JSObject::allocSlot(JSContext *cx, uint3
     uint32 slot = slotSpan();
     JS_ASSERT(slot >= JSSLOT_FREE(getClass()));
 
     /*
      * If this object is in dictionary mode, try to pull a free slot from the
      * property table's slot-number freelist.
      */
     if (inDictionaryMode()) {
-        PropertyTable &table = lastProp->table();
+        PropertyTable &table = lastProperty()->table();
         uint32 last = table.freelist;
         if (last != SHAPE_INVALID_SLOT) {
 #ifdef DEBUG
             JS_ASSERT(last < slot);
             uint32 next = getSlot(last).toPrivateUint32();
             JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
 #endif
 
@@ -4955,36 +5067,31 @@ JSObject::allocSlot(JSContext *cx, uint3
 
             const Value &vref = getSlot(last);
             table.freelist = vref.toPrivateUint32();
             setSlot(last, UndefinedValue());
             return true;
         }
     }
 
-    if (slot >= numSlots() && !growSlots(cx, slot + 1))
+    *slotp = slot;
+
+    if (inDictionaryMode() && !setSlotSpan(cx, slot + 1))
         return false;
 
-    /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
-    JS_ASSERT(getSlot(slot).isUndefined());
-    *slotp = slot;
-
-    if (inDictionaryMode())
-        lastProp->base()->setSlotSpan(slot + 1);
-
     return true;
 }
 
 void
 JSObject::freeSlot(JSContext *cx, uint32 slot)
 {
     JS_ASSERT(slot < slotSpan());
 
     if (inDictionaryMode()) {
-        uint32 &last = lastProp->table().freelist;
+        uint32 &last = lastProperty()->table().freelist;
 
         /* Can't afford to check the whole freelist, but let's check the head. */
         JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
 
         /*
          * Place all freed slots other than reserved slots (bug 595230) on the
          * dictionary's free list.
          */
@@ -5065,30 +5172,24 @@ js_AddNativeProperty(JSContext *cx, JSOb
     /*
      * Purge the property cache of now-shadowed id in obj's scope chain. Do
      * this optimistically (assuming no failure below) before locking obj, so
      * we can lock the shadowed scope.
      */
     if (!js_PurgeScopeChain(cx, obj, id))
         return NULL;
 
-    if (!obj->ensureClassReservedSlots(cx))
-        return NULL;
-
     return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
 }
 
 const Shape *
 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
                              const Shape *shape, uintN attrs, uintN mask,
                              PropertyOp getter, StrictPropertyOp setter)
 {
-    if (!obj->ensureClassReservedSlots(cx))
-        return NULL;
-
     /*
      * Check for freezing an object with shape-memoized methods here, on a
      * shape-by-shape basis.
      */
     if ((attrs & JSPROP_READONLY) && shape->isMethod()) {
         Value v = ObjectValue(*obj->nativeGetMethod(shape));
 
         shape = obj->methodReadBarrier(cx, *shape, &v);
@@ -5217,20 +5318,16 @@ DefineNativeProperty(JSContext *cx, JSOb
          * Type information for normal native properties should reflect the
          * initial value of the property.
          */
         AddTypePropertyId(cx, obj, id, value);
         if (attrs & JSPROP_READONLY)
             MarkTypePropertyConfigured(cx, obj, id);
     }
 
-    /* Get obj's own scope if it has one, or create a new one for obj. */
-    if (!obj->ensureClassReservedSlots(cx))
-        return NULL;
-
     /*
      * Make a local copy of value, in case a method barrier needs to update the
      * value to define, and just so addProperty can mutate its inout parameter.
      */
     Value valueCopy = value;
     bool adding = false;
 
     if (!shape) {
@@ -6187,20 +6284,16 @@ js_SetPropertyHelper(JSContext *cx, JSOb
 
         /*
          * Purge the property cache of now-shadowed id in obj's scope chain.
          * Do this early, before locking obj to avoid nesting locks.
          */
         if (!js_PurgeScopeChain(cx, obj, id))
             return JS_FALSE;
 
-        /* Find or make a property descriptor with the right heritage. */
-        if (!obj->ensureClassReservedSlots(cx))
-            return JS_FALSE;
-
         /*
          * Check for Object class here to avoid defining a method on a class
          * with magic resolve, addProperty, getProperty, etc. hooks.
          */
         if ((defineHow & DNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
             JS_ASSERT(IsFunctionObject(*vp));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
@@ -6938,52 +7031,47 @@ js_ClearNative(JSContext *cx, JSObject *
 bool
 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp)
 {
     if (!obj->isNative()) {
         vp->setUndefined();
         return true;
     }
 
-    if (slot < obj->numSlots())
-        *vp = obj->getSlot(slot);
-    else
-        vp->setUndefined();
+    JS_ASSERT(slot < JSSLOT_FREE(obj->getClass()));
+    *vp = obj->getSlot(slot);
     return true;
 }
 
 bool
 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v)
 {
     if (!obj->isNative())
         return true;
 
     Class *clasp = obj->getClass();
-
-    if (slot >= obj->numSlots()) {
-        uint32 nslots = JSSLOT_FREE(clasp);
-        JS_ASSERT(slot < nslots);
-        if (!obj->allocSlots(cx, nslots))
-            return false;
-    }
+    JS_ASSERT(slot < JSSLOT_FREE(clasp));
 
     obj->setSlot(slot, v);
     GCPoke(cx, NullValue());
     return true;
 }
 
 GlobalObject *
 JSObject::getGlobal() const
 {
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
     return obj->asGlobal();
 }
 
+static ObjectElements emptyObjectHeader(0);
+Value *js::emptyObjectElements = (Value *) (jsuword(&emptyObjectHeader) + sizeof(ObjectElements));
+
 JSBool
 js_ReportGetterOnlyAssignment(JSContext *cx)
 {
     return JS_ReportErrorFlagsAndNumber(cx,
                                         JSREPORT_WARNING | JSREPORT_STRICT |
                                         JSREPORT_STRICT_MODE_ERROR,
                                         js_GetErrorMessage, NULL,
                                         JSMSG_GETTER_ONLY);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -346,103 +346,171 @@ extern Class XMLFilterClass;
 
 class ArgumentsObject;
 class GlobalObject;
 class NormalArgumentsObject;
 class NumberObject;
 class StrictArgumentsObject;
 class StringObject;
 
+/*
+ * Header structure for object element arrays. This structure is immediately
+ * followed by an array of elements, with the elements member in an object
+ * pointing to the beginning of that array (the end of this structure).
+ * See below for usage of this structure.
+ */
+struct ObjectElements
+{
+    /* Number of allocated slots. */
+    uint32 capacity;
+
+    /*
+     * Number of initialized elements. This is <= the capacity, and for arrays
+     * is <= the length. Memory for elements above the initialized length is
+     * uninitialized, but values between the initialized length and the proper
+     * length are conceptually holes.
+     */
+    uint32 initializedLength;
+
+    /* 'length' property of array objects, unused for other objects. */
+    uint32 length;
+
+    /* :XXX: bug 586842 store state about sparse slots. */
+    uint32 unused;
+
+    ObjectElements(uint32 capacity)
+        : capacity(capacity), initializedLength(0), length(0)
+    {}
+
+    Value * elements() { return (Value *)(jsuword(this) + sizeof(ObjectElements)); }
+    static ObjectElements * fromElements(Value *elems) {
+        return (ObjectElements *)(jsuword(elems) - sizeof(ObjectElements));
+    }
+
+    static int offsetOfCapacity() {
+        return (int)offsetof(ObjectElements, capacity) - (int)sizeof(ObjectElements);
+    }
+    static int offsetOfInitializedLength() {
+        return (int)offsetof(ObjectElements, initializedLength) - (int)sizeof(ObjectElements);
+    }
+    static int offsetOfLength() {
+        return (int)offsetof(ObjectElements, length) - (int)sizeof(ObjectElements);
+    }
+};
+
+/* Shared singleton for objects with no elements. */
+extern Value *emptyObjectElements;
+
 }  /* namespace js */
 
 /*
- * 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.
+ * JSObject struct. 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.
+ * The lastProp member stores the shape of the object, which includes the
+ * object's class and the layout of all its properties.
  *
  * The type member stores the type of the object, which contains its prototype
  * object and the possible types of its properties.
  *
- * An object is a delegate if it is on another object's prototype (type->proto
- * field) or scope chain (the parent field), and therefore the delegate might
- * be asked implicitly to get or set a property on behalf of another object.
- * Delegates may be accessed directly too, as may any object, but only those
- * objects linked after the head of any prototype or scope chain are flagged
- * as delegates. This definition helps to optimize shape-based property cache
- * invalidation (see Purge{Scope,Proto}Chain in jsobj.cpp).
+ * The rest of the object stores its named properties and indexed elements.
+ * These are stored separately from one another, and either may make use of a
+ * variable-sized array of fixed slots immediately following the object.
  *
- * The meaning of the system object bit is defined by the API client. It is
- * set in JS_NewSystemObject and is queried by JS_IsSystemObject (jsdbgapi.h),
- * 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.
+ * Two native objects with the same shape are guaranteed to have the same
+ * number of fixed slots.
  *
- * Both these flag bits are initially zero; they may be set or queried using
- * the (is|set)(Delegate|System) inline methods.
- *
- * Objects can have slots allocated either in a fixed array immediately
- * following the object, in dynamically allocated slots, or both. In all cases,
- * 'capacity' gives the number of usable slots. How the slots are organized
- * is different for dense arrays vs. other objects.
+ * Named property storage can be split between fixed slots and a dynamically
+ * allocated array (the slots member). For an object with N fixed slots, shapes
+ * with slots [0..N-1] are stored in the fixed slots, and the remainder are
+ * stored in the dynamic array. If all properties fit in the fixed slots, the
+ * properties member is NULL.
  *
- * For dense arrays (arrays with only normal integer properties), the 'slots'
- * member points either to the fixed array or to a dynamic array, and in
- * all cases is indexed by the associated property (e.g. obj->slots[5] stores
- * the value for property '5'). If a dynamic array is in use, slots in the
- * fixed array are not used.
+ * Elements are indexed via the elements member. This member can point to
+ * either the shared emptyObjectElements singleton, into the fixed slots (the
+ * address of the third fixed slot, to leave room for a ObjectElements header)
+ * or to a dynamically allocated array.
  *
- * ArrayBuffer objects may also use their fixed slots for storage in a similar
- * manner to dense arrays. The fixed slots do not represent Values in such
- * cases. (ArrayBuffers never have other properties added directly to them, as
- * they delegate such attempts to another JSObject).
+ * Only certain combinations of properties and elements storage are currently
+ * possible. This will be changing soon :XXX: bug 586842.
+ *
+ * - For objects other than arrays and typed arrays, the elements are empty.
  *
- * For objects other than dense arrays and array buffers, if the object has N
- * fixed slots then those are always the first N slots of the object. The
- * dynamic slots pointer is used if those fixed slots overflow, and stores all
- * remaining slots. The dynamic slots pointer is NULL if there is no slots
- * overflow, and never points to the object's fixed slots. Unlike dense arrays,
- * the fixed slots can always be accessed. Two objects with the same shape are
- * guaranteed to have the same number of fixed slots.
+ * - For 'slow' arrays, both elements and properties are used, but the
+ *   elements have zero capacity --- only the length member is used.
+ *
+ * - For dense arrays, elements are used and properties are not used.
  *
- * If you change this struct, you'll probably need to change the AccSet values
- * in jsbuiltins.h.
+ * - For typed array buffers, elements are used and properties are not used.
+ *   The data indexed by the elements do not represent Values, but primitive
+ *   unboxed integers or floating point values.
  */
-struct JSObject : js::gc::Cell {
+struct JSObject : js::gc::Cell
+{
     /*
      * TraceRecorder must be a friend because it generates code that
      * manipulates JSObjects, which requires peeking under any encapsulation.
      * ValidateWriter must be a friend because it works in tandem with
      * TraceRecorder.
      */
     friend class js::TraceRecorder;
     friend class nanojit::ValidateWriter;
 
+  private:
+    friend class js::Shape;
+
     /*
      * Private pointer to the last added property and methods to manipulate the
      * list it links among properties in this scope.
      */
-    js::Shape           *lastProp;
-
-  private:
-
-    inline void setLastProperty(const js::Shape *shape);
-    inline void removeLastProperty();
-
-    /* For setLastProperty() only. */
-    friend class js::StringObject;
+    js::Shape *shape_;
 
 #ifdef DEBUG
     void checkShapeConsistency();
 #endif
 
+    /*
+     * The object's type and prototype. For objects with the LAZY_TYPE flag
+     * set, this is the prototype's default 'new' type and can only be used
+     * to get that prototype.
+     */
+    js::types::TypeObject *type_;
+
+    /* Make the type object to use for LAZY_TYPE objects. */
+    void makeLazyType(JSContext *cx);
+
   public:
-    inline const js::Shape *lastProperty() const;
+    inline js::Shape *lastProperty() const {
+        JS_ASSERT(shape_);
+        return shape_;
+    }
+
+    /*
+     * Update the last property, keeping the number of allocated slots in sync
+     * with the object's new slot span.
+     */
+    bool setLastProperty(JSContext *cx, const js::Shape *shape);
+
+    /* As above, but does not change the slot span. */
+    inline void setLastPropertyInfallible(const js::Shape *shape);
+
+    /* Set the initial property for a newborn object. */
+    bool setInitialProperty(JSContext *cx, const js::Shape *shape);
+
+    /* As above, but the slot span is guaranteed to fit in the fixed slots. */
+    void setInitialPropertyInfallible(const js::Shape *shape);
+
+    /*
+     * Update the slot span directly for a dictionary object, and allocate
+     * slots to cover the new span if necessary.
+     */
+    bool setSlotSpan(JSContext *cx, uint32 span);
+
+    static inline size_t offsetOfShape() { return offsetof(JSObject, shape_); }
+    inline js::Shape **addressOfShape() { return &shape_; }
 
     inline js::Shape **nativeSearch(JSContext *cx, jsid id, bool adding = false);
     inline const js::Shape *nativeLookup(JSContext *cx, jsid id);
 
     inline bool nativeContains(JSContext *cx, jsid id);
     inline bool nativeContains(JSContext *cx, const js::Shape &shape);
 
     enum {
@@ -450,87 +518,87 @@ struct JSObject : js::gc::Cell {
         SYSTEM                    =       0x02,
         NOT_EXTENSIBLE            =       0x04,
         GENERIC                   =       0x10,
         INDEXED                   =       0x40,
         BOUND_FUNCTION            =      0x400,
         HAS_EQUALITY              =      0x800,
         VAROBJ                    =     0x1000,
         WATCHED                   =     0x2000,
-        PACKED_ARRAY              =     0x4000,
         ITERATED                  =     0x8000,
         SINGLETON_TYPE            =    0x10000,
         LAZY_TYPE                 =    0x20000,
 
         /* The top 5 bits of an object's flags are its number of fixed slots. */
         FIXED_SLOTS_SHIFT         =         27,
         FIXED_SLOTS_MASK          =       0x1f << FIXED_SLOTS_SHIFT,
 
-        UNUSED_FLAG_BITS          = 0x07FC30A0
+        UNUSED_FLAG_BITS          = 0x07FC70A0
     };
 
     /*
      * Impose a sane upper bound, originally checked only for dense arrays, on
      * number of slots in an object.
      */
     enum {
         NSLOTS_BITS     = 29,
         NSLOTS_LIMIT    = JS_BIT(NSLOTS_BITS)
     };
 
     uint32      flags;                      /* flags */
 
-    /* If dense array, the initialized length (see jsarray.cpp). */
-    jsuword initializedLength;
-
-    JS_FRIEND_API(size_t) sizeOfSlotsArray(JSUsableSizeFun usf);
-
     JSObject    *parent;                    /* object's parent */
     void        *privateData;               /* private data */
-    jsuword     capacity;                   /* total number of available slots */
 
   private:
-    js::Value   *slots;                     /* dynamically allocated slots,
-                                               or pointer to fixedSlots() for
-                                               dense arrays. */
+    js::Value   *slots;                     /* Slots for object properties. */
+    js::Value   *elements;                  /* Slots for object elements. */
 
-    /*
-     * The object's type and prototype. For objects with the LAZY_TYPE flag
-     * set, this is the prototype's default 'new' type and can only be used
-     * to get that prototype.
-     */
-    js::types::TypeObject *type_;
-
-    /* Make the type object to use for LAZY_TYPE objects. */
-    void makeLazyType(JSContext *cx);
+#if JS_BITS_PER_WORD == 32
+    void *padding;
+#endif
 
   public:
 
     inline bool isNative() const;
-    inline bool isNewborn() const;
+    inline bool isNewborn() const { return !shape_; }
 
     inline js::Class *getClass() const;
     inline JSClass *getJSClass() const;
     inline bool hasClass(const js::Class *c) const;
     inline const js::ObjectOps *getOps() const;
 
     inline void trace(JSTracer *trc);
     inline void scanSlots(js::GCMarker *gcmarker);
 
+    /*
+     * An object is a delegate if it is on another object's prototype or scope
+     * chain, and therefore the delegate might be asked implicitly to get or
+     * set a property on behalf of another object. Delegates may be accessed
+     * directly too, as may any object, but only those objects linked after the
+     * head of any prototype or scope chain are flagged as delegates. This
+     * definition helps to optimize shape-based property cache invalidation
+     * (see Purge{Scope,Proto}Chain in jsobj.cpp).
+     */
     bool isDelegate() const     { return !!(flags & DELEGATE); }
     void setDelegate()          { flags |= DELEGATE; }
     void clearDelegate()        { flags &= ~DELEGATE; }
 
     bool isBoundFunction() const { return !!(flags & BOUND_FUNCTION); }
 
     static void setDelegateNullSafe(JSObject *obj) {
         if (obj)
             obj->setDelegate();
     }
 
+    /*
+     * The meaning of the system object bit is defined by the API client. It is
+     * set in JS_NewSystemObject and is queried by JS_IsSystemObject, but it
+     * has no intrinsic meaning to SpiderMonkey.
+     */
     bool isSystem() const       { return !!(flags & SYSTEM); }
     void setSystem()            { flags |= SYSTEM; }
 
     bool generic()              { return !!(flags & GENERIC); }
     void setGeneric()           { flags |= GENERIC; }
 
     bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); }
     inline void assertSpecialEqualitySynced() const;
@@ -551,17 +619,16 @@ struct JSObject : js::gc::Cell {
    /* See StackFrame::varObj. */
    inline bool isVarObj() const { return flags & VAROBJ; }
    inline void makeVarObj() { flags |= VAROBJ; }
   private:
     bool generateOwnShape(JSContext *cx, js::Shape *newShape = NULL);
 
   public:
     inline bool nativeEmpty() const;
-    inline void setMap(js::Shape *amap);
 
     /* Functions for setting up scope chain object maps and shapes. */
     bool initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent);
     bool initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *priv);
     void setBlockOwnShape(JSContext *cx);
 
     const js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
     bool protoShapeChange(JSContext *cx);
@@ -591,146 +658,133 @@ struct JSObject : js::gc::Cell {
      * a doubly-linked list.
      */
     inline bool inDictionaryMode() const;
 
     inline uint32 propertyCount() const;
 
     inline bool hasPropertyTable() const;
 
-    uint32 numSlots() const { return uint32(capacity); }
-
     inline size_t structSize() const;
     inline size_t slotsAndStructSize() const;
 
-    /* Slot accessors for JITs. */
-
-    static inline size_t getFixedSlotOffset(size_t slot);
-    static inline size_t offsetOfCapacity() { return offsetof(JSObject, capacity); }
-    static inline size_t offsetOfSlots() { return offsetof(JSObject, slots); }
+    inline size_t numFixedSlots() const;
 
-    /*
-     * Get a raw pointer to the object's slots, or a slot of the object given
-     * a previous value for its since-reallocated dynamic slots.
-     */
-    inline const js::Value *getRawSlots();
-    inline const js::Value *getRawSlot(size_t slot, const js::Value *slots);
+  private:
+    inline js::Value* fixedSlots() const;
+  public:
+
+    /* Accessors for properties. */
 
     /* Whether a slot is at a fixed offset from this object. */
     inline bool isFixedSlot(size_t slot);
 
     /* Index into the dynamic slots array to use for a dynamic slot. */
     inline size_t dynamicSlotIndex(size_t slot);
 
-    inline size_t numFixedSlots() const;
+    /* Get a raw pointer to the object's properties. */
+    inline const js::Value *getRawSlots();
 
-    /* Whether this object has any dynamic slots at all. */
-    inline bool hasSlotsArray() const;
+    /* JIT Accessors */
+    static inline size_t getFixedSlotOffset(size_t slot);
+    static inline size_t offsetOfSlots() { return offsetof(JSObject, slots); }
+
+    /* Minimum size for dynamically allocated slots. */
+    static const uint32 SLOT_CAPACITY_MIN = 8;
 
-    /* Get the number of dynamic slots required for a given capacity. */
-    inline size_t numDynamicSlots(size_t capacity) const;
+    /*
+     * Grow or shrink slots immediately before changing the slot span.
+     * The number of allocated slots is not stored explicitly, and changes to
+     * the slots must track changes in the slot span.
+     */
+    bool growSlots(JSContext *cx, uint32 oldCount, uint32 newCount);
+    void shrinkSlots(JSContext *cx, uint32 oldCount, uint32 newCount);
+    bool changeSlots(JSContext *cx, uint32 oldCount, uint32 newCount) {
+        if (oldCount < newCount)
+            return growSlots(cx, oldCount, newCount);
+        else if (oldCount > newCount)
+            shrinkSlots(cx, oldCount, newCount);
+        return true;
+    }
 
-  private:
-    inline js::Value* fixedSlots() const;
+    bool hasDynamicSlots() const { return slots != NULL; }
+
+    /*
+     * Get the number of dynamic slots to allocate to cover the properties in
+     * an object with the given number of fixed slots and slot span. The slot
+     * capacity is not stored explicitly, and the allocated size of the slot
+     * array is kept in sync with this count.
+     */
+    static inline size_t dynamicSlotsCount(size_t nfixed, size_t span);
+
+    /* Compute dynamicSlotsCount() for this object. */
+    inline size_t numDynamicSlots() const;
 
   protected:
     inline bool hasContiguousSlots(size_t start, size_t count) const;
 
-  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);
+    inline void clearSlotRange(size_t start, size_t count);
+    inline void invalidateSlotRange(size_t start, size_t count);
+    inline void updateSlotsForSpan(size_t oldSpan, size_t newSpan);
 
-    bool ensureSlots(JSContext *cx, size_t nslots) {
-        if (numSlots() < nslots)
-            return growSlots(cx, nslots);
-        return true;
-    }
-
-    /*
-     * Fill a range of slots with holes or undefined, depending on whether this
-     * is a dense array.
-     */
-    void clearSlotRange(size_t start, size_t length);
+  public:
 
     /*
      * Copy a flat array of slots to this object at a start slot. Caller must
      * ensure there are enough slots in this object.
      */
     void copySlotRange(size_t start, const js::Value *vector, size_t length);
 
-    /*
-     * 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
-     * its map set to a shape path that maps those slots.
-     *
-     * Block objects satisfy (a) and (b), as there is no evil eval-based way to
-     * 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);
-
-    /*
-     * 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);
-
     inline uint32 slotSpan() const;
 
     inline bool containsSlot(uint32 slot) const;
 
     void rollbackProperties(JSContext *cx, uint32 slotSpan);
 
+#ifdef DEBUG
+    /*
+     * Check that slot is in range for the object's allocated slots.
+     * If sentinelAllowed then slot may equal the slot capacity.
+     */
+    bool slotInRange(uintN slot, bool sentinelAllowed = false) const;
+#endif
+
     js::Value *getSlotAddress(uintN slot) {
         /*
          * This can be used to get the address of the end of the slots for the
          * object, which may be necessary when fetching zero-length arrays of
          * slots (e.g. for callObjVarArray).
          */
-        JS_ASSERT(slot <= capacity);
+        JS_ASSERT(slotInRange(slot, /* sentinelAllowed = */ true));
         size_t fixed = numFixedSlots();
         if (slot < fixed)
             return fixedSlots() + slot;
         return slots + (slot - fixed);
     }
 
     js::Value &getSlotRef(uintN slot) {
-        JS_ASSERT(slot < capacity);
+        JS_ASSERT(slotInRange(slot));
         return *getSlotAddress(slot);
     }
 
     inline js::Value &nativeGetSlotRef(uintN slot);
 
     const js::Value &getSlot(uintN slot) const {
-        JS_ASSERT(slot < capacity);
+        JS_ASSERT(slotInRange(slot));
         size_t fixed = numFixedSlots();
         if (slot < fixed)
             return fixedSlots()[slot];
         return slots[slot - fixed];
     }
 
     inline const js::Value &nativeGetSlot(uintN slot) const;
     inline JSFunction *nativeGetMethod(const js::Shape *shape) const;
 
     void setSlot(uintN slot, const js::Value &value) {
-        JS_ASSERT(slot < capacity);
+        JS_ASSERT(slotInRange(slot));
         getSlotRef(slot) = value;
     }
 
     inline void nativeSetSlot(uintN slot, const js::Value &value);
     inline void nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value);
 
     inline js::Value getReservedSlot(uintN index) const;
 
@@ -753,17 +807,17 @@ struct JSObject : js::gc::Cell {
         JS_ASSERT(slot < numFixedSlots());
         fixedSlots()[slot] = value;
     }
 
     /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
     inline void updateFlags(const js::Shape *shape, bool isDefinitelyAtom = false);
 
     /* Extend this object to have shape as its last-added property. */
-    inline void extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom = false);
+    inline bool extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom = false);
 
     /*
      * Whether this is the only object which has its specified type. This
      * object will have its type constructed lazily as needed by analysis.
      */
     bool hasSingletonType() const { return flags & SINGLETON_TYPE; }
 
     /*
@@ -887,40 +941,71 @@ struct JSObject : js::gc::Cell {
         /* All primitive objects have their value in a fixed slot. */
         return getFixedSlotOffset(JSSLOT_PRIMITIVE_THIS);
     }
 
   public:
     inline js::NumberObject *asNumber();
     inline js::StringObject *asString();
 
+    /* Accessors for elements. */
+
+    js::ObjectElements *getElementsHeader() const {
+        return js::ObjectElements::fromElements(elements);
+    }
+
+    inline bool ensureElements(JSContext *cx, uintN cap);
+    bool growElements(JSContext *cx, uintN cap);
+    void shrinkElements(JSContext *cx, uintN cap);
+
+    inline js::Value* fixedElements() const {
+        JS_STATIC_ASSERT(2 * sizeof(js::Value) == sizeof(js::ObjectElements));
+        return &fixedSlots()[2];
+    }
+
+    inline bool hasDynamicElements() const {
+        /*
+         * Note: for objects with zero fixed slots this could potentially give
+         * a spurious 'true' result, if the end of this object is exactly
+         * aligned with the end of its arena and dynamic slots are allocated
+         * immediately afterwards. Such cases cannot occur for dense arrays
+         * (which have at least two fixed slots) and can only result in a leak.
+         */
+        return elements != js::emptyObjectElements && elements != fixedElements();
+    }
+
+    /* JIT Accessors */
+    static inline size_t offsetOfElements() { return offsetof(JSObject, elements); }
+    static inline size_t offsetOfFixedElements() {
+        return sizeof(JSObject) + sizeof(js::ObjectElements);
+    }
+
     /*
      * Array-specific getters and setters (for both dense and slow arrays).
      */
 
+    bool allocateSlowArrayElements(JSContext *cx);
+
     inline uint32 getArrayLength() const;
     inline void setArrayLength(JSContext *cx, uint32 length);
 
     inline uint32 getDenseArrayCapacity();
     inline uint32 getDenseArrayInitializedLength();
     inline void setDenseArrayLength(uint32 length);
     inline void setDenseArrayInitializedLength(uint32 length);
     inline void ensureDenseArrayInitializedLength(JSContext *cx, uintN index, uintN extra);
-    inline void backfillDenseArrayHoles(JSContext *cx);
     inline const js::Value* getDenseArrayElements();
     inline const js::Value &getDenseArrayElement(uintN idx);
     inline void setDenseArrayElement(uintN idx, const js::Value &val);
     inline void setDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val);
     inline void copyDenseArrayElements(uintN dstStart, const js::Value *src, uintN count);
     inline void moveDenseArrayElements(uintN dstStart, uintN srcStart, uintN count);
-    inline void shrinkDenseArrayElements(JSContext *cx, uintN cap);
     inline bool denseArrayHasInlineSlots() const;
 
     /* Packed information for this array. */
-    inline bool isPackedDenseArray();
     inline void markDenseArrayNotPacked(JSContext *cx);
 
     /*
      * ensureDenseArrayElements ensures that the dense array can hold at least
      * index + extra elements. It returns ED_OK on success, ED_FAILED on
      * failure to grow the array, ED_SPARSE when the array is too sparse to
      * grow (this includes the case of index + extra overflow). In the last
      * two cases the array is kept intact.
@@ -985,17 +1070,16 @@ struct JSObject : js::gc::Cell {
     inline void setDateUTCTime(const js::Value &pthis);
 
     /*
      * Function-specific getters and setters.
      */
 
   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;
 
     /*
@@ -1137,24 +1221,25 @@ struct JSObject : js::gc::Cell {
     inline void setWithThis(JSObject *thisp);
 
     /*
      * Back to generic stuff.
      */
     inline bool isCallable();
 
     /* Do initialization required immediately after allocation. */
-    void earlyInit(jsuword capacity) {
-        this->capacity = capacity;
+    void earlyInit(jsuword capacity)
+    {
+        flags = capacity << FIXED_SLOTS_SHIFT;
 
         /* Stops obj from being scanned until initializated. */
-        lastProp = NULL;
+        shape_ = NULL;
     }
 
-    /* The map field is not initialized here and should be set separately. */
+    /* The last property is not initialized here and should be set separately. */
     void init(JSContext *cx, js::types::TypeObject *type,
               JSObject *parent, void *priv, bool denseArray);
 
     inline void finish(JSContext *cx);
     JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
 
     /*
      * Like init, but also initializes map.  proto must have an empty shape
@@ -1340,17 +1425,17 @@ struct JSObject : js::gc::Cell {
 
     inline void initArrayClass();
 
   private:
     static void staticAsserts() {
         /* Check alignment for any fixed slots allocated after the object. */
         JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0);
 
-        JS_STATIC_ASSERT(offsetof(JSObject, lastProp) == offsetof(js::shadow::Object, lastProp));
+        JS_STATIC_ASSERT(offsetof(JSObject, shape_) == offsetof(js::shadow::Object, shape));
         JS_STATIC_ASSERT(offsetof(JSObject, flags) == offsetof(js::shadow::Object, flags));
         JS_STATIC_ASSERT(offsetof(JSObject, parent) == offsetof(js::shadow::Object, parent));
         JS_STATIC_ASSERT(offsetof(JSObject, privateData) == offsetof(js::shadow::Object, privateData));
         JS_STATIC_ASSERT(offsetof(JSObject, slots) == offsetof(js::shadow::Object, slots));
         JS_STATIC_ASSERT(offsetof(JSObject, type_) == offsetof(js::shadow::Object, type));
         JS_STATIC_ASSERT(sizeof(JSObject) == sizeof(js::shadow::Object));
         JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Object::FIXED_SLOTS_SHIFT);
     }
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -311,45 +311,47 @@ JSObject::finalize(JSContext *cx, bool b
 inline bool
 JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent)
 {
     js::types::TypeObject *type = cx->compartment->getEmptyType(cx);
     if (!type)
         return false;
 
     init(cx, type, parent, NULL, false);
-    setMap(bindings.lastShape());
+    if (!setInitialProperty(cx, bindings.lastShape()))
+        return false;
 
     JS_ASSERT(isCall());
     JS_ASSERT(!inDictionaryMode());
 
     /*
      * If |bindings| is for a function that has extensible parents, that means
      * its Call should have its own shape; see js::BaseShape::extensibleParents.
      */
-    if (lastProp->extensibleParents())
+    if (lastProperty()->extensibleParents())
         return generateOwnShape(cx);
     return true;
 }
 
 /*
  * Initializer for cloned block objects. Set class, prototype, frame, map, and
  * shape.
  */
 inline bool
 JSObject::initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *frame)
 {
     init(cx, type, NULL, frame, false);
 
-    setMap(getProto()->lastProp);
+    if (!setInitialProperty(cx, getProto()->lastProperty()))
+        return false;
 
     JS_ASSERT(!inDictionaryMode());
     JS_ASSERT(isClonedBlock());
 
-    if (lastProp->extensibleParents())
+    if (lastProperty()->extensibleParents())
         return generateOwnShape(cx);
     return true;
 }
 
 /*
  * Property read barrier for deferred cloning of compiler-created function
  * objects optimized as typically non-escaping, ad-hoc methods in obj.
  */
@@ -397,57 +399,68 @@ JSObject::canHaveMethodBarrier() const
 
 inline const js::Value *
 JSObject::getRawSlots()
 {
     JS_ASSERT(isGlobal());
     return slots;
 }
 
-inline const js::Value *
-JSObject::getRawSlot(size_t slot, const js::Value *slots)
-{
-    JS_ASSERT(isGlobal());
-    size_t fixed = numFixedSlots();
-    if (slot < fixed)
-        return fixedSlots() + slot;
-    return slots + slot - fixed;
-}
-
 inline bool
 JSObject::isFixedSlot(size_t slot)
 {
     JS_ASSERT(!isDenseArray());
     return slot < numFixedSlots();
 }
 
 inline size_t
-JSObject::numDynamicSlots(size_t capacity) const
-{
-    JS_ASSERT(capacity >= numFixedSlots());
-    return isDenseArray() ? capacity : capacity - numFixedSlots();
-}
-
-inline size_t
 JSObject::dynamicSlotIndex(size_t slot)
 {
     JS_ASSERT(!isDenseArray() && slot >= numFixedSlots());
     return slot - numFixedSlots();
 }
 
-inline bool
-JSObject::ensureClassReservedSlots(JSContext *cx)
+/*static*/ inline size_t
+JSObject::dynamicSlotsCount(size_t nfixed, size_t span)
 {
-    return !nativeEmpty() || ensureClassReservedSlotsForEmptyObject(cx);
+    if (span <= nfixed)
+        return 0;
+    span -= nfixed;
+    if (span <= SLOT_CAPACITY_MIN)
+        return SLOT_CAPACITY_MIN;
+
+    size_t log2;
+    JS_FLOOR_LOG2(log2, span - 1);
+    size_t slots = 1 << (log2 + 1);
+    JS_ASSERT(slots >= span);
+    return slots;
+}
+
+inline size_t
+JSObject::numDynamicSlots() const
+{
+    return dynamicSlotsCount(numFixedSlots(), slotSpan());
+}
+
+inline void
+JSObject::setLastPropertyInfallible(const js::Shape *shape)
+{
+    JS_ASSERT(!shape->inDictionary());
+    JS_ASSERT(shape->compartment() == compartment());
+    JS_ASSERT(!inDictionaryMode());
+    JS_ASSERT(slotSpan() == shape->slotSpan());
+
+    shape_ = const_cast<js::Shape *>(shape);
 }
 
 inline js::Value
 JSObject::getReservedSlot(uintN index) const
 {
-    return (index < numSlots()) ? getSlot(index) : js::UndefinedValue();
+    JS_ASSERT(index < JSSLOT_FREE(getClass()));
+    return getSlot(index);
 }
 
 inline void
 JSObject::setReservedSlot(uintN index, const js::Value &v)
 {
     JS_ASSERT(index < JSSLOT_FREE(getClass()));
     setSlot(index, v);
 }
@@ -462,62 +475,31 @@ JSObject::getPrimitiveThis() const
 inline void
 JSObject::setPrimitiveThis(const js::Value &pthis)
 {
     JS_ASSERT(isPrimitive());
     setFixedSlot(JSSLOT_PRIMITIVE_THIS, pthis);
 }
 
 inline bool
-JSObject::hasSlotsArray() const
-{
-    JS_ASSERT_IF(!slots, !isDenseArray());
-    JS_ASSERT_IF(slots == fixedSlots(), isDenseArray() || isArrayBuffer());
-    return slots && slots != fixedSlots();
-}
-
-inline bool
 JSObject::hasContiguousSlots(size_t start, size_t count) const
 {
     /*
      * Check that the range [start, start+count) is either all inline or all
      * out of line.
      */
-    JS_ASSERT(start + count <= numSlots());
+    JS_ASSERT(slotInRange(start + count, /* sentinelAllowed = */ true));
     return (start + count <= numFixedSlots()) || (start >= numFixedSlots());
 }
 
-inline size_t
-JSObject::structSize() const
-{
-    return (isFunction() && !getPrivate())
-           ? sizeof(JSFunction)
-           : (sizeof(JSObject) + sizeof(js::Value) * numFixedSlots());
-}
-
-inline size_t
-JSObject::slotsAndStructSize() const
-{
-    int ndslots = 0;
-    if (isDenseArray()) {
-        if (!denseArrayHasInlineSlots())
-            ndslots = numSlots();
-    } else {
-        if (slots)
-            ndslots = numSlots() - numFixedSlots();
-    }
-
-    return structSize() + sizeof(js::Value) * ndslots;
-}
-
 inline uint32
 JSObject::getArrayLength() const
 {
     JS_ASSERT(isArray());
-    return (uint32)(uintptr_t) getPrivate();
+    return getElementsHeader()->length;
 }
 
 inline void
 JSObject::setArrayLength(JSContext *cx, uint32 length)
 {
     JS_ASSERT(isArray());
 
     if (length > INT32_MAX) {
@@ -528,91 +510,91 @@ JSObject::setArrayLength(JSContext *cx, 
         js::types::MarkTypeObjectFlags(cx, this,
                                        js::types::OBJECT_FLAG_NON_PACKED_ARRAY |
                                        js::types::OBJECT_FLAG_NON_DENSE_ARRAY);
         jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
         js::types::AddTypePropertyId(cx, this, lengthId,
                                      js::types::Type::DoubleType());
     }
 
-    setPrivate((void*)(uintptr_t) length);
+    getElementsHeader()->length = length;
 }
 
 inline void
 JSObject::setDenseArrayLength(uint32 length)
 {
     /* Variant of setArrayLength for use on dense arrays where the length cannot overflow int32. */
     JS_ASSERT(isDenseArray());
     JS_ASSERT(length <= INT32_MAX);
-    setPrivate((void*)(uintptr_t) length);
+    getElementsHeader()->length = length;
 }
 
 inline uint32
 JSObject::getDenseArrayCapacity()
 {
     JS_ASSERT(isDenseArray());
-    return numSlots();
+    return getElementsHeader()->capacity;
+}
+
+inline bool
+JSObject::ensureElements(JSContext *cx, uint32 capacity)
+{
+    if (capacity > getDenseArrayCapacity())
+        return growElements(cx, capacity);
+    return true;
 }
 
 inline const js::Value *
 JSObject::getDenseArrayElements()
 {
     JS_ASSERT(isDenseArray());
-    return slots;
+    return elements;
 }
 
 inline const js::Value &
 JSObject::getDenseArrayElement(uintN idx)
 {
     JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength());
-    return slots[idx];
+    return elements[idx];
 }
 
 inline void
 JSObject::setDenseArrayElement(uintN idx, const js::Value &val)
 {
     JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength());
-    slots[idx] = val;
+    elements[idx] = val;
 }
 
 inline void
 JSObject::setDenseArrayElementWithType(JSContext *cx, uintN idx, const js::Value &val)
 {
     js::types::AddTypePropertyId(cx, this, JSID_VOID, val);
     setDenseArrayElement(idx, val);
 }
 
 inline void
 JSObject::copyDenseArrayElements(uintN dstStart, const js::Value *src, uintN count)
 {
-    JS_ASSERT(isDenseArray());
-    copySlotRange(dstStart, src, count);
+    JS_ASSERT(dstStart + count <= getDenseArrayCapacity());
+    memcpy(elements + dstStart, src, count * sizeof(js::Value));
 }
 
 inline void
 JSObject::moveDenseArrayElements(uintN dstStart, uintN srcStart, uintN count)
 {
-    JS_ASSERT(isDenseArray());
-    JS_ASSERT(dstStart + count <= capacity);
-    JS_ASSERT(srcStart + count <= capacity);
-    memmove(slots + dstStart, slots + srcStart, count * sizeof(js::Value));
-}
-
-inline void
-JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
-{
-    JS_ASSERT(isDenseArray());
-    shrinkSlots(cx, cap);
+    JS_ASSERT(dstStart + count <= getDenseArrayCapacity());
+    JS_ASSERT(srcStart + count <= getDenseArrayCapacity());
+    memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value));
 }
 
 inline bool
 JSObject::denseArrayHasInlineSlots() const
 {
-    JS_ASSERT(isDenseArray() && slots);
-    return slots == fixedSlots();
+    JS_ASSERT(isDenseArray());
+    return elements == fixedElements();
 }
 
 namespace js {
 
 /*
  * Any name atom for a function which will be added as a DeclEnv object to the
  * scope chain above call objects for fun.
  */
@@ -707,18 +689,17 @@ JSObject::setFlatClosureUpvars(js::Value
     JS_ASSERT(isFunction());
     JS_ASSERT(getFunctionPrivate()->isFlatClosure());
     setFixedSlot(JSSLOT_FLAT_CLOSURE_UPVARS, js::PrivateValue(upvars));
 }
 
 inline bool
 JSObject::hasMethodObj(const JSObject& obj) const
 {
-    return JSSLOT_FUN_METHOD_OBJ < numSlots() &&
-           getFixedSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
+    return getFixedSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
            getFixedSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == obj;
 }
 
 inline void
 JSObject::setMethodObj(JSObject& obj)
 {
     setFixedSlot(JSSLOT_FUN_METHOD_OBJ, js::ObjectValue(obj));
 }
@@ -835,17 +816,17 @@ JSObject::setWithThis(JSObject *thisp)
 }
 
 inline bool
 JSObject::setSingletonType(JSContext *cx)
 {
     if (!cx->typeInferenceEnabled())
         return true;
 
-    JS_ASSERT(!lastProp->previous());
+    JS_ASSERT(!lastProperty()->previous());
     JS_ASSERT(!hasLazyType());
     JS_ASSERT_IF(getProto(), type() == getProto()->getNewType(cx, NULL));
 
     flags |= SINGLETON_TYPE | LAZY_TYPE;
 
     return true;
 }
 
@@ -882,19 +863,16 @@ JSObject::setType(js::types::TypeObject 
     JS_ASSERT(!hasSingletonType());
     type_ = newType;
 }
 
 inline bool JSObject::isArguments() const { return isNormalArguments() || isStrictArguments(); }
 inline bool JSObject::isArrayBuffer() const { return hasClass(&js::ArrayBufferClass); }
 inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); }
 inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); }
-inline bool JSObject::isArray() const { return isSlowArray() || isDenseArray(); }
-inline bool JSObject::isDenseArray() const { return hasClass(&js::ArrayClass); }
-inline bool JSObject::isSlowArray() const { return hasClass(&js::SlowArrayClass); }
 inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); }
 inline bool JSObject::isBoolean() const { return hasClass(&js::BooleanClass); }
 inline bool JSObject::isString() const { return hasClass(&js::StringClass); }
 inline bool JSObject::isPrimitive() const { return isNumber() || isString() || isBoolean(); }
 inline bool JSObject::isDate() const { return hasClass(&js::DateClass); }
 inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); }
 inline bool JSObject::isObject() const { return hasClass(&js::ObjectClass); }
 inline bool JSObject::isWith() const { return hasClass(&js::WithClass); }
@@ -909,16 +887,35 @@ inline bool JSObject::isGenerator() cons
 inline bool JSObject::isIterator() const { return hasClass(&js::IteratorClass); }
 inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); }
 inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); }
 inline bool JSObject::isXML() const { return hasClass(&js::XMLClass); }
 inline bool JSObject::isNamespace() const { return hasClass(&js::NamespaceClass); }
 inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); }
 inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); }
 
+inline bool JSObject::isArray() const
+{
+    return isSlowArray() || isDenseArray();
+}
+
+inline bool JSObject::isDenseArray() const
+{
+    bool result = hasClass(&js::ArrayClass);
+    JS_ASSERT_IF(result, elements != js::emptyObjectElements);
+    return result;
+}
+
+inline bool JSObject::isSlowArray() const
+{
+    bool result = hasClass(&js::SlowArrayClass);
+    JS_ASSERT_IF(result, elements != js::emptyObjectElements);
+    return result;
+}
+
 inline bool
 JSObject::isXMLId() const
 {
     return hasClass(&js::QNameClass)
         || hasClass(&js::AttributeNameClass)
         || hasClass(&js::AnyNameClass);
 }
 
@@ -929,62 +926,65 @@ JSObject::isQName() const
         || hasClass(&js::AttributeNameClass)
         || hasClass(&js::AnyNameClass);
 }
 
 inline void
 JSObject::init(JSContext *cx, js::types::TypeObject *type,
                JSObject *parent, void *priv, bool denseArray)
 {
-    flags = capacity << FIXED_SLOTS_SHIFT;
+    privateData = priv;
 
-    privateData = priv;
+    JS_STATIC_ASSERT(sizeof(js::ObjectElements) == 2 * sizeof(js::Value));
+
+    uint32 numSlots = numFixedSlots();
 
     /*
      * Fill the fixed slots with undefined if needed.  This object must
-     * already have its capacity filled in, as by js_NewGCObject. If inference
-     * is disabled, NewArray will backfill holes up to the array's capacity
-     * and unset the PACKED_ARRAY flag.
+     * already have its numFixedSlots() filled in, as by js_NewGCObject.
      */
     slots = NULL;
     if (denseArray) {
-        slots = fixedSlots();
-        flags |= PACKED_ARRAY;
+        JS_ASSERT(numSlots >= 2);
+        elements = fixedElements();
+        new (getElementsHeader()) js::ObjectElements(numSlots - 2);
     } else {
-        js::ClearValueRange(fixedSlots(), capacity, denseArray);
+        elements = js::emptyObjectElements;
+        js::ClearValueRange(fixedSlots(), numSlots);
     }
 
-    initializedLength = 0;
-
     setType(type);
     setParent(parent);
 }
 
 inline void
 JSObject::finish(JSContext *cx)
 {
-    if (slots && slots != fixedSlots())
+    if (hasDynamicSlots())
         cx->free_(slots);
+    if (hasDynamicElements())
+        cx->free_(getElementsHeader());
 }
 
 inline bool
 JSObject::initSharingEmptyShape(JSContext *cx,
                                 js::Class *aclasp,
                                 js::types::TypeObject *type,
                                 JSObject *parent,
                                 void *privateValue,
                                 js::gc::AllocKind kind)
 {
     init(cx, type, parent, privateValue, false);
 
     js::EmptyShape *empty = type->getEmptyShape(cx, aclasp, kind);
     if (!empty)
         return false;
 
-    setMap(empty);
+    if (!setInitialProperty(cx, empty))
+        return false;
 
     JS_ASSERT(!isDenseArray());
     return true;
 }
 
 inline bool
 JSObject::hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags)
 {
@@ -1010,35 +1010,28 @@ JSObject::principals(JSContext *cx)
     if (JSObjectPrincipalsFinder finder = cb ? cb->findObjectPrincipals : NULL)
         return finder(cx, this);
     return cx->compartment ? cx->compartment->principals : NULL;
 }
 
 inline uint32
 JSObject::slotSpan() const
 {
+    JS_ASSERT(!isNewborn());
     if (inDictionaryMode())
-        return lastProp->base()->slotSpan();
-    uint32 free = JSSLOT_FREE(getClass());
-    return lastProp->hasMissingSlot() ? free : js::Max(free, lastProp->maybeSlot() + 1);
+        return lastProperty()->base()->slotSpan();
+    return lastProperty()->slotSpan();
 }
 
 inline bool
 JSObject::containsSlot(uint32 slot) const
 {
     return slot < slotSpan();
 }
 
-inline void
-JSObject::setMap(js::Shape *amap)
-{
-    JS_ASSERT_IF(lastProp && isNative(), !inDictionaryMode());
-    lastProp = amap;
-}
-
 inline js::Value &
 JSObject::nativeGetSlotRef(uintN slot)
 {
     JS_ASSERT(isNative());
     JS_ASSERT(containsSlot(slot));
     return getSlotRef(slot);
 }
 
@@ -1079,29 +1072,23 @@ JSObject::nativeSetSlotWithType(JSContex
 {
     nativeSetSlot(shape->slot(), value);
     js::types::AddTypePropertyId(cx, this, shape->propid(), value);
 }
 
 inline bool
 JSObject::isNative() const
 {
-    return lastProp->isNative();
-}
-
-inline bool
-JSObject::isNewborn() const
-{
-    return !lastProp;
+    return lastProperty()->isNative();
 }
 
 inline js::Shape **
 JSObject::nativeSearch(JSContext *cx, jsid id, bool adding)
 {
-    return js::Shape::search(cx, &lastProp, id, adding);
+    return js::Shape::search(cx, lastProperty(), id, adding);
 }
 
 inline const js::Shape *
 JSObject::nativeLookup(JSContext *cx, jsid id)
 {
     JS_ASSERT(isNative());
     return SHAPE_FETCH(nativeSearch(cx, id));
 }
@@ -1113,24 +1100,16 @@ JSObject::nativeContains(JSContext *cx, 
 }
 
 inline bool
 JSObject::nativeContains(JSContext *cx, const js::Shape &shape)
 {
     return nativeLookup(cx, shape.propid()) == &shape;
 }
 
-inline const js::Shape *
-JSObject::lastProperty() const
-{
-    JS_ASSERT_IF(!isNative(), lastProp->isEmptyShape());
-    JS_ASSERT_IF(!lastProp->isEmptyShape(), !JSID_IS_EMPTY(lastProp->propid()));
-    return lastProp;
-}
-
 inline bool
 JSObject::nativeEmpty() const
 {
     return lastProperty()->isEmptyShape();
 }
 
 inline bool
 JSObject::inDictionaryMode() const
@@ -1145,34 +1124,31 @@ JSObject::propertyCount() const
 }
 
 inline bool
 JSObject::hasPropertyTable() const
 {
     return lastProperty()->hasTable();
 }
 
-/*
- * FIXME: shape must not be null, should use a reference here and other places.
- */
-inline void
-JSObject::setLastProperty(const js::Shape *shape)
+inline size_t
+JSObject::structSize() const
 {
-    JS_ASSERT(!inDictionaryMode());
-    JS_ASSERT(shape->compartment() == compartment());
-
-    lastProp = const_cast<js::Shape *>(shape);
+    return (isFunction() && !getPrivate())
+           ? sizeof(JSFunction)
+           : (sizeof(JSObject) + sizeof(js::Value) * numFixedSlots());
 }
 
-inline void
-JSObject::removeLastProperty()
+inline size_t
+JSObject::slotsAndStructSize() const
 {
-    JS_ASSERT(!inDictionaryMode());
-
-    lastProp = lastProp->parent;
+    int ndslots = numDynamicSlots();
+    if (hasDynamicElements())
+        ndslots += getElementsHeader()->capacity;
+    return structSize() + ndslots * sizeof(js::Value);
 }
 
 inline JSBool
 JSObject::lookupProperty(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp)
 {
     js::LookupPropOp op = getOps()->lookupProperty;
     return (op ? op : js_LookupProperty)(cx, this, id, objp, propp);
 }
@@ -1374,38 +1350,29 @@ InitScopeForObject(JSContext* cx, JSObje
         empty = type->getEmptyShape(cx, clasp, kind);
     else
         empty = js::EmptyShape::create(cx, clasp);
     if (!empty) {
         JS_ASSERT(obj->isNewborn());
         return false;
     }
 
-    obj->setMap(empty);
-
-    uint32 freeslot = JSSLOT_FREE(clasp);
-    if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot)) {
-        obj->setMap(NULL);
-        JS_ASSERT(obj->isNewborn());
-        return false;
-    }
-
-    return true;
+    return obj->setInitialProperty(cx, empty);
 }
 
 static inline bool
 InitScopeForNonNativeObject(JSContext *cx, JSObject *obj, js::Class *clasp)
 {
     JS_ASSERT(!clasp->isNative());
 
     js::EmptyShape *empty = js::BaseShape::lookupEmpty(cx, clasp);
     if (!empty)
         return false;
 
-    obj->setMap(empty);
+    obj->setInitialPropertyInfallible(empty);
     return true;
 }
 
 static inline bool
 CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp)
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST);
@@ -1456,21 +1423,19 @@ NewNativeClassInstance(JSContext *cx, Cl
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
     bool denseArray = (clasp == &ArrayClass);
     obj->init(cx, type, parent, NULL, denseArray);
 
     JS_ASSERT(type->canProvideEmptyShape(clasp));
     js::EmptyShape *empty = type->getEmptyShape(cx, clasp, kind);
-    if (!empty)
+    if (!empty || !obj->setInitialProperty(cx, empty))
         return NULL;
 
-    obj->setMap(empty);
-
     return obj;
 }
 
 static inline JSObject *
 NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
     return NewNativeClassInstance(cx, clasp, proto, parent, kind);
@@ -1726,21 +1691,29 @@ NewReshapedObject(JSContext *cx, js::typ
                   gc::AllocKind kind, const Shape *shape);
 
 /*
  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
  * the object, zero if the final size is unknown. This should only be used for
  * objects that do not require any fixed slots.
  */
 static inline gc::AllocKind
-GuessObjectGCKind(size_t numSlots, bool isArray)
+GuessObjectGCKind(size_t numSlots)
 {
     if (numSlots)
-        return gc::GetGCObjectKind(numSlots, isArray);
-    return isArray ? gc::FINALIZE_OBJECT8 : gc::FINALIZE_OBJECT4;
+        return gc::GetGCObjectKind(numSlots);
+    return gc::FINALIZE_OBJECT4;
+}
+
+static inline gc::AllocKind
+GuessArrayGCKind(size_t numSlots)
+{
+    if (numSlots)
+        return gc::GetGCArrayKind(numSlots);
+    return gc::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 gc::AllocKind
 NewObjectGCKind(JSContext *cx, js::Class *clasp)
@@ -1783,22 +1756,24 @@ CopyInitializerObject(JSContext *cx, JSO
 
     gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
 #ifdef JS_THREADSAFE
     kind = gc::GetBackgroundAllocKind(kind);
 #endif
     JS_ASSERT(kind == baseobj->getAllocKind());
     JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
 
-    if (!obj || !obj->ensureSlots(cx, baseobj->numSlots()))
+    if (!obj)
         return NULL;
 
     obj->setType(type);
     obj->flags = baseobj->flags;
-    obj->lastProp = baseobj->lastProp;
+
+    if (!obj->setLastProperty(cx, baseobj->lastProperty()))
+        return NULL;
 
     return obj;
 }
 
 inline bool
 DefineConstructorAndPrototype(JSContext *cx, GlobalObject *global,
                               JSProtoKey key, JSObject *ctor, JSObject *proto)
 {
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -116,16 +116,19 @@ bool stopExecution(JSContext *cx, JSScri
 /* Heap has been resized */
 bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
 
 /*
  * Object has been created. |obj| must exist (its class and size are read)
  */
 bool createObject(JSContext *cx, JSObject *obj);
 
+/* Resize events are being tracked. */
+bool objectResizeActive();
+
 /* Object has been resized */
 bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
 
 /*
  * Object is about to be finalized. |obj| must still exist (its class is
  * read)
  */
 bool finalizeObject(JSObject *obj);
@@ -400,16 +403,27 @@ Probes::finalizeObject(JSObject *obj)
     if (ProfilingActive && !ETWFinalizeObject(obj))
         ok = false;
 #endif
 
     return ok;
 }
 
 inline bool
+Probes::objectResizeActive()
+{
+#ifdef MOZ_ETW
+    if (ProfilingActive)
+        return true;
+#endif
+
+    return false;
+}
+
+inline bool
 Probes::resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize)
 {
     bool ok = true;
 
 #ifdef MOZ_ETW
     if (ProfilingActive && !ETWResizeObject(cx, obj, oldSize, newSize))
         ok = false;
 #endif
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -60,17 +60,17 @@ using namespace js::gc;
 static inline const Value &
 GetCall(JSObject *proxy) {
     JS_ASSERT(IsFunctionProxy(proxy));
     return proxy->getSlot(JSSLOT_PROXY_CALL);
 }
 
 static inline Value
 GetConstruct(JSObject *proxy) {
-    if (proxy->numSlots() <= JSSLOT_PROXY_CONSTRUCT)
+    if (proxy->slotSpan() < JSSLOT_PROXY_CONSTRUCT)
         return UndefinedValue();
     return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
 }
 
 static bool
 OperationInProgress(JSContext *cx, JSObject *proxy)
 {
     PendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
@@ -1389,17 +1389,17 @@ js::NewProxyObject(JSContext *cx, ProxyH
      * Eagerly mark properties unknown for proxies, so we don't try to track
      * their properties and so that we don't need to walk the compartment if
      * their prototype changes later.
      */
     if (proto)
         proto->getNewType(cx, NULL, /* markUnknown = */ true);
 
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
-    if (!obj || !obj->ensureInstanceReservedSlots(cx, 0))
+    if (!obj)
         return NULL;
     obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
     obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
     if (fun) {
         obj->setSlot(JSSLOT_PROXY_CALL, call ? ObjectValue(*call) : UndefinedValue());
         if (construct) {
             obj->setSlot(JSSLOT_PROXY_CONSTRUCT, ObjectValue(*construct));
         }
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -748,17 +748,17 @@ JSObject::initRegExp(JSContext *cx, js::
      */
     if (nativeEmpty()) {
         const js::Shape **shapep = &cx->compartment->initialRegExpShape;
         if (!*shapep) {
             *shapep = assignInitialRegExpShape(cx);
             if (!*shapep)
                 return false;
         }
-        setLastProperty(*shapep);
+        setLastPropertyInfallible(*shapep);
         JS_ASSERT(!nativeEmpty());
     }
 
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_LAST_INDEX);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_SOURCE);
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom))->slot() ==
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -64,47 +64,16 @@
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 bool
-JSObject::ensureClassReservedSlotsForEmptyObject(JSContext *cx)
-{
-    JS_ASSERT(nativeEmpty());
-
-    /*
-     * Subtle rule: objects that call JSObject::ensureInstanceReservedSlots
-     * must either:
-     *
-     * (a) never escape anywhere an ad-hoc property could be set on them; or
-     *
-     * (b) protect their instance-reserved slots with shapes, at least a custom
-     * empty shape with the right slotSpan member.
-     *
-     * Block objects are the only objects that fall into category (a). While
-     * Call objects cannot escape, they can grow ad-hoc properties via eval
-     * of a var declaration, or due to a function statement being evaluated,
-     * but they have slots mapped by compiler-created shapes, and thus (b) no
-     * problem predicting first ad-hoc property slot. Bound Function objects
-     * have a custom empty shape.
-     *
-     * (Note that Block, Call, and bound Function objects are the only native
-     * class objects that are allowed to call ensureInstanceReservedSlots.)
-     */
-    uint32 nfixed = JSSLOT_FREE(getClass());
-    if (nfixed > numSlots() && !allocSlots(cx, nfixed))
-        return false;
-
-    return true;
-}
-
-bool
 PropertyTable::init(JSRuntime *rt, Shape *lastProp)
 {
     /*
      * Either we're creating a table for a large scope that was populated
      * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
      * JSOP_SETPROP; or else calloc failed at least once already. In any
      * event, let's try to grow, overallocating to hold at least twice the
      * current population.
@@ -373,36 +342,37 @@ JSObject::getChildProperty(JSContext *cx
         if (child.hasMissingSlot()) {
             uint32 slot;
             if (!allocSlot(cx, &slot))
                 return NULL;
             child.slot_ = slot;
         } else {
             /* Slots can only be allocated out of order on objects in dictionary mode. */
             JS_ASSERT(inDictionaryMode() ||
-                      lastProp->hasMissingSlot() ||
-                      child.slot() == lastProp->slot() + 1);
+                      parent->hasMissingSlot() ||
+                      child.slot() == parent->slot() + 1);
         }
     }
 
     Shape *shape;
 
     if (inDictionaryMode()) {
-        JS_ASSERT(parent == lastProp);
+        JS_ASSERT(parent == lastProperty());
         shape = js_NewGCShape(cx);
         if (!shape)
             return NULL;
-        shape->initDictionaryShape(child, &lastProp);
+        shape->initDictionaryShape(child, &shape_);
     } else {
         shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, child);
         if (!shape)
             return NULL;
         JS_ASSERT(shape->parent == parent);
-        JS_ASSERT_IF(parent != lastProp, parent == lastProp->parent);
-        setLastProperty(shape);
+        JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
+        if (!setLastProperty(cx, shape))
+            return NULL;
     }
 
     updateFlags(shape);
     return shape;
 }
 
 Shape *
 Shape::newDictionaryList(JSContext *cx, Shape **listp)
@@ -446,21 +416,21 @@ JSObject::toDictionaryMode(JSContext *cx
 {
     JS_ASSERT(!inDictionaryMode());
 
     /* We allocate the shapes from cx->compartment, so make sure it's right. */
     JS_ASSERT(compartment() == cx->compartment);
 
     uint32 span = slotSpan();
 
-    if (!Shape::newDictionaryList(cx, &lastProp))
+    if (!Shape::newDictionaryList(cx, &shape_))
         return false;
 
-    JS_ASSERT(lastProp->hasTable());
-    lastProp->base()->setSlotSpan(span);
+    JS_ASSERT(lastProperty()->hasTable());
+    lastProperty()->base()->setSlotSpan(span);
 
     return true;
 }
 
 /*
  * Normalize stub getter and setter values for faster is-stub testing in the
  * SHAPE_CALL_[GS]ETTER macros.
  */
@@ -502,41 +472,41 @@ JSObject::checkShapeConsistency()
         if (throttle < 0)
             throttle = 0;
     }
     if (throttle == 0)
         return;
 
     JS_ASSERT(isNative());
 
-    Shape *shape = lastProp;
+    Shape *shape = lastProperty();
     Shape *prev = NULL;
 
     if (inDictionaryMode()) {
         JS_ASSERT(shape->hasTable());
 
         PropertyTable &table = shape->table();
         for (uint32 fslot = table.freelist; fslot != SHAPE_INVALID_SLOT;
              fslot = getSlot(fslot).toPrivateUint32()) {
             JS_ASSERT(fslot < slotSpan());
         }
 
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
-            JS_ASSERT_IF(shape != lastProp, !shape->hasTable());
+            JS_ASSERT_IF(shape != lastProperty(), !shape->hasTable());
 
             Shape **spp = table.search(shape->propid(), false);
             JS_ASSERT(SHAPE_FETCH(spp) == shape);
         }
 
-        shape = lastProp;
+        shape = lastProperty();
         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
             JS_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
             if (!prev) {
-                JS_ASSERT(shape == lastProp);
-                JS_ASSERT(shape->listp == &lastProp);
+                JS_ASSERT(shape == lastProperty());
+                JS_ASSERT(shape->listp == &shape_);
             } else {
                 JS_ASSERT(shape->listp == &prev->parent);
             }
             prev = shape;
         }
     } else {
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
             if (shape->hasTable()) {
@@ -588,28 +558,28 @@ JSObject::addPropertyInternal(JSContext 
                               Shape **spp, bool allowDictionary)
 {
     JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
 
     PropertyTable *table = NULL;
     if (!inDictionaryMode()) {
         bool stableSlot =
             (slot == SHAPE_INVALID_SLOT) ||
-            lastProp->hasMissingSlot() ||
-            (slot == lastProp->maybeSlot() + 1);
+            lastProperty()->hasMissingSlot() ||
+            (slot == lastProperty()->maybeSlot() + 1);
         JS_ASSERT_IF(!allowDictionary, stableSlot);
         if (allowDictionary &&
-            (!stableSlot || lastProp->entryCount() >= PropertyTree::MAX_HEIGHT)) {
+            (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
             if (!toDictionaryMode(cx))
                 return NULL;
             spp = nativeSearch(cx, id, true);
-            table = &lastProp->table();
+            table = &lastProperty()->table();
         }
-    } else if (lastProp->hasTable()) {
-        table = &lastProp->table();
+    } else if (lastProperty()->hasTable()) {
+        table = &lastProperty()->table();
         if (table->needsToGrow()) {
             if (!table->grow(cx))
                 return NULL;
 
             spp = table->search(id, true);
             JS_ASSERT(!SHAPE_FETCH(spp));
         }
     }
@@ -618,28 +588,28 @@ JSObject::addPropertyInternal(JSContext 
     Shape *shape;
     {
         BaseShape base(getClass(), attrs, getter, setter);
         BaseShape *nbase = BaseShape::lookup(cx, base);
         if (!nbase)
             return NULL;
 
         Shape child(nbase, id, slot, attrs, flags, shortid);
-        shape = getChildProperty(cx, lastProp, child);
+        shape = getChildProperty(cx, lastProperty(), child);
     }
 
     if (shape) {
-        JS_ASSERT(shape == lastProp);
+        JS_ASSERT(shape == lastProperty());
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
             SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
             ++table->entryCount;
 
-            /* Pass the table along to the new lastProp, namely shape. */
+            /* Pass the table along to the new last property, namely shape. */
             JS_ASSERT(&shape->parent->table() == table);
             shape->parent->handoffTableTo(shape);
         }
 
         CHECK_SHAPE_CONSISTENCY(this);
         return shape;
     }
 
@@ -728,17 +698,17 @@ JSObject::putProperty(JSContext *cx, jsi
     if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
         return shape;
 
     /*
      * Overwriting a non-last property requires switching to dictionary mode.
      * The shape tree is shared immutable, and we can't removeProperty and then
      * addPropertyInternal because a failure under add would lose data.
      */
-    if (shape != lastProp && !inDictionaryMode()) {
+    if (shape != lastProperty() && !inDictionaryMode()) {
         if (!toDictionaryMode(cx))
             return NULL;
         spp = nativeSearch(cx, shape->propid());
         shape = SHAPE_FETCH(spp);
     }
 
     JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
 
@@ -748,89 +718,86 @@ JSObject::putProperty(JSContext *cx, jsi
      * has a unique shape (not shared via a shape tree). We can update the
      * shape in place, though after each modification we need to generate a new
      * last property to invalidate shape guards.
      *
      * This is more than an optimization: it is required to preserve for-in
      * enumeration order (see bug 601399).
      */
     if (inDictionaryMode()) {
-        bool updateLast = (shape == lastProp);
+        bool updateLast = (shape == lastProperty());
         if (!generateOwnShape(cx))
             return NULL;
         if (updateLast)
-            shape = lastProp;
+            shape = lastProperty();
 
         /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
         if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
             if (!allocSlot(cx, &slot))
                 return NULL;
         }
 
-        if (shape == lastProp) {
+        if (shape == lastProperty()) {
             uint32 span = shape->base()->slotSpan();
             PropertyTable *table = &shape->base()->table();
             new (shape->base()) BaseShape(nbase, table, span);
         } else {
             shape->base_ = nbase;
         }
 
         shape->slot_ = slot;
         shape->attrs = uint8(attrs);
         shape->flags = flags | Shape::IN_DICTIONARY;
         shape->shortid_ = int16(shortid);
 
         /*
-         * We are done updating shape and lastProp. Now we may need to update
-         * flags and we will need to update objShape, which is no longer "own".
-         * In the last non-dictionary property case in the else clause just
-         * below, getChildProperty handles this for us. First update flags.
+         * We are done updating shape and the last property. Now we may need to
+         * update flags. In the last non-dictionary property case in the else
+         * clause just below, getChildProperty handles this for us. First update
+         * flags.
          */
         updateFlags(shape);
     } else {
         /*
-         * Updating lastProp in a non-dictionary-mode object. Such objects
-         * share their shapes via a tree rooted at a prototype emptyShape, or
-         * perhaps a well-known compartment-wide singleton emptyShape.
+         * Updating the last property in a non-dictionary-mode object. Such
+         * objects share their shapes via a tree rooted at a prototype
+         * emptyShape, or perhaps a well-known compartment-wide singleton
+         * emptyShape.
          *
          * If any shape in the tree has a property hashtable, it is shared and
          * immutable too, therefore we must not update *spp.
          */
         BaseShape base(getClass(), attrs, getter, setter);
         BaseShape *nbase = BaseShape::lookup(cx, base);
         if (!nbase)
             return NULL;
 
-        JS_ASSERT(shape == lastProp);
-        removeLastProperty();
+        JS_ASSERT(shape == lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
         Shape child(nbase, id, slot, attrs, flags, shortid);
+        Shape *newShape = getChildProperty(cx, shape->parent, child);
 
-        Shape *newShape = getChildProperty(cx, lastProp, child);
         if (!newShape) {
-            setLastProperty(shape);
             CHECK_SHAPE_CONSISTENCY(this);
             return NULL;
         }
 
         shape = newShape;
     }
 
     /*
      * Can't fail now, so free the previous incarnation's slot if the new shape
      * has no slot. But we do not need to free oldSlot (and must not, as trying
-     * to will botch an assertion in JSObject::freeSlot) if the new lastProp
-     * (shape here) has a slotSpan that does not cover it.
+     * to will botch an assertion in JSObject::freeSlot) if the new last
+     * property (shape here) has a slotSpan that does not cover it.
      */
     if (hadSlot && !shape->hasSlot()) {
         if (oldSlot < slotSpan())
             freeSlot(cx, oldSlot);
-        else
-            setSlot(oldSlot, UndefinedValue());
         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     }
 
     CHECK_SHAPE_CONSISTENCY(this);
 
     return shape;
 }
 
@@ -881,29 +848,29 @@ bool
 JSObject::removeProperty(JSContext *cx, jsid id)
 {
     Shape **spp = nativeSearch(cx, id);
     Shape *shape = SHAPE_FETCH(spp);
     if (!shape)
         return true;
 
     /* If shape is not the last property added, switch to dictionary mode. */
-    if (shape != lastProp && !inDictionaryMode()) {
+    if (shape != lastProperty() && !inDictionaryMode()) {
         if (!toDictionaryMode(cx))
             return false;
         spp = nativeSearch(cx, shape->propid());
         shape = SHAPE_FETCH(spp);
     }
 
     /*
      * If in dictionary mode, get a new shape for the last property after the
      * removal. We need a fresh shape for all dictionary deletions, even of
-     * lastProp. Otherwise, a shape could replay and caches might return
-     * deleted DictionaryShapes! See bug 595365. Do this before changing the
-     * object or table, so the remaining removal is infallible.
+     * the last property. Otherwise, a shape could replay and caches might
+     * return deleted DictionaryShapes! See bug 595365. Do this before changing
+     * the object or table, so the remaining removal is infallible.
      */
     Shape *spare = NULL;
     if (inDictionaryMode()) {
         spare = js_NewGCShape(cx);
         if (!spare)
             return false;
         PodZero(spare);
     }
@@ -915,103 +882,97 @@ JSObject::removeProperty(JSContext *cx, 
     }
 
     /*
      * A dictionary-mode object owns mutable, unique shapes on a non-circular
      * doubly linked list, hashed by lastProp->table. So we can edit the list
      * and hash in place.
      */
     if (inDictionaryMode()) {
-        PropertyTable &table = lastProp->table();
+        PropertyTable &table = lastProperty()->table();
 
         if (SHAPE_HAD_COLLISION(*spp)) {
             *spp = SHAPE_REMOVED;
             ++table.removedCount;
             --table.entryCount;
         } else {
             *spp = NULL;
             --table.entryCount;
 
 #ifdef DEBUG
             /*
              * Check the consistency of the table but limit the number of
              * checks not to alter significantly the complexity of the
              * delete in debug builds, see bug 534493.
              */
-            const Shape *aprop = lastProp;
+            const Shape *aprop = lastProperty();
             for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
                 JS_ASSERT_IF(aprop != shape, nativeContains(cx, *aprop));
 #endif
         }
 
         /* Remove shape from its non-circular doubly linked list. */
-        Shape *oldLastProp = lastProp;
+        Shape *oldLastProp = lastProperty();
         shape->removeFromDictionary(this);
 
-        /* Hand off table from old to new lastProp. */
-        oldLastProp->handoffTableTo(lastProp);
+        /* Hand off table from the old to new last property. */
+        oldLastProp->handoffTableTo(lastProperty());
 
         /* Generate a new shape for the object, infallibly. */
         JS_ALWAYS_TRUE(generateOwnShape(cx, spare));
 
         /* Consider shrinking table if its load factor is <= .25. */
         uint32 size = table.capacity();
         if (size > PropertyTable::MIN_SIZE && table.entryCount <= size >> 2)
             (void) table.change(-1, 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 hashify the exact table for the new property lineage.
+         * need do is retract the last property and we'll either get or else
+         * lazily make via a later hashify the exact table for the new property
+         * lineage.
          */
-        JS_ASSERT(shape == lastProp);
-        removeLastProperty();
-    }
-
-    /* Consider shrinking object slots if 25% or more are unused. */
-    if (hasSlotsArray()) {
-        JS_ASSERT(slotSpan() <= numSlots());
-        if ((slotSpan() + (slotSpan() >> 2)) < numSlots())
-            shrinkSlots(cx, slotSpan());
+        JS_ASSERT(shape == lastProperty());
+        JS_ALWAYS_TRUE(setLastProperty(cx, shape->parent));
     }
 
     CHECK_SHAPE_CONSISTENCY(this);
     return true;
 }
 
 void
 JSObject::clear(JSContext *cx)
 {
-    Shape *shape = lastProp;
+    Shape *shape = lastProperty();
     JS_ASSERT(inDictionaryMode() == shape->inDictionary());
 
     while (shape->parent) {
         shape = shape->parent;
         JS_ASSERT(inDictionaryMode() == shape->inDictionary());
     }
     JS_ASSERT(shape->isEmptyShape());
 
     if (inDictionaryMode())
-        shape->listp = &lastProp;
+        shape->listp = &shape_;
 
-    setMap(shape);
+    JS_ALWAYS_TRUE(setLastProperty(cx, shape));
 
     LeaveTraceIfGlobalObject(cx, this);
     JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     CHECK_SHAPE_CONSISTENCY(this);
 }
 
 void
 JSObject::rollbackProperties(JSContext *cx, uint32 slotSpan)
 {
     /* Remove properties from this object until it has a matching slot span. */
-    JS_ASSERT(!inDictionaryMode() && !hasSlotsArray() && slotSpan <= this->slotSpan());
+    JS_ASSERT(!inDictionaryMode() && slotSpan <= this->slotSpan());
     while (this->slotSpan() != slotSpan) {
-        JS_ASSERT(lastProp->hasSlot() && getSlot(lastProp->slot()).isUndefined());
-        removeLastProperty();
+        JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
+        JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->parent));
     }
 }
 
 bool
 JSObject::generateOwnShape(JSContext *cx, Shape *newShape)
 {
 #ifdef JS_TRACER
     JS_ASSERT_IF(!parent && JS_ON_TRACE(cx), JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit);
@@ -1030,21 +991,21 @@ JSObject::generateOwnShape(JSContext *cx
         return false;
 
     if (!newShape) {
         newShape = js_NewGCShape(cx);
         if (!newShape)
             return false;
     }
 
-    PropertyTable &table = lastProp->table();
-    Shape **spp = lastProp->isEmptyShape() ? NULL : table.search(lastProp->maybePropid(), false);
+    PropertyTable &table = lastProperty()->table();
+    Shape **spp = lastProperty()->isEmptyShape() ? NULL : table.search(lastProperty()->maybePropid(), false);
 
-    Shape *oldShape = lastProp;
-    newShape->initDictionaryShape(*oldShape, &lastProp);
+    Shape *oldShape = lastProperty();
+    newShape->initDictionaryShape(*oldShape, &shape_);
 
     JS_ASSERT(newShape->parent == oldShape);
     oldShape->removeFromDictionary(this);
 
     oldShape->handoffTableTo(newShape);
 
     if (spp)
         SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
@@ -1194,46 +1155,47 @@ void
 BaseShape::finalize(JSContext *cx, bool background)
 {
     if (table_) {
         cx->delete_(table_);
         table_ = NULL;
     }
 }
 
-/* static */ bool
-Shape::setExtensibleParents(JSContext *cx, Shape **pshape)
+/* static */ Shape *
+Shape::setExtensibleParents(JSContext *cx, Shape *shape)
 {
-    Shape *shape = *pshape;
     JS_ASSERT(!shape->inDictionary());
 
     BaseShape base(shape->getClass(), shape->attrs, shape->getter(), shape->setter());
     base.flags |= BaseShape::EXTENSIBLE_PARENTS;
     BaseShape *nbase = BaseShape::lookup(cx, base);
     if (!nbase)
-        return false;
+        return NULL;
 
     Shape child(nbase, shape->maybePropid(), shape->maybeSlot(),
                 shape->attrs, shape->flags, shape->maybeShortid());
     Shape *newShape;
     if (shape->parent) {
         newShape = JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, child);
         if (!newShape)
-            return false;
+            return NULL;
     } else {
         newShape = js_NewGCShape(cx);
         if (!newShape)
-            return false;
+            return NULL;
         new (newShape) Shape(child);
     }
 
-    *pshape = newShape;
-
-    return true;
+    return newShape;
 }
 
 bool
 Bindings::setExtensibleParents(JSContext *cx)
 {
     if (!ensureShape(cx))
         return false;
-    return Shape::setExtensibleParents(cx, &lastBinding);
+    Shape *shape = Shape::setExtensibleParents(cx, lastBinding);
+    if (!shape)
+        return false;
+    lastBinding = shape;
+    return true;
 }
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -504,17 +504,17 @@ struct Shape : public js::gc::Cell
         js::KidsPointer kids;   /* null, single child, or a tagged ptr
                                    to many-kids data structure */
         js::Shape **listp;      /* dictionary list starting at lastProp
                                    has a double-indirect back pointer,
                                    either to shape->parent if not last,
                                    else to obj->lastProp */
     };
 
-    static inline js::Shape **search(JSContext *cx, js::Shape **startp, jsid id,
+    static inline js::Shape **search(JSContext *cx, js::Shape *start, jsid id,
                                      bool adding = false);
     static js::Shape *newDictionaryList(JSContext *cx, js::Shape **listp);
 
     inline void removeFromDictionary(JSObject *obj);
     inline void insertIntoDictionary(js::Shape **dictp);
 
     inline void initDictionaryShape(const js::Shape &child, js::Shape **dictp);
 
@@ -710,16 +710,22 @@ struct Shape : public js::gc::Cell
     uint32 slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
     uint32 maybeSlot() const { return hasMissingSlot() ? SHAPE_INVALID_SLOT : slot_; }
 
     bool isEmptyShape() const {
         JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
         return JSID_IS_EMPTY(propid_);
     }
 
+    uint32 slotSpan() const {
+        JS_ASSERT(!inDictionary());
+        uint32 free = JSSLOT_FREE(getClass());
+        return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
+    }
+
     jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); }
     jsid maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
 
     int16 shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
     int16 maybeShortid() const { return shortid_; }
 
     /*
      * If SHORTID is set in shape->flags, we use shape->shortid rather
@@ -795,17 +801,17 @@ struct Shape : public js::gc::Cell
      * extensible, and the other sorts of objects that appear in the scope
      * chains ('with' blocks, say) are not CacheableNonGlobalScopes.
      *
      * If the hasExtensibleParents flag is set for the last property in a
      * script's bindings or a compiler-generated Block object, then created
      * Call or Block objects need unique shapes. If the flag is clear, then we
      * can use lastBinding's shape.
      */
-    static bool setExtensibleParents(JSContext *cx, Shape **pshape);
+    static Shape * setExtensibleParents(JSContext *cx, Shape *shape);
     bool extensibleParents() const { return !!(base()->flags & BaseShape::EXTENSIBLE_PARENTS); }
 
     uint32 entryCount() const {
         if (hasTable())
             return table().entryCount;
 
         const js::Shape *shape = this;
         uint32 count = 0;
@@ -885,59 +891,58 @@ struct EmptyShape : public js::Shape
     ((js::Shape *) (jsuword(shape) & ~SHAPE_COLLISION))
 
 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape)                          \
     (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp))))
 
 namespace js {
 
 JS_ALWAYS_INLINE js::Shape **
-Shape::search(JSContext *cx, js::Shape **startp, jsid id, bool adding)
+Shape::search(JSContext *cx, js::Shape *start, jsid id, bool adding)
 {
-    js::Shape *start = *startp;
     if (start->hasTable())
         return start->table().search(id, adding);
 
     if (start->numLinearSearches == PropertyTable::MAX_LINEAR_SEARCHES) {
         if (start->hashify(cx))
             return start->table().search(id, adding);
         /* OOM!  Don't increment numLinearSearches, to keep hasTable() false. */
         JS_ASSERT(!start->hasTable());
     } else {
         JS_ASSERT(start->numLinearSearches < PropertyTable::MAX_LINEAR_SEARCHES);
         start->numLinearSearches++;
     }
 
     /*
      * Not enough searches done so far to justify hashing: search linearly
-     * from *startp.
+     * from start.
      *
      * We don't use a Range here, or stop at null parent (the empty shape
      * at the end), to avoid an extra load per iteration just to save a
      * load and id test at the end (when missing).
      */
     js::Shape **spp;
-    for (spp = startp; js::Shape *shape = *spp; spp = &shape->parent) {
+    for (spp = &start; js::Shape *shape = *spp; spp = &shape->parent) {
         if (shape->maybePropid() == id)
             return spp;
     }
     return spp;
 }
 
 } // namespace js
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
 inline js::Class *
 JSObject::getClass() const
 {
-    return lastProp->getClass();
+    return lastProperty()->getClass();
 }
 
 inline JSClass *
 JSObject::getJSClass() const
 {
     return Jsvalify(getClass());
 }
 
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -111,33 +111,35 @@ js::types::TypeObject::canProvideEmptySh
 inline void
 JSObject::updateFlags(const js::Shape *shape, bool isDefinitelyAtom)
 {
     jsuint index;
     if (!isDefinitelyAtom && js_IdIsIndex(shape->propid(), &index))
         setIndexed();
 }
 
-inline void
+inline bool
 JSObject::extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom)
 {
-    setLastProperty(shape);
+    if (!setLastProperty(cx, shape))
+        return false;
     updateFlags(shape, isDefinitelyAtom);
+    return true;
 }
 
 namespace js {
 
 inline bool
 StringObject::init(JSContext *cx, JSString *str)
 {
     JS_ASSERT(nativeEmpty());
 
     const Shape **shapep = &cx->compartment->initialStringShape;
     if (*shapep) {
-        setLastProperty(*shapep);
+        setLastPropertyInfallible(*shapep);
     } else {
         *shapep = assignInitialShape(cx);
         if (!*shapep)
             return false;
     }
     JS_ASSERT(*shapep == lastProperty());
     JS_ASSERT(!nativeEmpty());
     JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot() == LENGTH_SLOT);
@@ -258,18 +260,18 @@ Shape::set(JSContext* cx, JSObject* obj,
 
 inline void
 Shape::removeFromDictionary(JSObject *obj)
 {
     JS_ASSERT(inDictionary());
     JS_ASSERT(obj->inDictionaryMode());
     JS_ASSERT(listp);
 
-    JS_ASSERT(obj->lastProp->inDictionary());
-    JS_ASSERT(obj->lastProp->listp == &obj->lastProp);
+    JS_ASSERT(obj->shape_->inDictionary());
+    JS_ASSERT(obj->shape_->listp == &obj->shape_);
 
     if (parent)
         parent->listp = listp;
     *listp = parent;
     listp = NULL;
 }
 
 inline void
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -84,18 +84,17 @@ namespace js {
 
 BindingKind
 Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
 {
     if (!lastBinding)
         return NONE;
 
     Shape *shape =
-        SHAPE_FETCH(Shape::search(cx, const_cast<Shape **>(&lastBinding),
-                    ATOM_TO_JSID(name)));
+        SHAPE_FETCH(Shape::search(cx, lastBinding, ATOM_TO_JSID(name)));
     if (!shape)
         return NONE;
 
     if (indexp)
         *indexp = shape->shortid();
 
     if (shape->getter() == GetCallArg)
         return ARGUMENT;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2682,17 +2682,19 @@ TraceRecorder::nativeGlobalOffset(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() - globalObj->numFixedSlots());
+    JS_NOT_REACHED("FIXME");
+    return false;
+    //return (size_t(p - globalObj->slots) < globalObj->numSlots() - globalObj->numFixedSlots());
 }
 
 bool
 TraceRecorder::isVoidPtrGlobal(const void* p) const
 {
     return isGlobal((const Value *)p);
 }
 
@@ -3862,17 +3864,18 @@ TraceRecorder::isValidSlot(JSObject *obj
     return true;
 }
 
 /* Lazily import a global slot if we don't already have it in the tracker. */
 JS_REQUIRES_STACK void
 TraceRecorder::importGlobalSlot(unsigned slot)
 {
     JS_ASSERT(slot == uint16(slot));
-    JS_ASSERT(globalObj->numSlots() <= MAX_GLOBAL_SLOTS);
+    JS_NOT_REACHED("FIXME");
+    //JS_ASSERT(globalObj->numSlots() <= MAX_GLOBAL_SLOTS);
 
     const Value* vp = &globalObj->getSlot(slot);
     JS_ASSERT(!known(vp));
 
     /* Add the slot to the list of interned global slots. */
     JSValueType type;
     int index = tree->globalSlots->offsetOf(uint16(slot));
     if (index == -1) {
@@ -3895,18 +3898,19 @@ JS_REQUIRES_STACK bool
 TraceRecorder::lazilyImportGlobalSlot(unsigned slot)
 {
     if (slot != uint16(slot)) /* we use a table of 16-bit ints, bail out if that's not enough */
         return false;
     /*
      * If the global object grows too large, alloca in ExecuteTree might fail,
      * so abort tracing on global objects with unreasonably many slots.
      */
-    if (globalObj->numSlots() > MAX_GLOBAL_SLOTS)
-        return false;
+    JS_NOT_REACHED("FIXME");
+    //if (globalObj->numSlots() > MAX_GLOBAL_SLOTS)
+    //    return false;
     const Value* vp = &globalObj->getSlot(slot);
     if (known(vp))
         return true; /* we already have it */
     importGlobalSlot(slot);
     return true;
 }
 
 /* Write back a value onto the stack or global frames. */
@@ -4097,31 +4101,34 @@ TraceRecorder::known(JSObject** p)
 /*
  * The slots 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()
 {
+    JS_NOT_REACHED("FIXME");
+    /*
     debug_only_print0(LC_TMTracer, "globalObj->slots relocated, updating tracker\n");
     const Value* src = global_slots;
     const Value* dst = globalObj->getRawSlots();
     jsuint length = globalObj->capacity;
     LIns** map = (LIns**)alloca(sizeof(LIns*) * length);
     for (jsuint n = 0; n < length; ++n) {
         const Value *slot = globalObj->getRawSlot(n, src);
         map[n] = tracker.get(slot);
         tracker.set(slot, NULL);
     }
     for (jsuint n = 0; n < length; ++n) {
         const Value *slot = globalObj->getRawSlot(n, dst);
         tracker.set(slot, map[n]);
     }
     global_slots = globalObj->getRawSlots();
+    */
 }
 
 /* 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:
@@ -9798,17 +9805,16 @@ void
 TraceRecorder::stobj_set_slot(JSObject *obj, LIns* obj_ins, unsigned slot, LIns*& slots_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->isFixedSlot(slot)) {
-        JS_ASSERT(slot < obj->numSlots());
         stobj_set_fslot(obj_ins, slot, v, v_ins);
     } else {
         stobj_set_dslot(obj_ins, obj->dynamicSlotIndex(slot), slots_ins, v, v_ins);
     }
 }
 
 LIns*
 TraceRecorder::unbox_slot(JSObject *obj, LIns *obj_ins, uint32 slot, VMSideExit *exit)
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1631,17 +1631,19 @@ class TraceRecorder
 
     bool globalSetExpected(unsigned slot) {
         unsigned *pi = Find(pendingGlobalSlotsToSet, slot);
         if (pi == pendingGlobalSlotsToSet.end()) {
             /*
              * Do slot arithmetic manually to avoid getSlotRef assertions which
              * do not need to be satisfied for this purpose.
              */
-            const Value *vp = globalObj->getRawSlot(slot, globalObj->getRawSlots());
+            //const Value *vp = globalObj->getRawSlot(slot, globalObj->getRawSlots());
+            JS_NOT_REACHED("FIXME");
+            const Value *vp = NULL;
 
             /* If this global is definitely being tracked, then the write is unexpected. */
             if (tracker.has(vp))
                 return false;
             
             /*
              * Otherwise, only abort if the global is not present in the
              * import typemap. Just deep aborting false here is not acceptable,
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -156,35 +156,39 @@ ArrayBuffer::class_constructor(JSContext
 
 bool
 JSObject::allocateArrayBufferSlots(JSContext *cx, uint32 size)
 {
     /*
      * ArrayBuffer objects delegate added properties to another JSObject, so
      * their internal layout can use the object's fixed slots for storage.
      */
-    JS_ASSERT(isArrayBuffer() && !hasSlotsArray());
+    JS_ASSERT(isArrayBuffer() && !hasDynamicSlots() && !hasDynamicElements());
+
+    JS_STATIC_ASSERT(sizeof(ObjectElements) == 2 * sizeof(js::Value));
 
-    uint32 bytes = size + sizeof(Value);
-    if (size > sizeof(Value) * ARRAYBUFFER_RESERVED_SLOTS - sizeof(Value) ) {
-        Value *tmpslots = (Value *)cx->calloc_(bytes);
-        if (!tmpslots)
+    if (size > sizeof(Value) * (ARRAYBUFFER_RESERVED_SLOTS - 2) ) {
+        ObjectElements *tmpheader = (ObjectElements *)cx->calloc_(size + sizeof(ObjectElements));
+        if (!tmpheader)
             return false;
-        slots = tmpslots;
-        /*
-         * Note that |bytes| may not be a multiple of |sizeof(Value)|, so
-         * |capacity * sizeof(Value)| may underestimate the size by up to
-         * |sizeof(Value) - 1| bytes.
-         */
-        capacity = bytes / sizeof(Value);
+        elements = tmpheader->elements();
+        tmpheader->length = size;
     } else {
-        slots = fixedSlots();
-        memset(slots, 0, bytes);
+        elements = fixedElements();
+        memset(fixedSlots(), 0, size + sizeof(ObjectElements));
     }
-    *((uint32*)slots) = size;
+    getElementsHeader()->length = size;
+
+    /*
+     * Note that |bytes| may not be a multiple of |sizeof(Value)|, so
+     * |capacity * sizeof(Value)| may underestimate the size by up to
+     * |sizeof(Value) - 1| bytes.
+     */
+    getElementsHeader()->capacity = size / sizeof(Value);
+
     return true;
 }
 
 static JSObject *
 DelegateObject(JSContext *cx, JSObject *obj)
 {
     if (!obj->getPrivate()) {
         JSObject *delegate = NewNonFunction<WithProto::Given>(cx, &ObjectClass, obj->getProto(), NULL);
@@ -208,18 +212,20 @@ ArrayBuffer::create(JSContext *cx, int32
          * can fix.
          */
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
         return NULL;
     }
 
     JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
 
-    if (!InitScopeForNonNativeObject(cx, obj, &ArrayBufferClass))
-        return NULL;
+    js::EmptyShape *empty = BaseShape::lookupEmpty(cx, &ArrayBufferClass);
+    if (!empty)
+        return false;
+    obj->setLastPropertyInfallible(empty);
 
     /*
      * The first 8 bytes hold the length.
      * The rest of it is a flat data store for the array buffer.
      */
     if (!obj->allocateArrayBufferSlots(cx, nbytes))
         return NULL;
 
@@ -1282,18 +1288,20 @@ class TypedArrayTemplate
         DebugOnly<uint32> bufferByteLength = getBuffer(obj)->arrayBufferByteLength();
         JS_ASSERT(bufferByteLength - getByteOffset(obj) >= getByteLength(obj));
         JS_ASSERT(getByteOffset(obj) <= bufferByteLength);
         JS_ASSERT(getBuffer(obj)->arrayBufferDataOffset() <= getDataOffset(obj));
         JS_ASSERT(getDataOffset(obj) <= offsetData(obj, bufferByteLength));
 
         JS_ASSERT(obj->getClass() == slowClass());
 
-        if (!InitScopeForNonNativeObject(cx, obj, fastClass()))
-            return NULL;
+        js::EmptyShape *empty = BaseShape::lookupEmpty(cx, fastClass());
+        if (!empty)
+            return false;
+        obj->setLastPropertyInfallible(empty);
 
         // FIXME Bug 599008: make it ok to call preventExtensions here.
         obj->flags |= JSObject::NOT_EXTENSIBLE;
 
         return obj;
     }
 
     /*
--- a/js/src/jstypedarrayinlines.h
+++ b/js/src/jstypedarrayinlines.h
@@ -42,24 +42,23 @@
 
 #include "jsapi.h"
 #include "jsobj.h"
 
 inline uint32
 JSObject::arrayBufferByteLength()
 {
     JS_ASSERT(isArrayBuffer());
-    return *((uint32*) slots);
+    return getElementsHeader()->length;
 }
 
 inline uint8 *
 JSObject::arrayBufferDataOffset()
 {
-    uint64 *base = ((uint64*)slots) + 1;
-    return (uint8*) base;
+    return (uint8 *) elements;
 }
 
 namespace js {
 
 static inline int32
 ClampIntForUint8Array(int32 x)
 {
     if (x < 0)
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -181,21 +181,21 @@ static const JSC::MacroAssembler::Regist
         return differenceBetween(startLabel, l);
     }
 
     void loadPtrFromImm(void *ptr, RegisterID reg) {
         loadPtr(ptr, reg);
     }
 
     void loadShape(RegisterID obj, RegisterID shape) {
-        loadPtr(Address(obj, offsetof(JSObject, lastProp)), shape);
+        loadPtr(Address(obj, JSObject::offsetOfShape()), shape);
     }
 
     Jump guardShape(RegisterID objReg, JSObject *obj) {
-        return branchPtr(NotEqual, Address(objReg, offsetof(JSObject, lastProp)),
+        return branchPtr(NotEqual, Address(objReg, JSObject::offsetOfShape()),
                          ImmPtr(obj->lastProperty()));
     }
 
     /*
      * Finds and returns the address of a known object and slot.
      */
     Address objSlotRef(JSObject *obj, RegisterID reg, uint32 slot) {
         move(ImmPtr(obj), reg);
@@ -746,44 +746,45 @@ static const JSC::MacroAssembler::Regist
         }
     }
 
     struct FastArrayLoadFails {
         Jump rangeCheck;
         Jump holeCheck;
     };
 
-    // Guard an array's capacity, length or initialized length.
-    Jump guardArrayExtent(uint32 offset, RegisterID objReg, const Int32Key &key, Condition cond) {
-        Address initlen(objReg, offset);
+    // Guard an extent (capacity, length or initialized length) on an array or typed array.
+    Jump guardArrayExtent(int offset, RegisterID reg,
+                          const Int32Key &key, Condition cond) {
+        Address extent(reg, offset);
         if (key.isConstant()) {
             JS_ASSERT(key.index() >= 0);
-            return branch32(cond, initlen, Imm32(key.index()));
+            return branch32(cond, extent, Imm32(key.index()));
         }
-        return branch32(cond, initlen, key.reg());
+        return branch32(cond, extent, key.reg());
     }
 
     // Load a jsval from an array slot, given a key. |objReg| is clobbered.
     FastArrayLoadFails fastArrayLoad(RegisterID objReg, const Int32Key &key,
                                      RegisterID typeReg, RegisterID dataReg) {
         JS_ASSERT(objReg != typeReg);
 
+        RegisterID elementsReg = objReg;
+        loadPtr(Address(objReg, JSObject::offsetOfElements()), elementsReg);
+
         FastArrayLoadFails fails;
-        fails.rangeCheck = guardArrayExtent(offsetof(JSObject, initializedLength),
+        fails.rangeCheck = guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
                                             objReg, key, BelowOrEqual);
 
-        RegisterID dslotsReg = objReg;
-        loadPtr(Address(objReg, JSObject::offsetOfSlots()), dslotsReg);
-
         // Load the slot out of the array.
         if (key.isConstant()) {
-            Address slot(objReg, key.index() * sizeof(Value));
+            Address slot(elementsReg, key.index() * sizeof(Value));
             fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg);
         } else {
-            BaseIndex slot(objReg, key.reg(), JSVAL_SCALE);
+            BaseIndex slot(elementsReg, key.reg(), JSVAL_SCALE);
             fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg);
         }
 
         return fails;
     }
 
     void storeKey(const Int32Key &key, Address address) {
         if (key.isConstant())
@@ -809,27 +810,27 @@ static const JSC::MacroAssembler::Regist
         move(Imm32(fun->nargs), reg);
         overflowArgs.linkTo(label(), this);
         lshiftPtr(Imm32(3), reg);
         negPtr(reg);
         addPtr(JSFrameReg, reg);
     }
 
     void loadObjClass(RegisterID obj, RegisterID dest) {
-        loadPtr(Address(obj, offsetof(JSObject, lastProp)), dest);
+        loadPtr(Address(obj, JSObject::offsetOfShape()), dest);
         loadPtr(Address(dest, Shape::offsetOfBase()), dest);
         loadPtr(Address(dest, BaseShape::offsetOfClass()), dest);
     }
 
     Jump testClass(Condition cond, RegisterID claspReg, js::Class *clasp) {
         return branchPtr(cond, claspReg, ImmPtr(clasp));
     }
 
     Jump testObjClass(Condition cond, RegisterID obj, RegisterID temp, js::Class *clasp) {
-        loadPtr(Address(obj, offsetof(JSObject, lastProp)), temp);
+        loadPtr(Address(obj, JSObject::offsetOfShape()), temp);
         loadPtr(Address(temp, Shape::offsetOfBase()), temp);
         return branchPtr(cond, Address(temp, BaseShape::offsetOfClass()), ImmPtr(clasp));
     }
 
     Jump testFunction(Condition cond, RegisterID fun, RegisterID temp) {
         return testObjClass(cond, fun, temp, &js::FunctionClass);
     }
 
@@ -1245,17 +1246,18 @@ static const JSC::MacroAssembler::Regist
     Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject)
     {
         gc::AllocKind allocKind = templateObject->getAllocKind();
 
         JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
         int thingSize = (int)gc::Arena::thingSize(allocKind);
 
         JS_ASSERT(cx->typeInferenceEnabled());
-        JS_ASSERT(!templateObject->hasSlotsArray());
+        JS_ASSERT(!templateObject->hasDynamicSlots());
+        JS_ASSERT(!templateObject->hasDynamicElements());
 
 #ifdef JS_GC_ZEAL
         if (cx->runtime->needZealousGC())
             return jump();
 #endif
 
         /*
          * Inline FreeSpan::allocate. Only the case where the current freelist
@@ -1275,42 +1277,51 @@ static const JSC::MacroAssembler::Regist
          * everything is infallible. Note that this bakes GC thing pointers
          * into the code without explicitly pinning them. With type inference
          * enabled, JIT code is collected on GC except when analysis or
          * compilation is active, in which case type objects won't be collected
          * but other things may be. The shape held by templateObject *must* be
          * pinned against GC either by the script or by some type object.
          */
 
+        int elementsOffset = JSObject::offsetOfFixedElements();
+
         /*
-         * Write out the slots pointer before readjusting the result register,
+         * Write out the elements pointer before readjusting the result register,
          * as for dense arrays we will need to get the address of the fixed
-         * slots first.
+         * elements first.
          */
-        JS_ASSERT(!templateObject->initializedLength);
         if (templateObject->isDenseArray()) {
-            addPtr(Imm32(-thingSize + sizeof(JSObject)), result);
-            storePtr(result, Address(result, -(int)sizeof(JSObject) + JSObject::offsetOfSlots()));
-            addPtr(Imm32(-(int)sizeof(JSObject)), result);
+            JS_ASSERT(!templateObject->getDenseArrayInitializedLength());
+            addPtr(Imm32(-thingSize + elementsOffset), result);
+            storePtr(result, Address(result, -elementsOffset + JSObject::offsetOfElements()));
+            addPtr(Imm32(-elementsOffset), result);
         } else {
             addPtr(Imm32(-thingSize), result);
-            storePtr(ImmPtr(NULL), Address(result, JSObject::offsetOfSlots()));
+            storePtr(ImmPtr(emptyObjectElements), Address(result, JSObject::offsetOfElements()));
         }
 
-        storePtr(ImmPtr(templateObject->lastProp), Address(result, offsetof(JSObject, lastProp)));
+        storePtr(ImmPtr(templateObject->lastProperty()), Address(result, JSObject::offsetOfShape()));
+        storePtr(ImmPtr(templateObject->type()), Address(result, JSObject::offsetOfType()));
         store32(Imm32(templateObject->flags), Address(result, offsetof(JSObject, flags)));
-        storePtr(ImmPtr((void *) templateObject->initializedLength), Address(result, offsetof(JSObject, initializedLength)));
+        storePtr(ImmPtr(NULL), Address(result, JSObject::offsetOfSlots()));
         storePtr(ImmPtr(templateObject->parent), Address(result, offsetof(JSObject, parent)));
         storePtr(ImmPtr(templateObject->privateData), Address(result, offsetof(JSObject, privateData)));
-        storePtr(ImmPtr((void *) templateObject->capacity), Address(result, offsetof(JSObject, capacity)));
-        storePtr(ImmPtr(templateObject->type()), Address(result, JSObject::offsetOfType()));
 
-        /* Fixed slots of non-array objects are required to be initialized. */
-        if (!templateObject->isDenseArray()) {
-            for (unsigned i = 0; i < templateObject->numFixedSlots(); i++)
+        if (templateObject->isDenseArray()) {
+            /* Fill in the elements header. */
+            store32(Imm32(templateObject->getDenseArrayCapacity()),
+                    Address(result, elementsOffset + ObjectElements::offsetOfCapacity()));
+            store32(Imm32(templateObject->getDenseArrayInitializedLength()),
+                    Address(result, elementsOffset + ObjectElements::offsetOfInitializedLength()));
+            store32(Imm32(templateObject->getArrayLength()),
+                    Address(result, elementsOffset + ObjectElements::offsetOfLength()));
+        } else {
+            /* Non-array object slots need to be initialized up to the slot span. */
+            for (unsigned i = 0; i < templateObject->slotSpan(); i++)
                 storeValue(UndefinedValue(), Address(result, JSObject::getFixedSlotOffset(i)));
         }
 
         return jump;
     }
 
     /* Add the value stored in 'value' to the accumulator 'counter'. */
     void addCounter(const double *value, double *counter, RegisterID scratch)
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -4290,19 +4290,22 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
             if (!isObject) {
                 Jump notObject = frame.testObject(Assembler::NotEqual, top);
                 stubcc.linkExit(notObject, Uses(1));
                 stubcc.leave();
                 OOL_STUBCALL(stubs::GetProp, rejoin);
                 if (rejoin == REJOIN_GETTER)
                     testPushedType(rejoin, -1);
             }
+            RegisterID result = frame.allocReg();
             RegisterID reg = frame.tempRegForData(top);
             frame.pop();
-            frame.pushWord(Address(reg, offsetof(JSObject, privateData)), JSVAL_TYPE_INT32);
+            masm.loadPtr(Address(reg, JSObject::offsetOfElements()), result);
+            masm.load32(Address(result, ObjectElements::offsetOfLength()), result);
+            frame.pushTypedPayload(JSVAL_TYPE_INT32, result);
             if (!isObject)
                 stubcc.rejoin(Changes(1));
             return true;
         }
 
         /*
          * Check if we're accessing the 'length' property of a typed array.
          * The typed array length always fits in an int32.
@@ -4469,17 +4472,17 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(usePropCache ? ic::GetProp : ic::GetPropNoCache, rejoin);
     CHECK_OOL_SPACE();
     if (rejoin == REJOIN_GETTER)
         testPushedType(rejoin, -1);
 
     /* Load the base slot address. */
-    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
+    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, JSObject::offsetOfSlots()),
                                                                objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
     frame.pop();
 
     Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg);
     pic.fastPathRejoin = masm.label();
@@ -4586,17 +4589,17 @@ mjit::Compiler::jsop_callprop_generic(JS
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(ic::CallProp, REJOIN_FALLTHROUGH);
     CHECK_OOL_SPACE();
 
     testPushedType(REJOIN_FALLTHROUGH, -1);
 
     /* Load the base slot address. */
-    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
+    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, JSObject::offsetOfSlots()),
                                                                objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 
     Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg);
     pic.fastPathRejoin = masm.label();
 
@@ -4737,17 +4740,17 @@ mjit::Compiler::jsop_callprop_obj(JSAtom
     stubcc.leave();
     passICAddress(&pic);
     pic.slowPathCall = OOL_STUBCALL(ic::CallProp, REJOIN_FALLTHROUGH);
     CHECK_OOL_SPACE();
 
     testPushedType(REJOIN_FALLTHROUGH, -1);
 
     /* Load the base slot address. */
-    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
+    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, JSObject::offsetOfSlots()),
                                                                objReg);
 
     /* Copy the slot value to the expression stack. */
     Address slot(objReg, 1 << 24);
 
     Label fastValueLoad = masm.loadValueWithAddressOffsetPatch(slot, shapeReg, objReg);
 
     pic.fastPathRejoin = masm.label();
@@ -5245,17 +5248,17 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
 
         stubcc.leave();
         passICAddress(&pic);
         pic.slowPathCall = OOL_STUBCALL(ic::SetProp, REJOIN_FALLTHROUGH);
         CHECK_OOL_SPACE();
     }
 
     /* Load dslots. */
-    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, offsetof(JSObject, slots)),
+    Label dslotsLoadLabel = masm.loadPtrWithPatchToLEA(Address(objReg, JSObject::offsetOfSlots()),
                                                        objReg);
 
     /* Store RHS into object slot. */
     Address slot(objReg, 1 << 24);
     DataLabel32 inlineValueStore = masm.storeValueWithAddressOffsetPatch(vr, slot);
     pic.fastPathRejoin = masm.label();
 
     frame.freeReg(objReg);
@@ -5980,17 +5983,17 @@ mjit::Compiler::jsop_getgname(uint32 ind
     ic.fastPathStart = masm.label();
     if (fe->isConstant()) {
         JSObject *obj = &fe->getValue().toObject();
         frame.pop();
         JS_ASSERT(obj->isNative());
 
         objReg = frame.allocReg();
 
-        masm.loadPtrFromImm(&obj->lastProp, objReg);
+        masm.loadPtrFromImm(obj->addressOfShape(), objReg);
         shapeGuard = masm.branchPtrWithPatch(Assembler::NotEqual, objReg,
                                              ic.shape, ImmPtr(NULL));
         masm.move(ImmPtr(obj), objReg);
     } else {
         objReg = frame.ownRegForData(fe);
         frame.pop();
         RegisterID reg = frame.allocReg();
 
@@ -6007,17 +6010,17 @@ mjit::Compiler::jsop_getgname(uint32 ind
 
     CHECK_IC_SPACE();
 
     testPushedType(REJOIN_GETTER, 0);
 
     /* Garbage value. */
     uint32 slot = 1 << 24;
 
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
     Address address(objReg, slot);
 
     /* Allocate any register other than objReg. */
     RegisterID treg = frame.allocReg();
     /* After dreg is loaded, it's safe to clobber objReg. */
     RegisterID dreg = objReg;
 
     ic.load = masm.loadValueWithAddressOffsetPatch(address, treg, dreg);
@@ -6209,17 +6212,17 @@ mjit::Compiler::jsop_setgname(JSAtom *at
     if (objFe->isConstant()) {
         JSObject *obj = &objFe->getValue().toObject();
         JS_ASSERT(obj->isNative());
 
         ic.objReg = frame.allocReg();
         ic.shapeReg = ic.objReg;
         ic.objConst = true;
 
-        masm.loadPtrFromImm(&obj->lastProp, ic.shapeReg);
+        masm.loadPtrFromImm(obj->addressOfShape(), ic.shapeReg);
         shapeGuard = masm.branchPtrWithPatch(Assembler::NotEqual, ic.shapeReg,
                                              ic.shape, ImmPtr(NULL));
         masm.move(ImmPtr(obj), ic.objReg);
     } else {
         ic.objReg = frame.copyDataIntoReg(objFe);
         ic.shapeReg = frame.allocReg();
         ic.objConst = false;
 
@@ -6235,17 +6238,17 @@ mjit::Compiler::jsop_setgname(JSAtom *at
     passMICAddress(ic);
     ic.slowPathCall = OOL_STUBCALL(ic::SetGlobalName, REJOIN_FALLTHROUGH);
 
     /* Garbage value. */
     uint32 slot = 1 << 24;
 
     ic.usePropertyCache = usePropertyCache;
 
-    masm.loadPtr(Address(ic.objReg, offsetof(JSObject, slots)), ic.objReg);
+    masm.loadPtr(Address(ic.objReg, JSObject::offsetOfSlots()), ic.objReg);
     Address address(ic.objReg, slot);
 
     if (ic.vr.isConstant()) {
         ic.store = masm.storeValueWithAddressOffsetPatch(ic.vr.value(), address);
     } else if (ic.vr.isTypeKnown()) {
         ic.store = masm.storeValueWithAddressOffsetPatch(ImmType(ic.vr.knownType()),
                                                           ic.vr.dataReg(), address);
     } else {
@@ -6443,17 +6446,17 @@ mjit::Compiler::jsop_newinit()
         if (!type)
             return false;
     }
 
     if (!cx->typeInferenceEnabled() ||
         !globalObj ||
         (isArray && count >= gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST)) ||
         (!isArray && !baseobj) ||
-        (!isArray && baseobj->hasSlotsArray())) {
+        (!isArray && baseobj->hasDynamicSlots())) {
         prepareStubCall(Uses(0));
         masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
         masm.move(ImmPtr(stubArg), Registers::ArgReg1);
         INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
         frame.pushSynced(JSVAL_TYPE_OBJECT);
 
         frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY);
         frame.extra(frame.peek(-1)).initObject = baseobj;
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -420,40 +420,40 @@ mjit::Compiler::compileArrayPush(FrameEn
     /* Allocate registers. */
     ValueRemat vr;
     frame.pinEntry(arg, vr, /* breakDouble = */ false);
 
     RegisterID objReg = frame.tempRegForData(thisValue);
     frame.pinReg(objReg);
 
     RegisterID slotsReg = frame.allocReg();
+    masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg);
 
     RegisterID lengthReg = frame.allocReg();
-    masm.load32(Address(objReg, offsetof(JSObject, privateData)), lengthReg);
+    masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), lengthReg);
 
     frame.unpinReg(objReg);
 
     Int32Key key = Int32Key::FromRegister(lengthReg);
 
     /* Test for 'length == initializedLength' */
-    Jump initlenGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
-                                              objReg, key, Assembler::NotEqual);
+    Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
+                                              slotsReg, key, Assembler::NotEqual);
     stubcc.linkExit(initlenGuard, Uses(3));
 
     /* Test for 'length < capacity' */
-    Jump capacityGuard = masm.guardArrayExtent(offsetof(JSObject, capacity),
-                                               objReg, key, Assembler::BelowOrEqual);
+    Jump capacityGuard = masm.guardArrayExtent(ObjectElements::offsetOfCapacity(),
+                                               slotsReg, key, Assembler::BelowOrEqual);
     stubcc.linkExit(capacityGuard, Uses(3));
 
-    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), slotsReg);
     masm.storeValue(vr, BaseIndex(slotsReg, lengthReg, masm.JSVAL_SCALE));
 
     masm.bumpKey(key, 1);
-    masm.store32(lengthReg, Address(objReg, offsetof(JSObject, privateData)));
-    masm.store32(lengthReg, Address(objReg, offsetof(JSObject, initializedLength)));
+    masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfLength()));
+    masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfInitializedLength()));
 
     stubcc.leave();
     stubcc.masm.move(Imm32(1), Registers::ArgReg1);
     OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
 
     frame.unpinEntry(vr);
     frame.freeReg(slotsReg);
     frame.popn(3);
@@ -470,58 +470,59 @@ mjit::Compiler::compileArrayPop(FrameEnt
     /* Filter out silly cases. */
     if (thisValue->isConstant())
         return Compile_InlineAbort;
 
     RegisterID objReg = frame.tempRegForData(thisValue);
     frame.pinReg(objReg);
 
     RegisterID lengthReg = frame.allocReg();
-    masm.load32(Address(objReg, offsetof(JSObject, privateData)), lengthReg);
+    RegisterID slotsReg = frame.allocReg();
 
     JSValueType type = knownPushedType(0);
 
-    MaybeRegisterID slotsReg, dataReg, typeReg;
+    MaybeRegisterID dataReg, typeReg;
     if (!analysis->popGuaranteed(PC)) {
-        slotsReg = frame.allocReg();
         dataReg = frame.allocReg();
         if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE)
             typeReg = frame.allocReg();
     }
 
     frame.unpinReg(objReg);
 
+    masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), slotsReg);
+    masm.load32(Address(slotsReg, ObjectElements::offsetOfLength()), lengthReg);
+
     /* Test for 'length == initializedLength' */
     Int32Key key = Int32Key::FromRegister(lengthReg);
-    Jump initlenGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
-                                              objReg, key, Assembler::NotEqual);
+    Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
+                                              slotsReg, key, Assembler::NotEqual);
     stubcc.linkExit(initlenGuard, Uses(3));
 
     /* Test for length != 0 */
     Jump emptyGuard = masm.branch32(Assembler::Equal, lengthReg, Imm32(0));
     stubcc.linkExit(emptyGuard, Uses(3));
 
     masm.bumpKey(key, -1);
 
     if (dataReg.isSet()) {
-        masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), slotsReg.reg());
-        BaseIndex slot(slotsReg.reg(), lengthReg, masm.JSVAL_SCALE);
+        BaseIndex slot(slotsReg, lengthReg, masm.JSVAL_SCALE);
         Jump holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg.reg());
         if (!isPacked)
             stubcc.linkExit(holeCheck, Uses(3));
-        frame.freeReg(slotsReg.reg());
     }
 
-    masm.store32(lengthReg, Address(objReg, offsetof(JSObject, privateData)));
-    masm.store32(lengthReg, Address(objReg, offsetof(JSObject, initializedLength)));
+    masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfLength()));
+    masm.store32(lengthReg, Address(slotsReg, ObjectElements::offsetOfInitializedLength()));
 
     stubcc.leave();
     stubcc.masm.move(Imm32(0), Registers::ArgReg1);
     OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
 
+    frame.freeReg(slotsReg);
     frame.freeReg(lengthReg);
     frame.popn(2);
 
     if (dataReg.isSet()) {
         if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE)
             frame.pushRegs(typeReg.reg(), dataReg.reg(), type);
         else
             frame.pushTypedPayload(type, dataReg.reg());
@@ -597,23 +598,26 @@ mjit::Compiler::compileArrayWithArgs(uin
     templateObject->setType(type);
 
     JS_ASSERT(templateObject->getDenseArrayCapacity() >= argc);
 
     RegisterID result = frame.allocReg();
     Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
     stubcc.linkExit(emptyFreeList, Uses(0));
 
+    int offset = JSObject::offsetOfFixedElements();
+    masm.store32(Imm32(argc),
+                 Address(result, offset + ObjectElements::offsetOfInitializedLength()));
+
     for (unsigned i = 0; i < argc; i++) {
         FrameEntry *arg = frame.peek(-(int)argc + i);
-        frame.storeTo(arg, Address(result, JSObject::getFixedSlotOffset(i)), /* popped = */ true);
+        frame.storeTo(arg, Address(result, offset), /* popped = */ true);
+        offset += sizeof(Value);
     }
 
-    masm.storePtr(ImmPtr((void *) argc), Address(result, offsetof(JSObject, initializedLength)));
-
     stubcc.leave();
 
     stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
     OOL_STUBCALL(stubs::SlowCall, REJOIN_FALLTHROUGH);
 
     frame.popn(argc + 2);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1102,72 +1102,69 @@ mjit::Compiler::jsop_setelem_dense()
     if (hoisted) {
         FrameEntry *slotsFe = loop->invariantArraySlots(objv);
         slotsReg = frame.tempRegForData(slotsFe);
 
         frame.unpinEntry(vr);
         if (pinKey)
             frame.unpinReg(key.reg());
     } else {
-        // Get a register for the object which we can clobber.
-        RegisterID objReg;
+        // Get a register for the object which we can clobber, and load its elements.
         if (frame.haveSameBacking(obj, value)) {
-            objReg = frame.allocReg();
-            masm.move(vr.dataReg(), objReg);
+            slotsReg = frame.allocReg();
+            masm.move(vr.dataReg(), slotsReg);
         } else if (frame.haveSameBacking(obj, id)) {
-            objReg = frame.allocReg();
-            masm.move(key.reg(), objReg);
+            slotsReg = frame.allocReg();
+            masm.move(key.reg(), slotsReg);
         } else {
-            objReg = frame.copyDataIntoReg(obj);
+            slotsReg = frame.copyDataIntoReg(obj);
         }
+        masm.loadPtr(Address(slotsReg, JSObject::offsetOfElements()), slotsReg);
 
         frame.unpinEntry(vr);
         if (pinKey)
             frame.unpinReg(key.reg());
 
         // Make an OOL path for setting exactly the initialized length.
         Label syncTarget = stubcc.syncExitAndJump(Uses(3));
 
-        Jump initlenGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
-                                                  objReg, key, Assembler::BelowOrEqual);
+        Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
+                                                  slotsReg, key, Assembler::BelowOrEqual);
         stubcc.linkExitDirect(initlenGuard, stubcc.masm.label());
 
         // Recheck for an exact initialized length. :TODO: would be nice to
         // reuse the condition bits from the previous test.
-        Jump exactlenGuard = stubcc.masm.guardArrayExtent(offsetof(JSObject, initializedLength),
-                                                          objReg, key, Assembler::NotEqual);
+        Jump exactlenGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
+                                                          slotsReg, key, Assembler::NotEqual);
         exactlenGuard.linkTo(syncTarget, &stubcc.masm);
 
         // Check array capacity.
-        Jump capacityGuard = stubcc.masm.guardArrayExtent(offsetof(JSObject, capacity),
-                                                          objReg, key, Assembler::BelowOrEqual);
+        Jump capacityGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfCapacity(),
+                                                          slotsReg, key, Assembler::BelowOrEqual);
         capacityGuard.linkTo(syncTarget, &stubcc.masm);
 
         // Bump the index for setting the array length.  The above guard
         // ensures this won't overflow, due to NSLOTS_LIMIT.
         stubcc.masm.bumpKey(key, 1);
 
         // Update the initialized length.
-        stubcc.masm.storeKey(key, Address(objReg, offsetof(JSObject, initializedLength)));
+        stubcc.masm.storeKey(key, Address(slotsReg, ObjectElements::offsetOfInitializedLength()));
 
         // Update the array length if needed.
-        Jump lengthGuard = stubcc.masm.guardArrayExtent(offsetof(JSObject, privateData),
-                                                        objReg, key, Assembler::AboveOrEqual);
-        stubcc.masm.storeKey(key, Address(objReg, offsetof(JSObject, privateData)));
+        Jump lengthGuard = stubcc.masm.guardArrayExtent(ObjectElements::offsetOfLength(),
+                                                        slotsReg, key, Assembler::AboveOrEqual);
+        stubcc.masm.storeKey(key, Address(slotsReg, ObjectElements::offsetOfLength()));
         lengthGuard.linkTo(stubcc.masm.label(), &stubcc.masm);
 
         // Restore the index.
         stubcc.masm.bumpKey(key, -1);
 
         // Rejoin with the inline path.
         Jump initlenExit = stubcc.masm.jump();
         stubcc.crossJump(initlenExit, masm.label());
-
-        masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
-        slotsReg = objReg;
     }
 
     // Fully store the value. :TODO: don't need to do this in the non-initlen case
     // if the array is packed and monomorphic.
     if (key.isConstant())
         masm.storeValue(vr, Address(slotsReg, key.index() * sizeof(Value)));
     else
         masm.storeValue(vr, BaseIndex(slotsReg, key.reg(), masm.JSVAL_SCALE));
@@ -1558,28 +1555,28 @@ mjit::Compiler::jsop_setelem(bool popGua
     if (!denseArrayShape) {
         denseArrayShape = BaseShape::lookupEmpty(cx, &ArrayClass);
         if (!denseArrayShape)
             return false;
     }
 
     // Guard obj is a dense array.
     ic.shapeGuard = masm.branchPtr(Assembler::NotEqual,
-                                   Address(ic.objReg, offsetof(JSObject, lastProp)),
+                                   Address(ic.objReg, JSObject::offsetOfShape()),
                                    ImmPtr(denseArrayShape));
     stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
 
+    // Load the dynamic elements vector.
+    masm.loadPtr(Address(ic.objReg, JSObject::offsetOfElements()), ic.objReg);
+
     // Guard in range of initialized length.
-    Jump initlenGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
+    Jump initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
                                               ic.objReg, ic.key, Assembler::BelowOrEqual);
     stubcc.linkExitDirect(initlenGuard, ic.slowPathStart);
 
-    // Load the dynamic slots vector.
-    masm.loadPtr(Address(ic.objReg, offsetof(JSObject, slots)), ic.objReg);
-
     // Guard there's no hole, then store directly to the slot.
     if (ic.key.isConstant()) {
         Address slot(ic.objReg, ic.key.index() * sizeof(Value));
         ic.holeGuard = masm.guardNotHole(slot);
         masm.storeValue(ic.vr, slot);
     } else {
         BaseIndex slot(ic.objReg, ic.key.reg(), Assembler::JSVAL_SCALE);
         ic.holeGuard = masm.guardNotHole(slot);
@@ -1689,17 +1686,17 @@ mjit::Compiler::jsop_getelem_dense(bool 
     bool allowUndefined = mayPushUndefined(0);
 
     analyze::CrossSSAValue objv(a->inlineIndex, analysis->poppedValue(PC, 1));
     analyze::CrossSSAValue indexv(a->inlineIndex, analysis->poppedValue(PC, 0));
     bool hoisted = loop && id->isType(JSVAL_TYPE_INT32) &&
         loop->hoistArrayLengthCheck(DENSE_ARRAY, objv, indexv);
 
     // Get a register with either the object or its slots, depending on whether
-    // we are hoisting the bounds check.
+    // we are hoisting the slots computation.
     RegisterID baseReg;
     if (hoisted) {
         FrameEntry *slotsFe = loop->invariantArraySlots(objv);
         baseReg = frame.tempRegForData(slotsFe);
     } else {
         baseReg = frame.tempRegForData(obj);
     }
     frame.pinReg(baseReg);
@@ -1712,35 +1709,35 @@ mjit::Compiler::jsop_getelem_dense(bool 
         frame.pinReg(key.reg());
 
     RegisterID dataReg = frame.allocReg();
 
     MaybeRegisterID typeReg;
     if (type == JSVAL_TYPE_UNKNOWN || type == JSVAL_TYPE_DOUBLE || hasTypeBarriers(PC))
         typeReg = frame.allocReg();
 
-    // Guard on the array's initialized length.
-    MaybeJump initlenGuard;
-    if (!hoisted) {
-        initlenGuard = masm.guardArrayExtent(offsetof(JSObject, initializedLength),
-                                             baseReg, key, Assembler::BelowOrEqual);
-    }
-
     frame.unpinReg(baseReg);
     if (pinKey)
         frame.unpinReg(key.reg());
 
     RegisterID slotsReg;
     if (hoisted) {
         slotsReg = baseReg;
     } else {
+        masm.loadPtr(Address(baseReg, JSObject::offsetOfElements()), dataReg);
+        slotsReg = dataReg;
+    }
+
+    // Guard on the array's initialized length.
+    MaybeJump initlenGuard;
+    if (!hoisted) {
+        initlenGuard = masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
+                                             slotsReg, key, Assembler::BelowOrEqual);
         if (!allowUndefined)
             stubcc.linkExit(initlenGuard.get(), Uses(2));
-        masm.loadPtr(Address(baseReg, offsetof(JSObject, slots)), dataReg);
-        slotsReg = dataReg;
     }
 
     // Get the slot, skipping the hole check if the array is known to be packed.
     Jump holeCheck;
     if (key.isConstant()) {
         Address slot(slotsReg, key.index() * sizeof(Value));
         holeCheck = masm.fastArrayLoadSlot(slot, !isPacked, typeReg, dataReg);
     } else {
@@ -2119,17 +2116,17 @@ mjit::Compiler::jsop_getelem(bool isCall
         if (!denseArrayShape) {
             denseArrayShape = BaseShape::lookupEmpty(cx, &ArrayClass);
             if (!denseArrayShape)
                 return false;
         }
 
         // Guard obj is a dense array.
         ic.shapeGuard = masm.branchPtr(Assembler::NotEqual,
-                                       Address(ic.objReg, offsetof(JSObject, lastProp)),
+                                       Address(ic.objReg, JSObject::offsetOfShape()),
                                        ImmPtr(denseArrayShape));
         stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
 
         Int32Key key = id->isConstant()
                        ? Int32Key::FromConstant(id->getValue().toInt32())
                        : Int32Key::FromRegister(ic.id.dataReg());
 
         Assembler::FastArrayLoadFails fails =
@@ -2551,19 +2548,17 @@ mjit::Compiler::jsop_initelem()
         masm.move(Imm32(next == JSOP_ENDINIT ? 1 : 0), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::InitElem, REJOIN_FALLTHROUGH);
         return;
     }
 
     int32 idx = id->getValue().toInt32();
 
     RegisterID objReg = frame.copyDataIntoReg(obj);
-
-    if (cx->typeInferenceEnabled()) {
-        /* Update the initialized length. */
-        masm.store32(Imm32(idx + 1), Address(objReg, offsetof(JSObject, initializedLength)));
-    }
+    masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), objReg);
+
+    /* Update the initialized length. */
+    masm.store32(Imm32(idx + 1), Address(objReg, ObjectElements::offsetOfInitializedLength()));
 
     /* Perform the store. */
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
     frame.storeTo(fe, Address(objReg, idx * sizeof(Value)));
     frame.freeReg(objReg);
 }
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -1319,20 +1319,22 @@ LoopState::restoreInvariants(jsbytecode 
 
           case InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK:
           case InvariantEntry::TYPED_ARRAY_BOUNDS_CHECK: {
             /*
              * Hoisted bounds checks always have preceding invariant slots
              * in the invariant list, so don't recheck this is an object.
              */
             masm.loadPayload(frame.addressOf(entry.u.check.arraySlot), T0);
-            if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK)
-                masm.load32(Address(T0, offsetof(JSObject, initializedLength)), T0);
-            else
+            if (entry.kind == InvariantEntry::DENSE_ARRAY_BOUNDS_CHECK) {
+                masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
+                masm.load32(Address(T0, ObjectElements::offsetOfInitializedLength()), T0);
+            } else {
                 masm.load32(Address(T0, TypedArray::lengthOffset()), T0);
+            }
 
             int32 constant = entry.u.check.constant;
 
             if (entry.u.check.valueSlot1 != uint32(-1)) {
                 constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot1);
                 masm.loadPayload(frame.addressOf(entry.u.check.valueSlot1), T1);
                 if (entry.u.check.valueSlot2 != uint32(-1)) {
                     constant += adjustConstantForIncrement(pc, entry.u.check.valueSlot2);
@@ -1388,28 +1390,26 @@ LoopState::restoreInvariants(jsbytecode 
           }
 
           case InvariantEntry::DENSE_ARRAY_SLOTS:
           case InvariantEntry::DENSE_ARRAY_LENGTH: {
             uint32 array = entry.u.array.arraySlot;
             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
             jumps->append(notObject);
             masm.loadPayload(frame.addressOf(array), T0);
-
-            uint32 offset = (entry.kind == InvariantEntry::DENSE_ARRAY_SLOTS)
-                ? JSObject::offsetOfSlots()
-                : offsetof(JSObject, privateData);
+            masm.loadPtr(Address(T0, JSObject::offsetOfElements()), T0);
 
             Address address = frame.addressOf(frame.getTemporary(entry.u.array.temporary));
 
-            masm.loadPtr(Address(T0, offset), T0);
-            if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH)
+            if (entry.kind == InvariantEntry::DENSE_ARRAY_LENGTH) {
+                masm.load32(Address(T0, ObjectElements::offsetOfLength()), T0);
                 masm.storeValueFromComponents(ImmType(JSVAL_TYPE_INT32), T0, address);
-            else
+            } else {
                 masm.storePayload(T0, address);
+            }
             break;
           }
 
           case InvariantEntry::TYPED_ARRAY_SLOTS:
           case InvariantEntry::TYPED_ARRAY_LENGTH: {
             uint32 array = entry.u.array.arraySlot;
             Jump notObject = masm.testObject(Assembler::NotEqual, frame.addressOf(array));
             jumps->append(notObject);
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -320,34 +320,32 @@ class SetPropCompiler : public PICStubCo
                 }
             }
 
             if (obj->isFixedSlot(shape->slot())) {
                 Address address(pic.objReg,
                                 JSObject::getFixedSlotOffset(shape->slot()));
                 masm.storeValue(pic.u.vr, address);
             } else {
-                /* Check capacity. */
-                Address capacity(pic.objReg, offsetof(JSObject, capacity));
-                masm.load32(capacity, pic.shapeReg);
-                Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg,
-                                                  Imm32(shape->slot()));
-                if (!slowExits.append(overCapacity))
-                    return error();
-
+                /*
+                 * Note: the guard on the initial shape determines the object's
+                 * number of fixed slots and slot span, which in turn determine
+                 * the number of dynamic slots allocated for the object.
+                 * We don't need to check capacity here.
+                 */
                 masm.loadPtr(Address(pic.objReg, JSObject::offsetOfSlots()), pic.shapeReg);
                 Address address(pic.shapeReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value));
                 masm.storeValue(pic.u.vr, address);
             }
 
             JS_ASSERT(shape == obj->lastProperty());
             JS_ASSERT(shape != initialShape);
 
             /* Write the object's new shape. */
-            masm.storePtr(ImmPtr(shape), Address(pic.objReg, offsetof(JSObject, lastProp)));
+            masm.storePtr(ImmPtr(shape), Address(pic.objReg, JSObject::offsetOfShape()));
         } else if (shape->hasDefaultSetter()) {
             JS_ASSERT(!shape->isMethod());
             Address address = masm.objPropAddress(obj, pic.objReg, shape->slot());
             masm.storeValue(pic.u.vr, address);
         } else {
             //   \ /        In general, two function objects with different JSFunctions
             //    #         can have the same shape, thus we must not rely on the identity
             // >--+--<      of 'fun' remaining the same. However, since:
@@ -518,21 +516,18 @@ class SetPropCompiler : public PICStubCo
             if (clasp->ops.defineProperty)
                 return disable("ops define property hook");
 
             uint32 index;
             if (js_IdIsIndex(id, &index))
                 return disable("index");
 
             const Shape *initialShape = obj->lastProperty();
-
-            if (!obj->ensureClassReservedSlots(cx))
-                return error();
-
-            uint32 slots = obj->numSlots();
+            uint32 slots = obj->numDynamicSlots();
+
             uintN flags = 0;
             PropertyOp getter = clasp->getProperty;
 
             if (pic.kind == ic::PICInfo::SETMETHOD) {
                 if (!obj->canHaveMethodBarrier())
                     return disable("can't have method barrier");
 
                 JSObject *funobj = &f.regs.sp[-1].toObject();
@@ -579,17 +574,17 @@ class SetPropCompiler : public PICStubCo
              * capacity checks.  Alternatively, we could avoid the disable
              * and just not generate a stub in case there are multiple shapes
              * that can flow here which don't all require reallocation.
              * 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)
+            if (obj->numDynamicSlots() != slots)
                 return disable("insufficient slot capacity");
 
             if (pic.typeMonitored && !updateMonitoredTypes())
                 return Lookup_Uncacheable;
 
             return generateStub(initialShape, shape, true);
         }
 
@@ -856,17 +851,18 @@ class GetPropCompiler : public PICStubCo
     {
         Assembler masm;
 
         masm.loadObjClass(pic.objReg, pic.shapeReg);
         Jump isDense = masm.testClass(Assembler::Equal, pic.shapeReg, &ArrayClass);
         Jump notArray = masm.testClass(Assembler::NotEqual, pic.shapeReg, &SlowArrayClass);
 
         isDense.linkTo(masm.label(), &masm);
-        masm.load32(Address(pic.objReg, offsetof(JSObject, privateData)), pic.objReg);
+        masm.loadPtr(Address(pic.objReg, JSObject::offsetOfElements()), pic.objReg);
+        masm.load32(Address(pic.objReg, ObjectElements::offsetOfLength()), 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();
 
         pic.updatePCCounters(cx, masm);
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
@@ -1181,17 +1177,17 @@ class GetPropCompiler : public PICStubCo
         Label start;
         Jump shapeGuardJump;
         Jump argsLenGuard;
 
         bool setStubShapeOffset = true;
         if (obj->isDenseArray()) {
             start = masm.label();
             shapeGuardJump = masm.branchPtr(Assembler::NotEqual,
-                                            Address(pic.objReg, offsetof(JSObject, lastProp)),
+                                            Address(pic.objReg, JSObject::offsetOfShape()),
                                             ImmPtr(obj->lastProperty()));
 
             /*
              * No need to assert validity of GETPROP_STUB_SHAPE_JUMP in this case:
              * the IC is disabled after a dense array hit, so no patching can occur.
              */
 #ifndef JS_HAS_IC_LABELS
             setStubShapeOffset = false;
@@ -2928,18 +2924,18 @@ LookupStatus
 SetElementIC::attachHoleStub(JSContext *cx, JSObject *obj, int32 keyval)
 {
     if (keyval < 0)
         return disable(cx, "negative key index");
 
     // We may have failed a capacity check instead of a dense array check.
     // However we should still build the IC in this case, since it could
     // be in a loop that is filling in the array. We can assert, however,
-    // that either we're in capacity or there's a hole - guaranteed by
-    // the fast path.
+    // that either we're outside the initialized length or there's a hole
+    // - guaranteed by the fast path.
     JS_ASSERT((jsuint)keyval >= obj->getDenseArrayInitializedLength() ||
               obj->getDenseArrayElement(keyval).isMagic(JS_ARRAY_HOLE));
 
     if (js_PrototypeHasIndexedProperties(cx, obj))
         return disable(cx, "prototype has indexed properties");
 
     Assembler masm;
 
@@ -2957,37 +2953,41 @@ SetElementIC::attachHoleStub(JSContext *
         Jump j = masm.guardShape(objReg, pobj);
         if (!fails.append(j))
             return error(cx);
     }
 
     // Restore |obj|.
     masm.rematPayload(StateRemat::FromInt32(objRemat), objReg);
 
-    // Guard against negative indices.
-    MaybeJump keyGuard;
-    if (!hasConstantKey)
-        keyGuard = masm.branch32(Assembler::LessThan, keyReg, Imm32(0));
-
-    // Update the array length if necessary.
-    Jump skipUpdate;
-    Address arrayLength(objReg, offsetof(JSObject, privateData));
-    if (hasConstantKey) {
-        skipUpdate = masm.branch32(Assembler::Above, arrayLength, Imm32(keyValue));
-        masm.store32(Imm32(keyValue + 1), arrayLength);
-    } else {
-        skipUpdate = masm.branch32(Assembler::Above, arrayLength, keyReg);
-        masm.add32(Imm32(1), keyReg);
-        masm.store32(keyReg, arrayLength);
-        masm.sub32(Imm32(1), keyReg);
-    }
-    skipUpdate.linkTo(masm.label(), &masm);
+    // Load the elements.
+    masm.loadPtr(Address(objReg, JSObject::offsetOfElements()), objReg);
+
+    Int32Key key = hasConstantKey ? Int32Key::FromConstant(keyValue) : Int32Key::FromRegister(keyReg);
+
+    // Guard that the initialized length is being updated exactly.
+    fails.append(masm.guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
+                                       objReg, key, Assembler::NotEqual));
+
+    // Check the array capacity.
+    fails.append(masm.guardArrayExtent(ObjectElements::offsetOfCapacity(),
+                                       objReg, key, Assembler::BelowOrEqual));
+
+    masm.bumpKey(key, 1);
+
+    // Update the length and initialized length.
+    masm.storeKey(key, Address(objReg, ObjectElements::offsetOfInitializedLength()));
+    Jump lengthGuard = masm.guardArrayExtent(ObjectElements::offsetOfLength(),
+                                             objReg, key, Assembler::AboveOrEqual);
+    masm.storeKey(key, Address(objReg, ObjectElements::offsetOfLength()));
+    lengthGuard.linkTo(masm.label(), &masm);
+
+    masm.bumpKey(key, -1);
 
     // Store the value back.
-    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
     if (hasConstantKey) {
         Address slot(objReg, keyValue * sizeof(Value));
         masm.storeValue(vr, slot);
     } else {
         BaseIndex slot(objReg, keyReg, Assembler::JSVAL_SCALE);
         masm.storeValue(vr, slot);
     }
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1256,17 +1256,17 @@ stubs::NewInitArray(VMFrame &f, uint32 c
 
 void JS_FASTCALL
 stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
 {
     JSContext *cx = f.cx;
     TypeObject *type = (TypeObject *) f.scratch;
 
     if (!baseobj) {
-        gc::AllocKind kind = GuessObjectGCKind(0, false);
+        gc::AllocKind kind = GuessObjectGCKind(0);
         JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
         if (!obj)
             THROW();
         if (type)
             obj->setType(type);
         f.regs.sp[0].setObject(*obj);
         return;
     }
--- a/js/src/tracejit/Writer.cpp
+++ b/js/src/tracejit/Writer.cpp
@@ -438,17 +438,18 @@ void ValidateWriter::checkAccSet(LOpcode
         // ins  = {ld,st}p.objprivate base[offsetof(JSObject, privateData)]
         ok = (op == LIR_ldi || op == LIR_ldp ||
               op == LIR_sti || op == LIR_stp) &&
              disp == offsetof(JSObject, privateData) &&
              couldBeObjectOrString(base);
         break;
 
       case ACCSET_OBJ_CAPACITY:
-        ok = OK_OBJ_FIELD(LIR_ldi, capacity) || OK_OBJ_FIELD(LIR_ldi, initializedLength);
+        ok = false;
+        // OK_OBJ_FIELD(LIR_ldi, capacity) || OK_OBJ_FIELD(LIR_ldi, initializedLength);
         break;
 
       case ACCSET_OBJ_SLOTS:
         ok = OK_OBJ_FIELD(LIR_ldp, slots);
         break;
 
       case ACCSET_SLOTS:
         // This check is imperfect.
--- a/js/src/tracejit/Writer.h
+++ b/js/src/tracejit/Writer.h
@@ -523,23 +523,25 @@ class Writer
 
     nj::LIns *stuiObjPrivate(nj::LIns *obj, nj::LIns *value) const {
         return name(lir->insStore(nj::LIR_sti, value, obj, offsetof(JSObject, privateData),
                                   ACCSET_OBJ_PRIVATE),
                     "private_uint32");
     }
 
     nj::LIns *ldiDenseArrayInitializedLength(nj::LIns *array) const {
-        return name(lir->insLoad(nj::LIR_ldi, array, offsetof(JSObject, initializedLength),
+        JS_NOT_REACHED("FIXME");
+        return name(lir->insLoad(nj::LIR_ldi, array, 0,
                                  ACCSET_OBJ_CAPACITY),
                     "capacity");
     }
 
     nj::LIns *ldpObjSlots(nj::LIns *obj) const {
-        return name(lir->insLoad(nj::LIR_ldp, obj, JSObject::offsetOfSlots(), ACCSET_OBJ_SLOTS),
+        JS_NOT_REACHED("FIXME");
+        return name(lir->insLoad(nj::LIR_ldp, obj, 0, ACCSET_OBJ_SLOTS),
                     "slots");
     }
 
     nj::LIns *ldpObjFixedSlots(nj::LIns *obj) const {
         //return name(lir->insLoad(nj::LIR_ldp, obj, sizeof(JSObject), ACCSET_SLOTS),
 #if JS_BITS_PER_WORD == 32
         return name(lir->ins2(nj::LIR_addp, obj, lir->insImmI(sizeof(JSObject))),
 #else
--- a/js/src/vm/CallObject-inl.h
+++ b/js/src/vm/CallObject-inl.h
@@ -129,17 +129,17 @@ CallObject::setVar(uintN i, const js::Va
     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
     JS_ASSERT(i < fun->script()->bindings.countVars());
     setSlot(RESERVED_SLOTS + fun->nargs + i, v);
 }
 
 inline void
 CallObject::copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots)
 {
-    JS_ASSERT(numSlots() >= RESERVED_SLOTS + nargs + nvars);
+    JS_ASSERT(slotInRange(RESERVED_SLOTS + nargs + nvars, /* sentinelAllowed = */ true));
     copySlotRange(RESERVED_SLOTS, argv, nargs);
     copySlotRange(RESERVED_SLOTS + nargs, slots, nvars);
 }
 
 inline js::Value *
 CallObject::argArray()
 {
     js::DebugOnly<JSFunction*> fun = getCalleeFunction();
--- a/js/src/vm/CallObject.cpp
+++ b/js/src/vm/CallObject.cpp
@@ -74,22 +74,18 @@ CallObject::create(JSContext *cx, JSScri
     if (!obj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
     if (!obj->initCall(cx, bindings, &scopeChain))
         return NULL;
     obj->makeVarObj();
 
-    /* This must come after callobj->lastProp has been set. */
-    if (!obj->ensureInstanceReservedSlots(cx, argsVars))
-        return NULL;
-
 #ifdef DEBUG
-    for (Shape::Range r = obj->lastProp; !r.empty(); r.popFront()) {
+    for (Shape::Range r = obj->lastProperty(); !r.empty(); r.popFront()) {
         const Shape &s = r.front();
         if (s.hasSlot()) {
             JS_ASSERT(s.slot() + 1 == obj->slotSpan());
             break;
         }
     }
 #endif
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -362,17 +362,17 @@ Debugger::getScriptFrame(JSContext *cx, 
 {
     JS_ASSERT(fp->isScriptFrame());
     FrameMap::AddPtr p = frames.lookupForAdd(fp);
     if (!p) {
         /* Create and populate the Debugger.Frame object. */
         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject();
         JSObject *frameobj =
             NewNonFunction<WithProto::Given>(cx, &DebuggerFrame_class, proto, NULL);
-        if (!frameobj || !frameobj->ensureClassReservedSlots(cx))
+        if (!frameobj)
             return false;
         frameobj->setPrivate(fp);
         frameobj->setReservedSlot(JSSLOT_DEBUGFRAME_OWNER, ObjectValue(*object));
 
         if (!frames.add(p, fp, frameobj)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
@@ -497,17 +497,17 @@ Debugger::wrapDebuggeeValue(JSContext *c
         ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj);
         if (p) {
             vp->setObject(*p->value);
         } else {
             /* Create a new Debugger.Object for obj. */
             JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject();
             JSObject *dobj =
                 NewNonFunction<WithProto::Given>(cx, &DebuggerObject_class, proto, NULL);
-            if (!dobj || !dobj->ensureClassReservedSlots(cx))
+            if (!dobj)
                 return false;
             dobj->setPrivate(obj);
             dobj->setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*object));
             if (!objects.relookupOrAdd(p, obj, dobj)) {
                 js_ReportOutOfMemory(cx);
                 return false;
             }
             vp->setObject(*dobj);
@@ -1609,17 +1609,17 @@ Debugger::construct(JSContext *cx, uintN
     JS_ASSERT(proto->getClass() == &Debugger::jsclass);
 
     /*
      * Make the new Debugger object. Each one has a reference to
      * Debugger.{Frame,Object,Script}.prototype in reserved slots. The rest of
      * the reserved slots are for hooks; they default to undefined.
      */
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, &Debugger::jsclass, proto, NULL);
-    if (!obj || !obj->ensureClassReservedSlots(cx))
+    if (!obj)
         return false;
     for (uintN slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
 
     Debugger *dbg = cx->new_<Debugger>(cx, obj);
     if (!dbg)
         return false;
     obj->setPrivate(dbg);
@@ -1880,17 +1880,17 @@ Class DebuggerScript_class = {
 JSObject *
 Debugger::newDebuggerScript(JSContext *cx, JSScript *script, JSObject *holder)
 {
     assertSameCompartment(cx, object);
 
     JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject();
     JS_ASSERT(proto);
     JSObject *scriptobj = NewNonFunction<WithProto::Given>(cx, &DebuggerScript_class, proto, NULL);
-    if (!scriptobj || !scriptobj->ensureClassReservedSlots(cx))
+    if (!scriptobj)
         return false;
     scriptobj->setPrivate(script);
     scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
     scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_HOLDER, PrivateValue(holder));
 
     return scriptobj;
 }
 
@@ -3640,17 +3640,17 @@ JS_DefineDebuggerObject(JSContext *cx, J
     JSObject *objProto;
     if (!js_GetClassPrototype(cx, obj, JSProto_Object, &objProto))
         return false;
 
     JSObject *debugCtor;
     JSObject *debugProto = js_InitClass(cx, obj, objProto, &Debugger::jsclass, Debugger::construct,
                                         1, Debugger::properties, Debugger::methods, NULL, NULL,
                                         &debugCtor);
-    if (!debugProto || !debugProto->ensureClassReservedSlots(cx))
+    if (!debugProto)
         return false;
 
     JSObject *frameProto = js_InitClass(cx, debugCtor, objProto, &DebuggerFrame_class,
                                         DebuggerFrame_construct, 0,
                                         DebuggerFrame_properties, DebuggerFrame_methods,
                                         NULL, NULL);
     if (!frameProto)
         return false;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -270,19 +270,16 @@ GlobalObject::create(JSContext *cx, Clas
     globalObj->setFlags(0);
 
     return globalObj;
 }
 
 bool
 GlobalObject::initStandardClasses(JSContext *cx)
 {
-    /* Native objects get their reserved slots from birth. */
-    JS_ASSERT(numSlots() >= JSSLOT_FREE(getClass()));
-
     JSAtomState &state = cx->runtime->atomState;
 
     /* Define a top-level property 'undefined' with the undefined value. */
     if (!defineProperty(cx, ATOM_TO_JSID(state.typeAtoms[JSTYPE_VOID]), UndefinedValue(),
                         JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1344,17 +1344,17 @@ CellCallback(JSContext *cx, void *vdata,
     IterateData *data = static_cast<IterateData *>(vdata);
     CompartmentStats *curr = data->currCompartmentStats;
     curr->gcHeapKinds[traceKind] += thingSize;
     switch (traceKind)
     {
         case JSTRACE_OBJECT:
         {
             JSObject *obj = static_cast<JSObject *>(thing);
-            curr->objectSlots += obj->sizeOfSlotsArray(moz_malloc_usable_size);
+            curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(JS::Value);
             break;
         }
         case JSTRACE_STRING:
         {
             JSString *str = static_cast<JSString *>(thing);
             curr->stringChars += str->charsHeapSize(moz_malloc_usable_size);
             break;
         }
@@ -1375,16 +1375,17 @@ CellCallback(JSContext *cx, void *vdata,
             break;
         }
         case JSTRACE_TYPE_OBJECT:
         {
             js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
             JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory);
             break;
         }
+        case JSTRACE_BASE_SHAPE:
         case JSTRACE_XML:
         {
             break;
         }
     }
     // Yes, this is a subtraction:  see ArenaCallback() for details.
     curr->gcHeapArenaUnused -= thingSize;
 }