[INFER] Use both inline and dynamic slots for objects other than dense arrays, bug 648321.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 08 Apr 2011 19:51:40 -0700
changeset 74919 d3215d1e985a03eb795203c3a6de4bc86c0b246c
parent 74918 bdacf8b9c9c4a342d7da79e89ce088bdd65cd189
child 74920 3816e4abb15803e3d612a883ab6f423f45f59e44
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs648321
milestone2.2a1pre
[INFER] Use both inline and dynamic slots for objects other than dense arrays, bug 648321.
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi-tests/testFuncCallback.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscope.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/LoopState.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -42,23 +42,24 @@ BEGIN_TEST(testConservativeGC)
     checkObjectFields(&obj2Copy, obj2);
     CHECK(!memcmp(&str2Copy, str2, sizeof(str2Copy)));
 
     return true;
 }
 
 bool checkObjectFields(JSObject *savedCopy, JSObject *obj)
 {
-    /*
-     * The GC can change the shape and shrink dslots so we update them before
-     * doing memcmp.
-     */
-    savedCopy->objShape = obj->objShape;
-    savedCopy->slots = obj->slots;
-    CHECK(!memcmp(savedCopy, obj, sizeof(*obj)));
+    /* Ignore fields which are unstable across GCs. */
+    CHECK(savedCopy->map == obj->map);
+    CHECK(savedCopy->clasp == obj->clasp);
+    CHECK(savedCopy->flags == obj->flags);
+    CHECK(savedCopy->newType == obj->newType);
+    CHECK(savedCopy->type == obj->type);
+    CHECK(savedCopy->parent == obj->parent);
+    CHECK(savedCopy->privateData == obj->privateData);
     return true;
 }
 
 END_TEST(testConservativeGC)
 
 BEGIN_TEST(testDerivedValues)
 {
   JSString *str = JS_NewStringCopyZ(cx, "once upon a midnight dreary");
--- a/js/src/jsapi-tests/testFuncCallback.cpp
+++ b/js/src/jsapi-tests/testFuncCallback.cpp
@@ -1,14 +1,15 @@
 #include "tests.h"
 #include "jsfun.h"
 #include "jscntxt.h"
 
 // For TRACING_ENABLED
 #include "jstracer.h"
+#include "jsobjinlines.h"
 
 #ifdef MOZ_TRACE_JSCALLS
 
 static int depth = 0;
 static int enters = 0;
 static int leaves = 0;
 static int interpreted = 0;
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -967,17 +967,17 @@ array_deleteProperty(JSContext *cx, JSOb
 }
 
 static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isDenseArray());
 
     uint32 capacity = obj->getDenseArrayInitializedLength();
-    MarkValueRange(trc, capacity, obj->slots, "element");
+    MarkValueRange(trc, capacity, obj->getDenseArrayElements(), "element");
 }
 
 static JSBool
 array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
 {
     JS_ASSERT(obj->isDenseArray());
 
     /*
@@ -1070,72 +1070,83 @@ JSObject::makeDenseArraySlow(JSContext *
      */
     JSObjectMap *oldMap = map;
 
     /* Create a native scope. */
     js::gc::FinalizeKind kind = js::gc::FinalizeKind(arena()->header()->thingKind);
     if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getType(), kind))
         return false;
 
-    uint32 initlen = getDenseArrayInitializedLength();
+    backfillDenseArrayHoles();
+
+    uint32 arrayCapacity = getDenseArrayCapacity();
+
+    /*
+     * 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.
+     */
+    if (denseArrayHasInlineSlots()) {
+        if (!allocSlots(cx, numSlots())) {
+            setMap(oldMap);
+            return false;
+        }
+        JS_ASSERT(!denseArrayHasInlineSlots());
+    }
+    capacity = numFixedSlots() + arrayCapacity;
+    clasp = &js_SlowArrayClass;
 
     /*
      * 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;
+        clasp = &js_ArrayClass;
         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 < initlen; i++) {
+    for (uint32 i = 0; i < arrayCapacity; i++) {
+        /* Dense array indexes can always fit in a jsid. */
         jsid id;
-        if (!ValueToId(cx, Int32Value(i), &id)) {
-            setMap(oldMap);
-            return false;
-        }
-
-        if (getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE))
+        JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id));
+
+        if (slots[i].isMagic(JS_ARRAY_HOLE))
             continue;
 
-        setDenseArrayElement(next, getDenseArrayElement(i));
+        setSlot(next, slots[i]);
 
         if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
             setMap(oldMap);
+            capacity = arrayCapacity;
+            clasp = &js_ArrayClass;
             return false;
         }
 
         next++;
     }
 
+    clearSlotRange(next, capacity - next);
+
     /* initialized length is not used anymore. */
     initializedLength = 0;
     JS_ASSERT(newType == NULL);
 
     /*
-     * Dense arrays with different numbers of slots but the same number of fixed
-     * slots and the same non-hole indexes must use their fixed slots consistently.
-     */
-    if (hasSlotsArray() && next <= numFixedSlots())
-        revertToFixedSlots(cx);
-
-    ClearValueRange(slots + next, this->capacity - next, false);
-
-    /*
      * Finally, update class. If |this| is Array.prototype, then js_InitClass
      * will create an emptyShape whose class is &js_SlowArrayClass, to ensure
      * that delegating instances can share shapes in the tree rooted at the
      * proto's empty shape.
      */
-    clasp = &js_SlowArrayClass;
     return true;
 }
 
 /* Transfer ownership of buffer to returned string. */
 static inline JSBool
 BufferToString(JSContext *cx, StringBuffer &sb, Value *rval)
 {
     JSString *str = sb.finishString();
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -104,17 +104,18 @@ JSObject::ensureDenseArrayElements(JSCon
         /* Optimize for the common case. */
         if (index < initLength)
             return ED_OK;
         if (index < currentCapacity) {
             JS_ASSERT(cx->typeInferenceEnabled());
             if (index > initLength) {
                 if (!setDenseArrayNotPacked(cx))
                     return ED_FAILED;
-                ClearValueRange(getSlots() + initLength, index - initLength, true);
+                ClearValueRange(getDenseArrayElements() + initLength,
+                                index - initLength, true);
             }
             setDenseArrayInitializedLength(index + 1);
             return ED_OK;
         }
         requiredCapacity = index + 1;
         if (requiredCapacity == 0) {
             /* Overflow. */
             return ED_SPARSE;
@@ -125,17 +126,18 @@ JSObject::ensureDenseArrayElements(JSCon
             /* Overflow. */
             return ED_SPARSE;
         }
         if (requiredCapacity <= initLength)
             return ED_OK;
         if (requiredCapacity <= currentCapacity) {
             JS_ASSERT(cx->typeInferenceEnabled());
             if (index > initLength) {
-                ClearValueRange(getSlots() + initLength, index - initLength, true);
+                ClearValueRange(getDenseArrayElements() + initLength,
+                                index - initLength, true);
                 if (!setDenseArrayNotPacked(cx))
                     return ED_FAILED;
             }
             setDenseArrayInitializedLength(requiredCapacity);
             return ED_OK;
         }
     }
 
@@ -149,17 +151,18 @@ JSObject::ensureDenseArrayElements(JSCon
     }
     if (!growSlots(cx, requiredCapacity))
         return ED_FAILED;
 
     if (cx->typeInferenceEnabled()) {
         if (index > initLength) {
             if (!setDenseArrayNotPacked(cx))
                 return ED_FAILED;
-            ClearValueRange(getSlots() + initLength, index - initLength, true);
+            ClearValueRange(getDenseArrayElements() + initLength,
+                            index - initLength, true);
         }
         setDenseArrayInitializedLength(requiredCapacity);
     } else {
         backfillDenseArrayHoles();
     }
 
     return ED_OK;
 }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -968,17 +968,17 @@ CalleeGetter(JSContext *cx, JSObject *ob
  * must be null.
  */
 static JSObject *
 NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
 {
     Bindings &bindings = script->bindings;
     size_t argsVars = bindings.countArgsAndVars();
     size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
-    gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
+    gc::FinalizeKind kind = gc::GetGCObjectKind(slots, gc::FINALIZE_OBJECT2);
 
     JSObject *callobj = js_NewGCObject(cx, kind);
     if (!callobj)
         return NULL;
 
     /* Init immediately to avoid GC seeing a half-init'ed object. */
     callobj->initCall(cx, bindings, &scopeChain);
 
@@ -1080,19 +1080,18 @@ js_CreateCallObjectOnTrace(JSContext *cx
 
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
                      0, nanojit::ACCSET_STORE_ANY)
 
 inline static void
 CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots)
 {
     JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars);
-    Value *base = callobj.getSlots() + JSObject::CALL_RESERVED_SLOTS;
-    memcpy(base, argv, nargs * sizeof(Value));
-    memcpy(base + nargs, slots, nvars * sizeof(Value));
+    callobj.copySlotRange(JSObject::CALL_RESERVED_SLOTS, argv, nargs);
+    callobj.copySlotRange(JSObject::CALL_RESERVED_SLOTS + nargs, slots, nvars);
 }
 
 void
 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject &callobj = fp->callObj();
     JS_ASSERT(callobj.getPrivate() == fp);
     JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
@@ -1402,17 +1401,17 @@ call_trace(JSTracer *trc, JSObject *obj)
          * for JS objects (including Call objects), so is unable to collect
          * cycles involving Call objects whose frames are active without this
          * hiding hack.
          */
         uintN first = JSObject::CALL_RESERVED_SLOTS;
         uintN count = fp->script()->bindings.countArgsAndVars();
 
         JS_ASSERT(obj->numSlots() >= first + count);
-        SetValueRangeToUndefined(obj->getSlots() + first, count);
+        obj->clearSlotRange(first, count);
     }
 
     MaybeMarkGenerator(trc, obj);
 }
 
 JS_PUBLIC_DATA(Class) js_CallClass = {
     "Call",
     JSCLASS_HAS_PRIVATE |
@@ -2305,17 +2304,17 @@ JSObject::initBoundFunction(JSContext *c
 
         empty->slotSpan += argslen;
         map = empty;
 
         if (!ensureInstanceReservedSlots(cx, argslen))
             return false;
 
         JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS);
-        memcpy(getSlots() + FUN_CLASS_RESERVED_SLOTS, args, argslen * sizeof(Value));
+        copySlotRange(FUN_CLASS_RESERVED_SLOTS, args, argslen);
     }
     return true;
 }
 
 inline JSObject *
 JSObject::getBoundFunctionTarget() const
 {
     JS_ASSERT(isFunction());
@@ -2329,26 +2328,33 @@ inline const js::Value &
 JSObject::getBoundFunctionThis() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
     return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
 }
 
-inline const js::Value *
-JSObject::getBoundFunctionArguments(uintN &argslen) const
+inline const js::Value &
+JSObject::getBoundFunctionArgument(uintN which) const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
-
-    argslen = getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
-    JS_ASSERT_IF(argslen > 0, numSlots() >= argslen);
-
-    return getSlots() + FUN_CLASS_RESERVED_SLOTS;
+    JS_ASSERT(which < getBoundFunctionArgumentCount());
+
+    return getSlot(FUN_CLASS_RESERVED_SLOTS + which);
+}
+
+inline size_t
+JSObject::getBoundFunctionArgumentCount() const
+{
+    JS_ASSERT(isFunction());
+    JS_ASSERT(isBoundFunction());
+
+    return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
 }
 
 namespace js {
 
 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
 JSBool
 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
 {
@@ -2356,18 +2362,17 @@ CallOrConstructBoundFunction(JSContext *
     JS_ASSERT(obj->isFunction());
     JS_ASSERT(obj->isBoundFunction());
 
     LeaveTrace(cx);
 
     bool constructing = IsConstructing(vp);
 
     /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
-    uintN argslen;
-    const Value *boundArgs = obj->getBoundFunctionArguments(argslen);
+    uintN argslen = obj->getBoundFunctionArgumentCount();
 
     if (argc + argslen > JS_ARGS_LENGTH_MAX) {
         js_ReportAllocationOverflow(cx);
         return false;
     }
 
     /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
     JSObject *target = obj->getBoundFunctionTarget();
@@ -2375,17 +2380,18 @@ CallOrConstructBoundFunction(JSContext *
     /* 15.3.4.5.1 step 2. */
     const Value &boundThis = obj->getBoundFunctionThis();
 
     InvokeArgsGuard args;
     if (!cx->stack().pushInvokeArgs(cx, argc + argslen, &args))
         return false;
 
     /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
-    memcpy(args.argv(), boundArgs, argslen * sizeof(Value));
+    for (uintN i = 0; i < argslen; i++)
+        args[i] = obj->getBoundFunctionArgument(i);
     memcpy(args.argv() + argslen, vp + 2, argc * sizeof(Value));
 
     /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
     args.callee().setObject(*target);
 
     if (!constructing)
         args.thisv() = boundThis;
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2637,31 +2637,40 @@ js_GC(JSContext *cx, JSCompartment *comp
          */
     } while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
 #ifdef JS_GCMETER
     js_DumpGCStats(cx->runtime, stderr);
 #endif
     GCTIMER_END(gckind == GC_LAST_CONTEXT);
 }
 
-namespace js {
-namespace gc {
-
 void
-MarkObjectSlots(JSTracer *trc, JSObject *obj)
+JSObject::markSlots(JSTracer *trc)
 {
-    JS_ASSERT(obj->slotSpan() <= obj->numSlots());
-    uint32 nslots = obj->slotSpan();
-    for (uint32 i = 0; i != nslots; ++i) {
-        const Value &v = obj->getSlot(i);
-        JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
+    JS_ASSERT(isNative());
+    JS_ASSERT(slotSpan() <= numSlots());
+    uint32 nfixed = numFixedSlots();
+    uint32 nslots = slotSpan();
+    uint32 i;
+    for (i = 0; i < nslots && i < nfixed; i++) {
+        const Value &v = fixedSlots()[i];
+        JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, this, i);
+        MarkValueRaw(trc, v);
+    }
+    for (; i < nslots; i++) {
+        const Value &v = slots[i - nfixed];
+        JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, this, i);
         MarkValueRaw(trc, v);
     }
 }
 
+
+namespace js {
+namespace gc {
+
 bool
 SetTypeCheckingForCycles(JSContext *cx, JSObject *obj, types::TypeObject *type)
 {
     /*
      * This function cannot be called during the GC and always requires a
      * request.
      */
 #ifdef JS_THREADSAFE
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -115,24 +115,28 @@ GetGCThingTraceKind(const void *thing)
         return JSTRACE_STRING;
     const Cell *cell = reinterpret_cast<const Cell *>(thing);
     return GetFinalizableTraceKind(cell->arena()->header()->thingKind);
 }
 
 /* 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. */
+/*
+ * Get the best kind to use when making an object with the given slot count.
+ * fallback is the kind to use if the number of slots exceeds the maximum
+ * number of fixed slots for an object.
+ */
 static inline FinalizeKind
-GetGCObjectKind(size_t numSlots)
+GetGCObjectKind(size_t numSlots, FinalizeKind fallback = FINALIZE_OBJECT0)
 {
     extern FinalizeKind slotsToThingKind[];
 
     if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
-        return FINALIZE_OBJECT0;
+        return FINALIZE_OBJECT16;
     return slotsToThingKind[numSlots];
 }
 
 /* Get the number of fixed slots and initial capacity associated with a kind. */
 static inline size_t
 GetGCKindSlots(FinalizeKind thingKind)
 {
     /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
@@ -373,19 +377,16 @@ JSObject::trace(JSTracer *trc)
         }
         objShape = newShape;
     }
 }
 
 namespace js {
 namespace gc {
 
-void
-MarkObjectSlots(JSTracer *trc, JSObject *obj);
-
 static inline void
 MarkChildren(JSTracer *trc, JSObject *obj)
 {
     /*
      * If obj either has no map or no type, it must be a newborn. The type is
      * set first, and must be marked in case constructing the map triggered GC.
      * :FIXME: need autorooters for type objects, remove this hack.
      */
@@ -409,17 +410,17 @@ MarkChildren(JSTracer *trc, JSObject *ob
     if (obj->isNative()) {
 #ifdef JS_DUMP_SCOPE_METERS
         js::MeterEntryCount(obj->propertyCount);
 #endif
 
         obj->trace(trc);
 
         if (obj->slotSpan() > 0)
-            MarkObjectSlots(trc, obj);
+            obj->markSlots(trc);
     }
 }
 
 static inline void
 MarkChildren(JSTracer *trc, JSString *str)
 {
     if (str->isDependent())
         MarkString(trc, str->asDependent().base(), "base");
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3316,17 +3316,17 @@ js_NewBlockObject(JSContext *cx)
 }
 
 JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
 {
     JS_ASSERT(proto->isStaticBlock());
 
     size_t count = OBJ_BLOCK_COUNT(cx, proto);
-    gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1);
+    gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1, gc::FINALIZE_OBJECT2);
 
     js::types::TypeObject *type = proto->getNewType(cx);
     if (!type)
         return false;
 
     JSObject *clone = js_NewGCObject(cx, kind);
     if (!clone)
         return NULL;
@@ -3363,17 +3363,17 @@ js_PutBlockObject(JSContext *cx, JSBool 
     JS_ASSERT(count <= size_t(cx->regs->sp - fp->base() - depth));
 
     /* See comments in CheckDestructuring from jsparse.cpp. */
     JS_ASSERT(count >= 1);
 
     if (normalUnwind) {
         uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
         depth += fp->numFixed();
-        memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value));
+        obj->copySlotRange(slot, fp->slots() + depth, count);
     }
 
     /* We must clear the private slot even with errors. */
     obj->setPrivate(NULL);
     fp->setScopeChainNoCallObj(*obj->getParent());
     return normalUnwind;
 }
 
@@ -3439,24 +3439,16 @@ JSObject::defineBlockVariable(JSContext 
                                      Shape::HAS_SHORTID, index);
     if (!shape)
         return NULL;
     if (slot >= numSlots() && !growSlots(cx, slot + 1))
         return NULL;
     return shape;
 }
 
-static size_t
-GetObjectSize(JSObject *obj)
-{
-    return (obj->isFunction() && !obj->getPrivate())
-           ? sizeof(JSFunction)
-           : sizeof(JSObject) + sizeof(js::Value) * obj->numFixedSlots();
-}
-
 bool
 JSObject::copyPropertiesFrom(JSContext *cx, JSObject *obj)
 {
     // If we're not native, then we cannot copy properties.
     JS_ASSERT(isNative() == obj->isNative());
     if (!isNative())
         return true;
 
@@ -3491,26 +3483,26 @@ CopySlots(JSContext *cx, JSObject *from,
     JS_ASSERT(!from->isNative() && !to->isNative());
     size_t nslots = from->numSlots();
     if (to->ensureSlots(cx, nslots))
         return false;
 
     size_t n = 0;
     if (to->isWrapper() &&
         (JSWrapper::wrapperHandler(to)->flags() & JSWrapper::CROSS_COMPARTMENT)) {
-        to->slots[0] = from->slots[0];
-        to->slots[1] = from->slots[1];
+        to->setSlot(0, from->getSlot(0));
+        to->setSlot(1, from->getSlot(1));
         n = 2;
     }
 
     for (; n < nslots; ++n) {
-        Value v = from->slots[n];
+        Value v = from->getSlot(n);
         if (!cx->compartment->wrap(cx, &v))
             return false;
-        to->slots[n] = v;
+        to->setSlot(n, v);
     }
     return true;
 }
 
 JSObject *
 JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
 {
     /*
@@ -3548,103 +3540,101 @@ JSObject::clone(JSContext *cx, JSObject 
     } else {
         JS_ASSERT(isProxy());
         if (!CopySlots(cx, this, clone))
             return NULL;
     }
     return clone;
 }
 
-static void
-TradeGuts(JSObject *a, JSObject *b)
+bool
+JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b)
 {
     JS_ASSERT(a->compartment() == b->compartment());
     JS_ASSERT(a->isFunction() == b->isFunction());
 
     /*
      * Regexp guts are more complicated -- we would need to migrate the
      * refcounted JIT code blob for them across compartments instead of just
      * swapping guts.
      */
     JS_ASSERT(!a->isRegExp() && !b->isRegExp());
 
-    bool aInline = !a->hasSlotsArray();
-    bool bInline = !b->hasSlotsArray();
+    /*
+     * Callers should not try to swap dense arrays, these use a different slot
+     * representation from other objects.
+     */
+    JS_ASSERT(!a->isDenseArray() && !b->isDenseArray());
 
     /* Trade the guts of the objects. */
-    const size_t size = GetObjectSize(a);
-    if (size == GetObjectSize(b)) {
+    const size_t size = a->structSize();
+    if (size == b->structSize()) {
         /*
          * If the objects are the same size, then we make no assumptions about
          * whether they have dynamically allocated slots and instead just copy
          * them over wholesale.
          */
         char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
         JS_ASSERT(size <= sizeof(tmp));
 
         memcpy(tmp, a, size);
         memcpy(a, b, size);
         memcpy(b, tmp, size);
-
-        /* Fixup pointers for inline slots on the objects. */
-        if (aInline)
-            b->slots = b->fixedSlots();
-        if (bInline)
-            a->slots = a->fixedSlots();
     } else {
         /*
-         * If the objects are of differing sizes, then we only copy over the
-         * JSObject portion (things like class, etc.) and leave it to
-         * JSObject::clone to copy over the dynamic slots for us.
+         * If the objects are of differing sizes, then we need to get arrays of
+         * all the slots and copy them over manually; whether a given slot is
+         * inline or not will vary between the objects. This situation must
+         * only happen when we are swapping proxies, which are non-native and
+         * thus do not have shapes depending on the number of inline slots.
          */
+        JS_ASSERT(a->isProxy() && b->isProxy());
+
+        AutoValueVector avals(cx);
+        if (!avals.reserve(a->numSlots()))
+            return false;
+        for (size_t i = 0; i < a->numSlots(); i++)
+            avals.infallibleAppend(a->getSlot(i));
+
+        AutoValueVector bvals(cx);
+        if (!bvals.reserve(b->numSlots()))
+            return false;
+        for (size_t i = 0; i < b->numSlots(); i++)
+            bvals.infallibleAppend(b->getSlot(i));
+
         if (a->isFunction()) {
             JSFunction tmp;
             memcpy(&tmp, a, sizeof tmp);
             memcpy(a, b, sizeof tmp);
             memcpy(b, &tmp, sizeof tmp);
         } else {
             JSObject tmp;
             memcpy(&tmp, a, sizeof tmp);
             memcpy(a, b, sizeof tmp);
             memcpy(b, &tmp, sizeof tmp);
         }
 
-        JS_ASSERT(!aInline);
-        JS_ASSERT(!bInline);
-    }
+        a->copySlotRange(0, bvals.begin(), a->numSlots());
+        b->copySlotRange(0, avals.begin(), b->numSlots());
+    }
+
+    return true;
 }
 
 /*
  * Use this method with extreme caution. It trades the guts of two objects and updates
  * scope ownership. This operation is not thread-safe, just as fast array to slow array
  * transitions are inherently not thread-safe. Don't perform a swap operation on objects
  * shared across threads or, or bad things will happen. You have been warned.
  */
 bool
 JSObject::swap(JSContext *cx, JSObject *other)
 {
-    /*
-     * If we are swapping objects with a different number of builtin slots, force
-     * both to not use their inline slots.
-     */
-    if (GetObjectSize(this) != GetObjectSize(other)) {
-        if (!hasSlotsArray()) {
-            if (!allocSlots(cx, numSlots()))
-                return false;
-        }
-        if (!other->hasSlotsArray()) {
-            if (!other->allocSlots(cx, other->numSlots()))
-                return false;
-        }
-    }
-
-    if (this->compartment() == other->compartment()) {
-        TradeGuts(this, other);
-        return true;
-    }
+    if (this->compartment() == other->compartment())
+        return TradeGuts(cx, this, other);
 
     JSObject *thisClone;
     JSObject *otherClone;
     {
         AutoCompartment ac(cx, other);
         if (!ac.enter())
             return false;
         thisClone = this->clone(cx, other->getProto(), other->getParent());
@@ -3654,18 +3644,20 @@ JSObject::swap(JSContext *cx, JSObject *
     {
         AutoCompartment ac(cx, this);
         if (!ac.enter())
             return false;
         otherClone = other->clone(cx, other->getProto(), other->getParent());
         if (!otherClone || !otherClone->copyPropertiesFrom(cx, other))
             return false;
     }
-    TradeGuts(this, otherClone);
-    TradeGuts(other, thisClone);
+    if (!TradeGuts(cx, this, otherClone))
+        return false;
+    if (!TradeGuts(cx, other, thisClone))
+        return false;
 
     return true;
 }
 
 #if JS_HAS_XDR
 
 #define NO_PARENT_INDEX ((uint32)-1)
 
@@ -4148,38 +4140,90 @@ js_InitClass(JSContext *cx, JSObject *ob
         !js_GetClassPrototype(cx, obj, JSProto_Object, &protoProto)) {
         return NULL;
     }
 
     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
                                          ctorHandler, ps, fs, static_ps, static_fs);
 }
 
+void
+JSObject::clearSlotRange(size_t start, size_t length)
+{
+    JS_ASSERT(start + length <= capacity);
+    if (isDenseArray()) {
+        ClearValueRange(slots + start, length, true);
+    } 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);
+            }
+        } else {
+            ClearValueRange(slots + start - fixed, length, false);
+        }
+    }
+}
+
+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));
+            }
+        } else {
+            memcpy(slots + start - fixed, vector, length * sizeof(Value));
+        }
+    }
+}
+
 bool
 JSObject::allocSlots(JSContext *cx, size_t newcap)
 {
     uint32 oldcap = numSlots();
 
     JS_ASSERT(newcap >= oldcap && !hasSlotsArray());
 
     if (newcap > NSLOTS_LIMIT) {
         if (!JS_ON_TRACE(cx))
             js_ReportAllocationOverflow(cx);
         return false;
     }
 
-    Value *tmpslots = (Value*) cx->malloc_(newcap * sizeof(Value));
+    uint32 allocCount = isDenseArray() ? newcap : newcap - numFixedSlots();
+
+    Value *tmpslots = (Value*) cx->malloc_(allocCount * sizeof(Value));
     if (!tmpslots)
         return false;  /* Leave slots at inline buffer. */
     slots = tmpslots;
     capacity = newcap;
 
-    /* Copy over anything from the inline buffer. */
-    memcpy(slots, fixedSlots(), oldcap * sizeof(Value));
-    ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray());
+    if (isDenseArray()) {
+        /* Copy over anything from the inline buffer. */
+        memcpy(slots, fixedSlots(), oldcap * sizeof(Value));
+        ClearValueRange(slots + oldcap, newcap - oldcap, true);
+    } else {
+        /* Clear out the new slots without copying. */
+        ClearValueRange(slots, allocCount, false);
+    }
+
     return true;
 }
 
 bool
 JSObject::growSlots(JSContext *cx, size_t newcap)
 {
     /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
@@ -4209,59 +4253,69 @@ JSObject::growSlots(JSContext *cx, size_
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
     /* If nothing was allocated yet, treat it as initial allocation. */
     if (!hasSlotsArray())
         return allocSlots(cx, actualCapacity);
 
-    Value *tmpslots = (Value*) cx->realloc_(slots, oldcap * sizeof(Value), actualCapacity * sizeof(Value));
+    uint32 oldAllocCount = isDenseArray() ? oldcap : oldcap - numFixedSlots();
+    uint32 allocCount = isDenseArray() ? actualCapacity : actualCapacity - numFixedSlots();
+
+    Value *tmpslots = (Value*) cx->realloc_(slots, oldAllocCount * sizeof(Value),
+                                            allocCount * sizeof(Value));
     if (!tmpslots)
         return false;    /* Leave dslots as its old size. */
     slots = tmpslots;
     capacity = actualCapacity;
 
     if (!isDenseArray()) {
         /* Initialize the additional slots we added. This is not required for dense arrays. */
-        ClearValueRange(slots + oldcap, actualCapacity - oldcap, false);
+        ClearValueRange(slots + oldAllocCount, allocCount - oldAllocCount, false);
     }
     return true;
 }
 
 void
 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
 {
     uint32 oldcap = numSlots();
     JS_ASSERT(newcap <= oldcap);
     JS_ASSERT(newcap >= slotSpan());
 
+    size_t fixed = numFixedSlots();
+
     if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) {
-        /* We won't shrink the slots any more.  Clear excess holes. */
+        /*
+         * 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())
-            ClearValueRange(slots + newcap, oldcap - newcap, false);
+            clearSlotRange(newcap, oldcap - newcap);
         return;
     }
 
     uint32 fill = newcap;
     if (newcap < SLOT_CAPACITY_MIN)
         newcap = SLOT_CAPACITY_MIN;
-    if (newcap < numFixedSlots())
-        newcap = numFixedSlots();
+    if (newcap < fixed)
+        newcap = fixed;
 
     Value *tmpslots = (Value*) cx->realloc_(slots, newcap * sizeof(Value));
     if (!tmpslots)
         return;  /* Leave slots at its old size. */
     slots = tmpslots;
     capacity = newcap;
 
     if (fill < newcap) {
         /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
         if (!isDenseArray())
-            ClearValueRange(slots + fill, newcap - fill, false);
+            clearSlotRange(fill, newcap - fill);
     }
 }
 
 bool
 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
 {
     JS_ASSERT_IF(isNative(),
                  isBlock() || isCall() || (isFunction() && isBoundFunction()));
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -306,36 +306,45 @@ class ValidateWriter;
  * but it has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM
  * and JS_FlagScriptFilenamePrefix (also exported via jsdbgapi.h) are intended
  * to be complementary to this bit, but it is up to the API client to implement
  * any such association.
  *
  * Both these flag bits are initially zero; they may be set or queried using
  * the (is|set)(Delegate|System) inline methods.
  *
- * The slots member is a pointer to the slot vector for the object.
- * This can be either a fixed array allocated immediately after the object,
- * or a dynamically allocated array.  A dynamic array can be tested for with
- * hasSlotsArray().  In all cases, capacity gives the number of usable slots.
- * Two objects with the same shape have the same number of fixed slots,
- * and either both have or neither have dynamically allocated slot arrays.
+ * 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.
+ *
+ * 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.
+ *
+ * For objects other than dense arrays, 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.
+ * Unlike dense arrays, the fixed slots can always be accessed. Two objects
+ * with the same shape have the same number of fixed slots.
  *
  * If you change this struct, you'll probably need to change the AccSet values
  * in jsbuiltins.h.
  */
 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;
-    friend class GetPropCompiler;
 
     /*
      * Private pointer to the last added property and methods to manipulate the
      * list it links among properties in this scope. The {remove,insert} pair
      * for DictionaryProperties assert that the scope is in dictionary mode and
      * any reachable properties are flagged as dictionary properties.
      *
      * For native objects, this field is always a Shape. For non-native objects,
@@ -380,17 +389,25 @@ struct JSObject : js::gc::Cell {
         METHOD_BARRIER            =  0x20,
         INDEXED                   =  0x40,
         OWN_SHAPE                 =  0x80,
         BOUND_FUNCTION            = 0x100,
         HAS_EQUALITY              = 0x200,
         PACKED_ARRAY              = 0x400,
         METHOD_THRASH_COUNT_MASK  = 0xc00,
         METHOD_THRASH_COUNT_SHIFT =    11,
-        METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT
+        METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT,
+
+        /* The top 5 bits of an object's flags are its number of fixed slots. */
+        FIXED_SLOTS_SHIFT         =    27,
+        FIXED_SLOTS_BIT0          = 0x01 << FIXED_SLOTS_SHIFT,
+        FIXED_SLOTS_BIT1          = 0x02 << FIXED_SLOTS_SHIFT,
+        FIXED_SLOTS_BIT2          = 0x04 << FIXED_SLOTS_SHIFT,
+        FIXED_SLOTS_BIT3          = 0x08 << FIXED_SLOTS_SHIFT,
+        FIXED_SLOTS_BIT4          = 0x10 << FIXED_SLOTS_SHIFT
     };
 
     /*
      * Impose a sane upper bound, originally checked only for dense arrays, on
      * number of slots in an object.
      */
     enum {
         NSLOTS_BITS     = 29,
@@ -406,34 +423,40 @@ struct JSObject : js::gc::Cell {
 
         /* If dense array, initialized length of the array. */
         jsuword initializedLength;
     };
 
     js::types::TypeObject *type;            /* object's type and prototype */
     JSObject    *parent;                    /* object's parent */
     void        *privateData;               /* private data */
-    jsuword     capacity;                   /* capacity of slots */
+    jsuword     capacity;                   /* total number of available slots */
+
+  private:
     js::Value   *slots;                     /* dynamically allocated slots,
-                                               or pointer to fixedSlots() */
+                                               or pointer to fixedSlots() for
+                                               dense arrays. */
+
+  public:
 
     bool isNative() const       { return map->isNative(); }
 
     js::Class *getClass() const { return clasp; }
     JSClass *getJSClass() const { return Jsvalify(clasp); }
 
     bool hasClass(const js::Class *c) const {
         return c == clasp;
     }
 
     const js::ObjectOps *getOps() const {
         return &getClass()->ops;
     }
 
     inline void trace(JSTracer *trc);
+    void markSlots(JSTracer *trc);
 
     uint32 shape() const {
         JS_ASSERT(objShape != JSObjectMap::INVALID_SHAPE);
         return objShape;
     }
 
     bool isDelegate() const     { return !!(flags & DELEGATE); }
     void setDelegate()          { flags |= DELEGATE; }
@@ -604,39 +627,70 @@ struct JSObject : js::gc::Cell {
     inline uint32 propertyCount() const;
 
     inline bool hasPropertyTable() const;
 
     /* gc::FinalizeKind */ unsigned finalizeKind() const;
 
     uint32 numSlots() const { return capacity; }
 
-    size_t slotsAndStructSize(uint32 nslots) const;
-    size_t slotsAndStructSize() const { return slotsAndStructSize(numSlots()); }
+    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); }
 
+    /*
+     * 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 js::Value *getRawSlots();
+    inline js::Value *getRawSlot(size_t slot, js::Value *slots);
+
+    /* 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);
+
+  private:
     inline js::Value* fixedSlots() const;
     inline size_t numFixedSlots() const;
-
-    static inline size_t getFixedSlotOffset(size_t slot);
+    inline bool hasSlotsArray() 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);
 
     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);
+
+    /*
+     * 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.
@@ -645,71 +699,86 @@ struct JSObject : js::gc::Cell {
      * add ad-hoc properties to a Block instance. Call objects satisfy (a) and
      * (b) as well, because the compiler-created Shape path that covers args,
      * vars, and upvars, stored in their callee function in u.i.names, becomes
      * their initial map.
      */
     bool ensureInstanceReservedSlots(JSContext *cx, size_t nreserved);
 
     /*
-     * Get a direct pointer to the object's slots.
-     * This can be reallocated if the object is modified, watch out!
-     */
-    js::Value *getSlots() const {
-        return slots;
-    }
-
-    /*
      * NB: ensureClassReservedSlotsForEmptyObject asserts that nativeEmpty()
      * Use ensureClassReservedSlots for any object, either empty or already
      * extended with properties.
      */
     bool ensureClassReservedSlotsForEmptyObject(JSContext *cx);
 
     inline bool ensureClassReservedSlots(JSContext *cx);
 
     uint32 slotSpan() const { return map->slotSpan; }
 
     bool containsSlot(uint32 slot) const { return slot < slotSpan(); }
 
     js::Value& getSlotRef(uintN slot) {
         JS_ASSERT(slot < capacity);
-        return slots[slot];
+        size_t fixed = numFixedSlots();
+        if (slot < fixed)
+            return fixedSlots()[slot];
+        return slots[slot - fixed];
     }
 
     js::Value &nativeGetSlotRef(uintN slot) {
         JS_ASSERT(isNative());
         JS_ASSERT(containsSlot(slot));
         return getSlotRef(slot);
     }
 
     const js::Value &getSlot(uintN slot) const {
         JS_ASSERT(slot < capacity);
-        return slots[slot];
+        size_t fixed = numFixedSlots();
+        if (slot < fixed)
+            return fixedSlots()[slot];
+        return slots[slot - fixed];
     }
 
     const js::Value &nativeGetSlot(uintN slot) const {
         JS_ASSERT(isNative());
         JS_ASSERT(containsSlot(slot));
         return getSlot(slot);
     }
 
     void setSlot(uintN slot, const js::Value &value) {
         JS_ASSERT(slot < capacity);
-        slots[slot] = value;
+        getSlotRef(slot) = value;
     }
 
     void nativeSetSlot(uintN slot, const js::Value &value) {
         JS_ASSERT(isNative());
         JS_ASSERT(containsSlot(slot));
-        return setSlot(slot, value);
+        setSlot(slot, value);
     }
 
     inline js::Value getReservedSlot(uintN index) const;
 
+    /* For slots which are known to always be fixed, due to the way they are allocated. */
+
+    js::Value &getFixedSlotRef(uintN slot) {
+        JS_ASSERT(isNative() && slot < numFixedSlots());
+        return fixedSlots()[slot];
+    }
+
+    const js::Value &getFixedSlot(uintN slot) const {
+        JS_ASSERT(isNative() && slot < numFixedSlots());
+        return fixedSlots()[slot];
+    }
+
+    void setFixedSlot(uintN slot, const js::Value &value) {
+        JS_ASSERT(isNative() && slot < numFixedSlots());
+        fixedSlots()[slot] = value;
+    }
+
     /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
     inline void updateShape(JSContext *cx);
     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);
 
     js::types::TypeObject* getType() const { return type; }
@@ -790,32 +859,38 @@ struct JSObject : js::gc::Cell {
 
   private:
     static const uint32 JSSLOT_PRIMITIVE_THIS = 0;
 
   public:
     inline const js::Value &getPrimitiveThis() const;
     inline void setPrimitiveThis(const js::Value &pthis);
 
+    static size_t getPrimitiveThisOffset() {
+        /* All primitive objects have their value in a fixed slot. */
+        return getFixedSlotOffset(JSSLOT_PRIMITIVE_THIS);
+    }
+
     /*
      * Array-specific getters and setters (for both dense and slow arrays).
      */
 
     inline uint32 getArrayLength() const;
     inline bool setArrayLength(JSContext *cx, uint32 length);
 
     inline uint32 getDenseArrayCapacity();
     inline uint32 getDenseArrayInitializedLength();
     inline void setDenseArrayLength(uint32 length);
     inline void setDenseArrayInitializedLength(uint32 length);
     inline js::Value* getDenseArrayElements();
     inline const js::Value &getDenseArrayElement(uintN idx);
     inline js::Value* addressOfDenseArrayElement(uintN idx);
     inline void setDenseArrayElement(uintN idx, const js::Value &val);
     inline void shrinkDenseArrayElements(JSContext *cx, uintN cap);
+    inline bool denseArrayHasInlineSlots() const;
     inline void backfillDenseArrayHoles();
 
     /* Packed information for this array. May be incorrect if !cx->typeInferenceEnabled(). */
     inline bool isPackedDenseArray();
     inline bool setDenseArrayNotPacked(JSContext *cx);
 
     /*
      * ensureDenseArrayElements ensures that the dense array can hold at least
@@ -893,16 +968,21 @@ struct JSObject : js::gc::Cell {
     static const uint32 JSSLOT_ARGS_LENGTH = 0;
     static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2;
     static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1;
 
     /* Lower-order bit stolen from the length slot. */
     static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1;
     static const uint32 ARGS_PACKED_BITS_COUNT = 1;
 
+    static size_t getArgumentsLengthOffset() {
+        /* All arguments objects have their length in a fixed slot. */
+        return getFixedSlotOffset(JSSLOT_ARGS_LENGTH);
+    }
+
     /*
      * Set the initial length of the arguments, and mark it as not overridden.
      */
     inline void setArgsLength(uint32 argc);
 
     /*
      * Return the initial length of the arguments.  This may differ from the
      * current value of arguments.length!
@@ -1017,32 +1097,37 @@ struct JSObject : js::gc::Cell {
     static const uint32 JSSLOT_FUN_METHOD_OBJ  = 1;
 
     static const uint32 JSSLOT_BOUND_FUNCTION_THIS       = 0;
     static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
 
   public:
     static const uint32 FUN_CLASS_RESERVED_SLOTS = 2;
 
+    static size_t getFlatClosureUpvarsOffset() {
+        return getFixedSlotOffset(JSSLOT_FLAT_CLOSURE_UPVARS);
+    }
+
     inline JSFunction *getFunctionPrivate() const;
 
     inline js::Value *getFlatClosureUpvars() const;
     inline js::Value getFlatClosureUpvar(uint32 i) const;
     inline js::Value &getFlatClosureUpvar(uint32 i);
     inline void setFlatClosureUpvars(js::Value *upvars);
 
     inline bool hasMethodObj(const JSObject& obj) const;
     inline void setMethodObj(JSObject& obj);
 
     inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg,
                                   const js::Value *args, uintN argslen);
 
     inline JSObject *getBoundFunctionTarget() const;
     inline const js::Value &getBoundFunctionThis() const;
-    inline const js::Value *getBoundFunctionArguments(uintN &argslen) const;
+    inline const js::Value &getBoundFunctionArgument(uintN which) const;
+    inline size_t getBoundFunctionArgumentCount() const;
 
     /*
      * RegExp-specific getters and setters.
      */
 
   private:
     static const uint32 JSSLOT_REGEXP_LAST_INDEX = 0;
     static const uint32 JSSLOT_REGEXP_SOURCE = 1;
@@ -1160,24 +1245,16 @@ struct JSObject : js::gc::Cell {
      */
     inline bool initSharingEmptyShape(JSContext *cx,
                                       js::Class *clasp,
                                       js::types::TypeObject *type,
                                       JSObject *parent,
                                       void *priv,
                                       /* gc::FinalizeKind */ unsigned kind);
 
-    inline bool hasSlotsArray() const;
-
-    /* This method can only be called when hasSlotsArray() returns true. */
-    inline void freeSlotsArray(JSContext *cx);
-
-    /* Free the slots array and copy slots that fit into the fixed array. */
-    inline void revertToFixedSlots(JSContext *cx);
-
     inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0);
 
     /*
      * Allocate and free an object slot. Note that freeSlot is infallible: it
      * returns true iff this is a dictionary-mode object and the freed slot was
      * added to the freelist.
      *
      * FIXME: bug 593129 -- slot allocation should be done by object methods
@@ -1213,16 +1290,18 @@ struct JSObject : js::gc::Cell {
     const js::Shape *addPropertyInternal(JSContext *cx, jsid id,
                                          js::PropertyOp getter, js::StrictPropertyOp setter,
                                          uint32 slot, uintN attrs,
                                          uintN flags, intN shortid,
                                          js::Shape **spp);
 
     bool toDictionaryMode(JSContext *cx);
 
+    static bool TradeGuts(JSContext *cx, JSObject *a, JSObject *b);
+
   public:
     /* Add a property whose id is not yet in this scope. */
     const js::Shape *addProperty(JSContext *cx, jsid id,
                                  js::PropertyOp getter, js::StrictPropertyOp setter,
                                  uint32 slot, uintN attrs,
                                  uintN flags, intN shortid);
 
     /* Add a data property whose id is not yet in this scope. */
@@ -1359,18 +1438,21 @@ struct JSObject : js::gc::Cell {
 /* Check alignment for any fixed slots allocated after the object. */
 JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0);
 
 inline js::Value*
 JSObject::fixedSlots() const {
     return (js::Value*) (jsuword(this) + sizeof(JSObject));
 }
 
-inline bool
-JSObject::hasSlotsArray() const { return this->slots != fixedSlots(); }
+inline size_t
+JSObject::numFixedSlots() const
+{
+    return flags >> FIXED_SLOTS_SHIFT;
+}
 
 /* static */ inline size_t
 JSObject::getFixedSlotOffset(size_t slot) {
     return sizeof(JSObject) + (slot * sizeof(js::Value));
 }
 
 struct JSObject_Slots2 : JSObject { js::Value fslots[2]; };
 struct JSObject_Slots4 : JSObject { js::Value fslots[4]; };
@@ -1471,19 +1553,19 @@ inline bool JSObject::isBlock() const  {
 static const uint32 JSSLOT_BLOCK_DEPTH = 0;
 static const uint32 JSSLOT_BLOCK_FIRST_FREE_SLOT = JSSLOT_BLOCK_DEPTH + 1;
 
 static const uint32 JSSLOT_WITH_THIS = 1;
 
 #define OBJ_BLOCK_COUNT(cx,obj)                                               \
     (obj)->propertyCount()
 #define OBJ_BLOCK_DEPTH(cx,obj)                                               \
-    (obj)->getSlot(JSSLOT_BLOCK_DEPTH).toInt32()
+    (obj)->getFixedSlot(JSSLOT_BLOCK_DEPTH).toInt32()
 #define OBJ_SET_BLOCK_DEPTH(cx,obj,depth)                                     \
-    (obj)->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)))
+    (obj)->setFixedSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)))
 
 /*
  * To make sure this slot is well-defined, always call js_NewWithObject to
  * create a With object, don't call js_NewObject directly.  When creating a
  * With object that does not correspond to a stack slot, pass -1 for depth.
  *
  * When popping the stack across this object's "with" statement, client code
  * must call withobj->setPrivate(NULL).
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -292,16 +292,47 @@ JSObject::methodWriteBarrier(JSContext *
         if (ChangesMethodValue(prev, v)) {
             JS_FUNCTION_METER(cx, mwslotbarrier);
             return methodShapeChange(cx, slot);
         }
     }
     return true;
 }
 
+inline js::Value *
+JSObject::getRawSlots()
+{
+    JS_ASSERT(isGlobal());
+    return slots;
+}
+
+inline js::Value *
+JSObject::getRawSlot(size_t slot, 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::dynamicSlotIndex(size_t slot)
+{
+    JS_ASSERT(!isDenseArray() && slot >= numFixedSlots());
+    return slot - numFixedSlots();
+}
+
 inline bool
 JSObject::ensureClassReservedSlots(JSContext *cx)
 {
     return !nativeEmpty() || ensureClassReservedSlotsForEmptyObject(cx);
 }
 
 inline js::Value
 JSObject::getReservedSlot(uintN index) const
@@ -320,52 +351,61 @@ JSObject::isPrimitive() const
 {
     return isNumber() || isString() || isBoolean();
 }
 
 inline const js::Value &
 JSObject::getPrimitiveThis() const
 {
     JS_ASSERT(isPrimitive());
-    return getSlot(JSSLOT_PRIMITIVE_THIS);
+    return getFixedSlot(JSSLOT_PRIMITIVE_THIS);
 }
 
 inline void
 JSObject::setPrimitiveThis(const js::Value &pthis)
 {
     JS_ASSERT(isPrimitive());
-    setSlot(JSSLOT_PRIMITIVE_THIS, pthis);
+    setFixedSlot(JSSLOT_PRIMITIVE_THIS, pthis);
 }
 
 inline /* gc::FinalizeKind */ unsigned
 JSObject::finalizeKind() const
 {
     return js::gc::FinalizeKind(arena()->header()->thingKind);
 }
 
-inline size_t
-JSObject::numFixedSlots() const
+inline bool
+JSObject::hasSlotsArray() const
 {
-    if (isFunction())
-        return JSObject::FUN_CLASS_RESERVED_SLOTS;
-    if (!hasSlotsArray())
-        return capacity;
-    return js::gc::GetGCKindSlots(js::gc::FinalizeKind(finalizeKind()));
+    JS_ASSERT_IF(!slots, !isDenseArray());
+    JS_ASSERT_IF(slots == fixedSlots(), isDenseArray());
+    return slots && slots != fixedSlots();
 }
 
 inline size_t
-JSObject::slotsAndStructSize(uint32 nslots) const
+JSObject::structSize() const
 {
-    bool isFun = isFunction() && this == (JSObject*) getPrivate();
+    return (isFunction() && !getPrivate())
+           ? sizeof(JSFunction)
+           : sizeof(JSObject) + sizeof(js::Value) * numFixedSlots();
+}
 
-    int ndslots = hasSlotsArray() ? nslots : 0;
-    int nfslots = isFun ? 0 : numFixedSlots();
+inline size_t
+JSObject::slotsAndStructSize() const
+{
+    int ndslots = 0;
+    if (isDenseArray()) {
+        if (!denseArrayHasInlineSlots())
+            ndslots = numSlots();
+    } else {
+        if (slots)
+            ndslots = numSlots() - numFixedSlots();
+    }
 
-    return sizeof(js::Value) * (ndslots + nfslots)
-           + isFun ? sizeof(JSFunction) : sizeof(JSObject);
+    return structSize() + sizeof(js::Value) * ndslots;
 }
 
 inline uint32
 JSObject::getArrayLength() const
 {
     JS_ASSERT(isArray());
     return (uint32)(size_t) getPrivate();
 }
@@ -400,102 +440,109 @@ JSObject::getDenseArrayCapacity()
     JS_ASSERT(isDenseArray());
     return numSlots();
 }
 
 inline js::Value*
 JSObject::getDenseArrayElements()
 {
     JS_ASSERT(isDenseArray());
-    return getSlots();
+    return slots;
 }
 
 inline const js::Value &
 JSObject::getDenseArrayElement(uintN idx)
 {
     JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength());
-    return getSlot(idx);
+    return slots[idx];
 }
 
 inline js::Value *
 JSObject::addressOfDenseArrayElement(uintN idx)
 {
-    JS_ASSERT(isDenseArray());
-    return &getSlotRef(idx);
+    JS_ASSERT(isDenseArray() && idx < capacity);
+    return &slots[idx];
 }
 
 inline void
 JSObject::setDenseArrayElement(uintN idx, const js::Value &val)
 {
     JS_ASSERT(isDenseArray() && idx < getDenseArrayInitializedLength());
-    setSlot(idx, val);
+    slots[idx] = val;
 }
 
 inline void
 JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
 {
     JS_ASSERT(isDenseArray());
     shrinkSlots(cx, cap);
 }
 
+inline bool
+JSObject::denseArrayHasInlineSlots() const
+{
+    JS_ASSERT(isDenseArray() && slots);
+    return slots == fixedSlots();
+}
+
 inline void
 JSObject::backfillDenseArrayHoles()
 {
     /* Only call this if !cx->typeInferenceEnabled(). */
     JS_ASSERT(isDenseArray());
     ClearValueRange(slots + initializedLength, capacity - initializedLength, true);
     initializedLength = capacity;
 }
 
 inline void
 JSObject::setArgsLength(uint32 argc)
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
     JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT));
-    getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT);
+    getFixedSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT);
     JS_ASSERT(!isArgsLengthOverridden());
 }
 
 inline uint32
 JSObject::getArgsInitialLength() const
 {
     JS_ASSERT(isArguments());
-    uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT;
+    uint32 argc = uint32(getFixedSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT;
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
     return argc;
 }
 
 inline void
 JSObject::setArgsLengthOverridden()
 {
     JS_ASSERT(isArguments());
-    getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
+    getFixedSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
 }
 
 inline bool
 JSObject::isArgsLengthOverridden() const
 {
     JS_ASSERT(isArguments());
-    const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH);
+    const js::Value &v = getFixedSlot(JSSLOT_ARGS_LENGTH);
     return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
 }
 
 inline js::ArgumentsData *
 JSObject::getArgsData() const
 {
     JS_ASSERT(isArguments());
-    return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate();
+    return (js::ArgumentsData *) getFixedSlot(JSSLOT_ARGS_DATA).toPrivate();
 }
 
 inline void
 JSObject::setArgsData(js::ArgumentsData *data)
 {
     JS_ASSERT(isArguments());
-    getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data);
+    getFixedSlotRef(JSSLOT_ARGS_DATA).setPrivate(data);
 }
 
 inline const js::Value &
 JSObject::getArgsCallee() const
 {
     return getArgsData()->callee;
 }
 
@@ -535,65 +582,65 @@ JSObject::setArgsElement(uint32 i, const
     JS_ASSERT(i < getArgsInitialLength());
     getArgsData()->slots[i] = v;
 }
 
 inline bool
 JSObject::callIsForEval() const
 {
     JS_ASSERT(isCall());
-    JS_ASSERT(getSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
-    JS_ASSERT_IF(getSlot(JSSLOT_CALL_CALLEE).isObject(),
-                 getSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
-    return getSlot(JSSLOT_CALL_CALLEE).isNull();
+    JS_ASSERT(getFixedSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
+    JS_ASSERT_IF(getFixedSlot(JSSLOT_CALL_CALLEE).isObject(),
+                 getFixedSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
+    return getFixedSlot(JSSLOT_CALL_CALLEE).isNull();
 }
 
 inline JSStackFrame *
 JSObject::maybeCallObjStackFrame() const
 {
     JS_ASSERT(isCall());
     return reinterpret_cast<JSStackFrame *>(getPrivate());
 }
 
 inline void
 JSObject::setCallObjCallee(JSObject *callee)
 {
     JS_ASSERT(isCall());
     JS_ASSERT_IF(callee, callee->isFunction());
-    return getSlotRef(JSSLOT_CALL_CALLEE).setObjectOrNull(callee);
+    return getFixedSlotRef(JSSLOT_CALL_CALLEE).setObjectOrNull(callee);
 }
 
 inline JSObject *
 JSObject::getCallObjCallee() const
 {
     JS_ASSERT(isCall());
-    return getSlot(JSSLOT_CALL_CALLEE).toObjectOrNull();
+    return getFixedSlot(JSSLOT_CALL_CALLEE).toObjectOrNull();
 }
 
 inline JSFunction *
 JSObject::getCallObjCalleeFunction() const
 {
     JS_ASSERT(isCall());
-    return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
+    return getFixedSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
 }
 
 inline const js::Value &
 JSObject::getCallObjArguments() const
 {
     JS_ASSERT(isCall());
     JS_ASSERT(!callIsForEval());
-    return getSlot(JSSLOT_CALL_ARGUMENTS);
+    return getFixedSlot(JSSLOT_CALL_ARGUMENTS);
 }
 
 inline void
 JSObject::setCallObjArguments(const js::Value &v)
 {
     JS_ASSERT(isCall());
     JS_ASSERT(!callIsForEval());
-    setSlot(JSSLOT_CALL_ARGUMENTS, v);
+    setFixedSlot(JSSLOT_CALL_ARGUMENTS, v);
 }
 
 inline const js::Value &
 JSObject::callObjArg(uintN i) const
 {
     JS_ASSERT(isCall());
     JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
     return getSlot(JSObject::CALL_RESERVED_SLOTS + i);
@@ -624,35 +671,35 @@ JSObject::callObjVar(uintN i)
     JS_ASSERT(i < fun->script()->bindings.countVars());
     return getSlotRef(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i);
 }
 
 inline const js::Value &
 JSObject::getDateUTCTime() const
 {
     JS_ASSERT(isDate());
-    return getSlot(JSSLOT_DATE_UTC_TIME);
+    return getFixedSlot(JSSLOT_DATE_UTC_TIME);
 }
 
 inline void 
 JSObject::setDateUTCTime(const js::Value &time)
 {
     JS_ASSERT(isDate());
-    setSlot(JSSLOT_DATE_UTC_TIME, time);
+    setFixedSlot(JSSLOT_DATE_UTC_TIME, time);
 }
 
 inline js::Value *
 JSObject::getFlatClosureUpvars() const
 {
 #ifdef DEBUG
     JSFunction *fun = getFunctionPrivate();
     JS_ASSERT(fun->isFlatClosure());
     JS_ASSERT(fun->script()->bindings.countUpvars() == fun->script()->upvars()->length);
 #endif
-    return (js::Value *) getSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate();
+    return (js::Value *) getFixedSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate();
 }
 
 inline js::Value
 JSObject::getFlatClosureUpvar(uint32 i) const
 {
     JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars());
     return getFlatClosureUpvars()[i];
 }
@@ -664,31 +711,31 @@ JSObject::getFlatClosureUpvar(uint32 i)
     return getFlatClosureUpvars()[i];
 }
 
 inline void
 JSObject::setFlatClosureUpvars(js::Value *upvars)
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate()));
-    getSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars);
+    getFixedSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars);
 }
 
 inline bool
 JSObject::hasMethodObj(const JSObject& obj) const
 {
     return JSSLOT_FUN_METHOD_OBJ < numSlots() &&
-           getSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
-           &getSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == &obj;
+           getFixedSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
+           &getFixedSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == &obj;
 }
 
 inline void
 JSObject::setMethodObj(JSObject& obj)
 {
-    getSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj);
+    getFixedSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj);
 }
 
 inline js::NativeIterator *
 JSObject::getNativeIterator() const
 {
     return (js::NativeIterator *) getPrivate();
 }
 
@@ -783,23 +830,23 @@ JSObject::setQNameLocalName(JSLinearStri
 {
     JS_ASSERT(isQName());
     setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue());
 }
 
 inline JSObject *
 JSObject::getWithThis() const
 {
-    return &getSlot(JSSLOT_WITH_THIS).toObject();
+    return &getFixedSlot(JSSLOT_WITH_THIS).toObject();
 }
 
 inline void
 JSObject::setWithThis(JSObject *thisp)
 {
-    getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp);
+    getFixedSlotRef(JSSLOT_WITH_THIS).setObject(*thisp);
 }
 
 inline js::types::TypeObject *
 JSObject::getNewType(JSContext *cx)
 {
     if (isDenseArray() && !makeDenseArraySlow(cx))
         return NULL;
     if (!newType)
@@ -866,57 +913,59 @@ JSObject::setTypeAndShape(js::types::Typ
     setLastProperty(newShape);
 }
 
 inline void
 JSObject::init(JSContext *cx, js::Class *aclasp, js::types::TypeObject *type,
                JSObject *parent, void *priv, bool useHoles)
 {
     clasp = aclasp;
+    flags = capacity << FIXED_SLOTS_SHIFT;
+
+    JS_ASSERT(useHoles == (aclasp == &js_ArrayClass));
 
 #ifdef DEBUG
     /*
      * NB: objShape must not be set here; rather, the caller must call setMap
      * or setSharedNonNativeMap after calling init. To defend this requirement
      * we set objShape to a value that obj->shape() is asserted never to return.
      */
     objShape = JSObjectMap::INVALID_SHAPE;
 #endif
 
     privateData = priv;
-    slots = fixedSlots();
 
     /*
      * Fill the fixed slots with undefined if needed.  This object must
      * already have its capacity filled in, as by js_NewGCObject.
      */
-    JS_ASSERT(capacity == numFixedSlots());
-    JS_ASSERT(useHoles == (aclasp == &js_ArrayClass));
+    slots = NULL;
+    ClearValueRange(fixedSlots(), capacity, useHoles);
     if (useHoles) {
+        slots = fixedSlots();
+        flags |= PACKED_ARRAY;
+    }
+
+    newType = NULL;
+    JS_ASSERT(initializedLength == 0);
         initializedLength = 0;
-        flags = PACKED_ARRAY;
-    } else {
-        ClearValueRange(slots, capacity, false);
-        newType = NULL;
-        flags = 0;
-    }
 
     setType(type);
     setParent(parent);
 }
 
 inline void
 JSObject::finish(JSContext *cx)
 {
 #ifdef DEBUG
     if (isNative())
         JS_LOCK_RUNTIME_VOID(cx->runtime, cx->runtime->liveObjectProps -= propertyCount());
 #endif
     if (hasSlotsArray())
-        freeSlotsArray(cx);
+        cx->free_(slots);
 }
 
 inline bool
 JSObject::initSharingEmptyShape(JSContext *cx,
                                 js::Class *aclasp,
                                 js::types::TypeObject *type,
                                 JSObject *parent,
                                 void *privateValue,
@@ -929,35 +978,16 @@ JSObject::initSharingEmptyShape(JSContex
     js::EmptyShape *empty = type->getEmptyShape(cx, aclasp, kind);
     if (!empty)
         return false;
 
     setMap(empty);
     return true;
 }
 
-inline void
-JSObject::freeSlotsArray(JSContext *cx)
-{
-    JS_ASSERT(hasSlotsArray());
-    cx->free_(slots);
-}
-
-inline void
-JSObject::revertToFixedSlots(JSContext *cx)
-{
-    JS_ASSERT(hasSlotsArray());
-    size_t fixed = numFixedSlots();
-    JS_ASSERT(capacity >= fixed);
-    memcpy(fixedSlots(), slots, fixed * sizeof(js::Value));
-    freeSlotsArray(cx);
-    slots = fixedSlots();
-    capacity = fixed;
-}
-
 inline bool
 JSObject::hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags)
 {
     JSObject *pobj;
     JSProperty *prop;
     JSAutoResolveFlags rf(cx, flags);
     if (!lookupProperty(cx, id, &pobj, &prop))
         return false;
@@ -1324,17 +1354,18 @@ static JS_ALWAYS_INLINE JSObject *
 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
 {
     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
     return NewObject<withProto>(cx, clasp, proto, parent, kind);
 }
 
 /*
  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
- * the object, zero if the final size is unknown.
+ * 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::FinalizeKind
 GuessObjectGCKind(size_t numSlots, bool isArray)
 {
     if (numSlots)
         return gc::GetGCObjectKind(numSlots);
     return isArray ? gc::FINALIZE_OBJECT8 : gc::FINALIZE_OBJECT4;
 }
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1193,28 +1193,16 @@ JSObject::removeProperty(JSContext *cx, 
     } else {
         /*
          * Non-dictionary-mode property tables are shared immutables, so all we
          * need do is retract lastProp and we'll either get or else lazily make
          * via a later hashify the exact table for the new property lineage.
          */
         JS_ASSERT(shape == lastProp);
         removeLastProperty();
-
-        /*
-         * Revert to fixed slots if this was the first dynamically allocated slot,
-         * preserving invariant that objects with the same shape use the fixed
-         * slots in the same way.
-         */
-        size_t fixed = numFixedSlots();
-        if (shape->slot == fixed) {
-            JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(),
-                         lastProp->slot == fixed - 1);
-            revertToFixedSlots(cx);
-        }
     }
     updateShape(cx);
 
     /* On the way out, consider shrinking table if its load factor is <= .25. */
     if (lastProp->hasTable()) {
         PropertyTable *table = lastProp->getTable();
         uint32 size = table->capacity();
         if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2) {
@@ -1249,24 +1237,16 @@ JSObject::clear(JSContext *cx)
         JS_ASSERT(inDictionaryMode() == shape->inDictionary());
     }
     JS_ASSERT(shape->isEmptyShape());
 
     if (inDictionaryMode())
         shape->listp = &lastProp;
 
     /*
-     * Revert to fixed slots if we have cleared below the first dynamically
-     * allocated slot, preserving invariant that objects with the same shape
-     * use the fixed slots in the same way.
-     */
-    if (hasSlotsArray() && JSSLOT_FREE(getClass()) <= numFixedSlots())
-        revertToFixedSlots(cx);
-
-    /*
      * We have rewound to a uniquely-shaped empty scope, so we don't need an
      * override for this object's shape.
      */
     clearOwnShape();
     setMap(shape);
 
     LeaveTraceIfGlobalObject(cx, this);
     JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2592,31 +2592,31 @@ TraceRecorder::w_immpIdGC(jsid id)
         tree->gcthings.addUnique(IdToValue(id));
     return w.immpNonGC((void*)JSID_BITS(id));
 }
 
 ptrdiff_t
 TraceRecorder::nativeGlobalSlot(const Value* p) const
 {
     JS_ASSERT(isGlobal(p));
-    return ptrdiff_t(p - globalObj->slots);
+    return ptrdiff_t(p - globalObj->slots + globalObj->numFixedSlots());
 }
 
 /* Determine the offset in the native global frame for a jsval we track. */
 ptrdiff_t
 TraceRecorder::nativeGlobalOffset(const Value* p) const
 {
     return nativeGlobalSlot(p) * sizeof(double);
 }
 
 /* Determine whether a value is a global stack slot. */
 bool
 TraceRecorder::isGlobal(const Value* p) const
 {
-    return (size_t(p - globalObj->slots) < globalObj->numSlots());
+    return (size_t(p - globalObj->slots) < globalObj->numSlots() - globalObj->numFixedSlots());
 }
 
 bool
 TraceRecorder::isVoidPtrGlobal(const void* p) const
 {
     return isGlobal((const Value *)p);
 }
 
@@ -3949,26 +3949,29 @@ TraceRecorder::known(JSObject** p)
  * This function check for that condition and re-maps the entries of the tracker
  * accordingly.
  */
 JS_REQUIRES_STACK void
 TraceRecorder::checkForGlobalObjectReallocationHelper()
 {
     debug_only_print0(LC_TMTracer, "globalObj->slots relocated, updating tracker\n");
     Value* src = global_slots;
-    Value* dst = globalObj->getSlots();
+    Value* dst = globalObj->getRawSlots();
     jsuint length = globalObj->capacity;
     LIns** map = (LIns**)alloca(sizeof(LIns*) * length);
     for (jsuint n = 0; n < length; ++n) {
-        map[n] = tracker.get(src);
-        tracker.set(src++, NULL);
-    }
-    for (jsuint n = 0; n < length; ++n)
-        tracker.set(dst++, map[n]);
-    global_slots = globalObj->getSlots();
+        Value *slot = globalObj->getRawSlot(n, src);
+        map[n] = tracker.get(slot);
+        tracker.set(slot, NULL);
+    }
+    for (jsuint n = 0; n < length; ++n) {
+        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:
@@ -9680,31 +9683,31 @@ TraceRecorder::stobj_set_dslot(LIns *obj
 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->hasSlotsArray()) {
+    if (obj->isFixedSlot(slot)) {
         JS_ASSERT(slot < obj->numSlots());
         stobj_set_fslot(obj_ins, slot, v, v_ins);
     } else {
-        stobj_set_dslot(obj_ins, slot, slots_ins, v, v_ins);
+        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)
 {
     /* Same guarantee about fixed slots as stobj_set_slot. */
-    Address addr = (!obj->hasSlotsArray())
+    Address addr = obj->isFixedSlot(slot)
                  ? (Address)FSlotsAddress(obj_ins, slot)
-                 : (Address)DSlotsAddress(w.ldpObjSlots(obj_ins), slot);
+                 : (Address)DSlotsAddress(w.ldpObjSlots(obj_ins), obj->dynamicSlotIndex(slot));
 
     return unbox_value(obj->getSlot(slot), addr, exit);
 }
 
 #if JS_BITS_PER_WORD == 32
 
 void
 TraceRecorder::box_undefined_into(Address addr)
@@ -12249,17 +12252,17 @@ TraceRecorder::setCallProp(JSObject *cal
         }
 
         // Now assert that the shortid get we did above was ok. Have to do it
         // after the RETURN_STOP above, since in that case we may in fact not
         // have a valid shortid; but we don't use it in that case anyway.
         JS_ASSERT(shape->hasShortID());
 
         LIns* slots_ins = NULL;
-        stobj_set_dslot(callobj_ins, slot, slots_ins, v, v_ins);
+        stobj_set_slot(callobj, callobj_ins, slot, slots_ins, v, v_ins);
         return RECORD_CONTINUE;
     }
 
     // This is the hard case: we have a JSStackFrame private, but it's not in
     // range.  During trace execution we may or may not have a JSStackFrame
     // anymore.  Call the standard builtins, which handle that situation.
 
     // Set variables in off-trace-stack call objects by calling standard builtins.
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1236,17 +1236,17 @@ class TraceRecorder
     JS_REQUIRES_STACK bool known(const Value* p);
     JS_REQUIRES_STACK bool known(JSObject** p);
     /*
      * The slots of the global object are sometimes reallocated by the
      * interpreter.  This function checks for that condition and re-maps the
      * entries of the tracker accordingly.
      */
     JS_REQUIRES_STACK void checkForGlobalObjectReallocation() {
-        if (global_slots != globalObj->getSlots())
+        if (global_slots != globalObj->getRawSlots())
             checkForGlobalObjectReallocationHelper();
     }
     JS_REQUIRES_STACK void checkForGlobalObjectReallocationHelper();
 
     JS_REQUIRES_STACK TypeConsensus selfTypeStability(SlotMap& smap);
     JS_REQUIRES_STACK TypeConsensus peerTypeStability(SlotMap& smap, const void* ip,
                                                       TreeFragment** peer);
 
@@ -1634,17 +1634,17 @@ 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.
              */
-            Value *vp = globalObj->getSlots() + slot;
+            Value *vp = globalObj->getRawSlot(slot, globalObj->getRawSlots());
 
             /* 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/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -319,16 +319,28 @@ JSWrapper::fun_toString(JSContext *cx, J
 }
 
 void
 JSWrapper::trace(JSTracer *trc, JSObject *wrapper)
 {
     MarkObject(trc, *wrappedObject(wrapper), "wrappedObject");
 }
 
+JSObject *
+JSWrapper::wrappedObject(const JSObject *wrapper)
+{
+    return wrapper->getProxyPrivate().toObjectOrNull();
+}
+
+JSWrapper *
+JSWrapper::wrapperHandler(const JSObject *wrapper)
+{
+    return static_cast<JSWrapper *>(wrapper->getProxyHandler());
+}
+
 bool
 JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
 {
     *bp = true;
     return true;
 }
 
 void
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -96,22 +96,18 @@ class JS_FRIEND_API(JSWrapper) : public 
     virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp);
     virtual void leave(JSContext *cx, JSObject *wrapper);
 
     static JSWrapper singleton;
 
     static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
                          JSWrapper *handler);
 
-    static inline JSObject *wrappedObject(const JSObject *wrapper) {
-        return wrapper->getProxyPrivate().toObjectOrNull();
-    }
-    static inline JSWrapper *wrapperHandler(const JSObject *wrapper) {
-        return static_cast<JSWrapper *>(wrapper->getProxyHandler());
-    }
+    static JSObject *wrappedObject(const JSObject *wrapper);
+    static JSWrapper *wrapperHandler(const JSObject *wrapper);
 
     enum {
         CROSS_COMPARTMENT = 1 << 0,
         LAST_USED_FLAG = CROSS_COMPARTMENT
     };
 
     static void *getWrapperFamily();
 };
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -201,19 +201,23 @@ static const JSC::MacroAssembler::Regist
         return branchPtr(cond, Address(fun, offsetof(JSObject, clasp)),
                          ImmPtr(&js_FunctionClass));
     }
 
     /*
      * Finds and returns the address of a known object and slot.
      */
     Address objSlotRef(JSObject *obj, RegisterID reg, uint32 slot) {
-        move(ImmPtr(&obj->slots), reg);
-        loadPtr(reg, reg);
-        return Address(reg, slot * sizeof(Value));
+        move(ImmPtr(obj), reg);
+        if (obj->isFixedSlot(slot)) {
+            return Address(reg, JSObject::getFixedSlotOffset(slot));
+        } else {
+            loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
+            return Address(reg, obj->dynamicSlotIndex(slot) * sizeof(Value));
+        }
     }
 
 #ifdef JS_CPU_X86
     void idiv(RegisterID reg) {
         m_assembler.cdq();
         m_assembler.idivl_r(reg);
     }
 
@@ -720,17 +724,17 @@ static const JSC::MacroAssembler::Regist
                                      RegisterID typeReg, RegisterID dataReg) {
         JS_ASSERT(objReg != typeReg);
 
         FastArrayLoadFails fails;
         fails.rangeCheck = guardArrayExtent(offsetof(JSObject, initializedLength),
                                             objReg, key, BelowOrEqual);
 
         RegisterID dslotsReg = objReg;
-        loadPtr(Address(objReg, offsetof(JSObject, slots)), dslotsReg);
+        loadPtr(Address(objReg, JSObject::offsetOfSlots()), dslotsReg);
 
         // Load the slot out of the array.
         if (key.isConstant()) {
             Address slot(objReg, key.index() * sizeof(Value));
             fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg);
         } else {
             BaseIndex slot(objReg, key.reg(), JSVAL_SCALE);
             fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg);
@@ -795,32 +799,40 @@ static const JSC::MacroAssembler::Regist
 
     void rematPayload(const StateRemat &remat, RegisterID reg) {
         if (remat.inMemory())
             loadPayload(remat.address(), reg);
         else
             move(remat.reg(), reg);
     }
 
-    void loadDynamicSlot(RegisterID objReg, uint32 slot,
+    void loadDynamicSlot(RegisterID objReg, uint32 index,
                          RegisterID typeReg, RegisterID dataReg) {
-        loadPtr(Address(objReg, offsetof(JSObject, slots)), dataReg);
-        loadValueAsComponents(Address(dataReg, slot * sizeof(Value)), typeReg, dataReg);
+        loadPtr(Address(objReg, JSObject::offsetOfSlots()), dataReg);
+        loadValueAsComponents(Address(dataReg, index * sizeof(Value)), typeReg, dataReg);
     }
 
     void loadObjProp(JSObject *obj, RegisterID objReg,
                      const js::Shape *shape,
                      RegisterID typeReg, RegisterID dataReg)
     {
         if (shape->isMethod())
             loadValueAsComponents(ObjectValue(shape->methodObject()), typeReg, dataReg);
-        else if (obj->hasSlotsArray())
-            loadDynamicSlot(objReg, shape->slot, typeReg, dataReg);
+        else if (obj->isFixedSlot(shape->slot))
+            loadInlineSlot(objReg, shape->slot, typeReg, dataReg);
         else
-            loadInlineSlot(objReg, shape->slot, typeReg, dataReg);
+            loadDynamicSlot(objReg, obj->dynamicSlotIndex(shape->slot), typeReg, dataReg);
+    }
+
+    Address objPropAddress(JSObject *obj, RegisterID objReg, uint32 slot)
+    {
+        if (obj->isFixedSlot(slot))
+            return Address(objReg, JSObject::getFixedSlotOffset(slot));
+        loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
+        return Address(objReg, obj->dynamicSlotIndex(slot) * sizeof(Value));
     }
 
     static uint32 maskAddress(Address address) {
         return Registers::maskReg(address.base);
     }
 
     static uint32 maskAddress(BaseIndex address) {
         return Registers::maskReg(address.base) |
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -2476,18 +2476,17 @@ mjit::Compiler::generateMethod()
             uintN index = GET_UINT16(PC);
 
             // Load the callee's payload into a register.
             frame.pushCallee();
             RegisterID reg = frame.copyDataIntoReg(frame.peek(-1));
             frame.pop();
 
             // obj->getFlatClosureUpvars()
-            masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg);
-            Address upvarAddress(reg, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS * sizeof(Value));
+            Address upvarAddress(reg, JSObject::getFlatClosureUpvarsOffset());
             masm.loadPrivate(upvarAddress, reg);
             // push ((Value *) reg)[index]
             frame.freeReg(reg);
             frame.push(Address(reg, index * sizeof(Value)), knownPushedType(0));
             if (op == JSOP_CALLFCSLOT)
                 frame.push(UndefinedValue());
           }
           END_CASE(JSOP_CALLFCSLOT)
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -2009,21 +2009,21 @@ mjit::Compiler::jsop_initprop()
 #ifdef DEBUG
     int res =
 #endif
     js_LookupPropertyWithFlags(cx, baseobj, ATOM_TO_JSID(atom),
                                JSRESOLVE_QUALIFIED, &holder, &prop);
     JS_ASSERT(res >= 0 && prop && holder == baseobj);
 
     RegisterID objReg = frame.copyDataIntoReg(obj);
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
 
     /* Perform the store. */
     Shape *shape = (Shape *) prop;
-    frame.storeTo(fe, Address(objReg, shape->slot * sizeof(Value)));
+    Address address = masm.objPropAddress(baseobj, objReg, shape->slot);
+    frame.storeTo(fe, address);
     frame.freeReg(objReg);
 }
 
 void
 mjit::Compiler::jsop_initelem()
 {
     FrameEntry *obj = frame.peek(-3);
     FrameEntry *id = frame.peek(-2);
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -498,13 +498,13 @@ LoopState::restoreInvariants(Assembler &
         const InvariantArraySlots &entry = invariantArraySlots[i];
         FrameEntry *fe = frame.getTemporary(entry.temporary);
 
         Address array = frame.addressOf(entry.arraySlot);
         Address address = frame.addressOf(fe);
 
         RegisterID reg = regs.takeAnyReg().reg();
         masm.loadPayload(array, reg);
-        masm.loadPtr(Address(reg, offsetof(JSObject, slots)), reg);
+        masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
         masm.storePtr(reg, address);
         regs.putReg(reg);
     }
 }
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -99,18 +99,19 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlo
     }
     uint32 slot = shape->slot;
 
     /* Patch shape guard. */
     Repatcher repatcher(f.jit());
     repatcher.repatch(ic->fastPathStart.dataLabel32AtOffset(ic->shapeOffset), obj->shape());
 
     /* Patch loads. */
+    uint32 index = obj->dynamicSlotIndex(slot);
     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
-    repatcher.patchAddressOffsetForValueLoad(label, slot * sizeof(Value));
+    repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
 
     /* Do load anyway... this time. */
     stubs::GetGlobalName(f);
 }
 
 template <JSBool strict>
 static void JS_FASTCALL
 DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic)
@@ -165,19 +166,20 @@ SetGlobalNameIC::patchInlineShapeGuard(R
 
 static LookupStatus
 UpdateSetGlobalNameStub(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape)
 {
     Repatcher repatcher(ic->extraStub);
 
     ic->patchExtraShapeGuard(repatcher, obj->shape());
 
+    uint32 index = obj->dynamicSlotIndex(shape->slot);
     JSC::CodeLocationLabel label(JSC::MacroAssemblerCodePtr(ic->extraStub.start()));
     label = label.labelAtOffset(ic->extraStoreOffset);
-    repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value),
+    repatcher.patchAddressOffsetForValueStore(label, index * sizeof(Value),
                                               ic->vr.isTypeKnown());
 
     return Lookup_Cacheable;
 }
 
 static LookupStatus
 AttachSetGlobalNameStub(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, const Shape *shape)
 {
@@ -194,30 +196,31 @@ AttachSetGlobalNameStub(VMFrame &f, ic::
         masm.move(ImmPtr(obj), ic->objReg);
 
     JS_ASSERT(obj->branded());
 
     /*
      * Load obj->slots. If ic->objConst, then this clobbers objReg, because
      * ic->objReg == ic->shapeReg.
      */
-    masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg);
+    JS_ASSERT(!obj->isFixedSlot(shape->slot));
+    masm.loadPtr(Address(ic->objReg, JSObject::offsetOfSlots()), ic->shapeReg);
 
     /* Test if overwriting a function-tagged slot. */
-    Address slot(ic->shapeReg, sizeof(Value) * shape->slot);
+    Address slot(ic->shapeReg, sizeof(Value) * obj->dynamicSlotIndex(shape->slot));
     Jump isNotObject = masm.testObject(Assembler::NotEqual, slot);
 
     /* Now, test if the object is a function object. */
     masm.loadPayload(slot, ic->shapeReg);
     Jump isFun = masm.testFunction(Assembler::Equal, ic->shapeReg);
 
     /* Restore shapeReg to obj->slots, since we clobbered it. */
     if (ic->objConst)
         masm.move(ImmPtr(obj), ic->objReg);
-    masm.loadPtr(Address(ic->objReg, offsetof(JSObject, slots)), ic->shapeReg);
+    masm.loadPtr(Address(ic->objReg, JSObject::offsetOfSlots()), ic->shapeReg);
 
     /* If the object test fails, shapeReg is still obj->slots. */
     isNotObject.linkTo(masm.label(), &masm);
     DataLabel32 store = masm.storeValueWithAddressOffsetPatch(ic->vr, slot);
 
     Jump done = masm.jump();
 
     JITScript *jit = f.jit();
@@ -299,18 +302,19 @@ UpdateSetGlobalName(VMFrame &f, ic::SetG
 
         return AttachSetGlobalNameStub(f, ic, obj, shape);
     }
 
     /* Object is not branded, so we can use the inline path. */
     Repatcher repatcher(f.jit());
     ic->patchInlineShapeGuard(repatcher, obj->shape());
 
+    uint32 index = obj->dynamicSlotIndex(shape->slot);
     JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
-    repatcher.patchAddressOffsetForValueStore(label, shape->slot * sizeof(Value),
+    repatcher.patchAddressOffsetForValueStore(label, index * sizeof(Value),
                                               ic->vr.isTypeKnown());
 
     return Lookup_Cacheable;
 }
 
 void JS_FASTCALL
 ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -212,42 +212,42 @@ class SetPropCompiler : public PICStubCo
                           int32(JSObjectMap::INVALID_SHAPE));
         repatcher.relink(labels.getInlineShapeJump(pic.fastPathStart.labelAtOffset(pic.shapeGuard)),
                          pic.slowPathStart);
 
         FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp));
         repatcher.relink(pic.slowPathCall, target);
     }
 
-    LookupStatus patchInline(const Shape *shape, bool inlineSlot)
+    LookupStatus patchInline(const Shape *shape)
     {
         JS_ASSERT(!pic.inlinePathPatched);
         JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress());
 
         Repatcher repatcher(f.jit());
         SetPropLabels &labels = pic.setPropLabels();
 
         int32 offset;
-        if (inlineSlot) {
+        if (obj->isFixedSlot(shape->slot)) {
             CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin, pic.u.vr);
             repatcher.repatchLoadPtrToLEA(istr);
 
             // 
             // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
             // To:           | lea fslots, [obj + DSLOTS_OFFSET]
             //
             // Because the offset is wrong, it's necessary to correct it
             // below.
             //
             int32 diff = int32(JSObject::getFixedSlotOffset(0)) -
-                         int32(offsetof(JSObject, slots));
+                         int32(JSObject::offsetOfSlots());
             JS_ASSERT(diff != 0);
             offset  = (int32(shape->slot) * sizeof(Value)) + diff;
         } else {
-            offset = shape->slot * sizeof(Value);
+            offset = obj->dynamicSlotIndex(shape->slot) * sizeof(Value);
         }
 
         repatcher.repatch(labels.getInlineShapeData(pic.fastPathStart, pic.shapeGuard),
                           obj->shape());
         repatcher.patchAddressOffsetForValueStore(labels.getInlineValueStore(pic.fastPathRejoin,
                                                                              pic.u.vr),
                                                   offset, pic.u.vr.isTypeKnown());
 
@@ -273,17 +273,17 @@ class SetPropCompiler : public PICStubCo
         } else {
             CodeLocationLabel shapeGuard = label.labelAtOffset(pic.shapeGuard);
             repatcher.relink(pic.setPropLabels().getInlineShapeJump(shapeGuard), cs);
         }
         if (int secondGuardOffset = getLastStubSecondShapeGuard())
             repatcher.relink(label.jumpAtOffset(secondGuardOffset), cs);
     }
 
-    LookupStatus generateStub(uint32 initialShape, const Shape *shape, bool adding, bool inlineSlot)
+    LookupStatus generateStub(uint32 initialShape, const Shape *shape, bool adding)
     {
         /* Exits to the slow path. */
         Vector<Jump, 8> slowExits(cx);
         Vector<Jump, 8> otherGuards(cx);
 
         Assembler masm;
 
         // Shape guard.
@@ -334,31 +334,31 @@ class SetPropCompiler : public PICStubCo
                 } else {
                     Jump mismatchedFunction =
                         masm.branchPtr(Assembler::NotEqual, pic.u.vr.dataReg(), ImmPtr(funobj));
                     if (!slowExits.append(mismatchedFunction))
                         return error();
                 }
             }
 
-            if (inlineSlot) {
+            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(masm.payloadOf(capacity), pic.shapeReg);
                 Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg,
                                                   Imm32(shape->slot));
                 if (!slowExits.append(overCapacity))
                     return error();
 
-                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.shapeReg);
-                Address address(pic.shapeReg, shape->slot * sizeof(Value));
+                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);
             }
 
             uint32 newShape = obj->shape();
             JS_ASSERT(newShape != initialShape);
 
             /* Write the object's new shape. */
             masm.storePtr(ImmPtr(shape), Address(pic.objReg, offsetof(JSObject, lastProp)));
@@ -369,21 +369,17 @@ class SetPropCompiler : public PICStubCo
                 Address flags(pic.objReg, offsetof(JSObject, flags));
 
                 /* Use shapeReg to load, bitwise-or, and store flags. */
                 masm.load32(flags, pic.shapeReg);
                 masm.or32(Imm32(JSObject::METHOD_BARRIER), pic.shapeReg);
                 masm.store32(pic.shapeReg, flags);
             }
         } else if (shape->hasDefaultSetter()) {
-            Address address(pic.objReg, JSObject::getFixedSlotOffset(shape->slot));
-            if (!inlineSlot) {
-                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-                address = Address(pic.objReg, shape->slot * sizeof(Value));
-            }
+            Address address = masm.objPropAddress(obj, pic.objReg, shape->slot);
 
             // If the scope is branded, or has a method barrier. It's now necessary
             // to guard that we're not overwriting a function-valued property.
             if (obj->brandedOrHasMethodBarrier()) {
                 masm.loadTypeTag(address, pic.shapeReg);
                 Jump skip = masm.testObject(Assembler::NotEqual, pic.shapeReg);
                 masm.loadPayload(address, pic.shapeReg);
                 Jump rebrand = masm.testFunction(Assembler::Equal, pic.shapeReg);
@@ -416,20 +412,21 @@ class SetPropCompiler : public PICStubCo
                 masm.storeValue(pic.u.vr, addr);
                 skipOver = masm.jump();
             }
 
             escapedFrame.linkTo(masm.label(), &masm);
             {
                 if (shape->setterOp() == SetCallVar)
                     slot += fun->nargs;
-                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-
-                Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value));
-                masm.storeValue(pic.u.vr, dslot);
+
+                slot += JSObject::CALL_RESERVED_SLOTS;
+                Address address = masm.objPropAddress(obj, pic.objReg, slot);
+
+                masm.storeValue(pic.u.vr, address);
             }
 
             pic.shapeRegHasBaseShape = false;
         }
 
         Jump done = masm.jump();
 
         // Common all secondary guards into one big exit.
@@ -612,17 +609,17 @@ class SetPropCompiler : public PICStubCo
             if (pic.typeMonitored) {
                 RecompilationMonitor monitor(cx);
                 if (!cx->addTypePropertyId(obj->getType(), shape->id, pic.rhsTypes))
                     return error();
                 if (monitor.recompiled())
                     return Lookup_Uncacheable;
             }
 
-            return generateStub(initialShape, shape, true, !obj->hasSlotsArray());
+            return generateStub(initialShape, shape, true);
         }
 
         const Shape *shape = (const Shape *) prop;
         if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod())
             return disable("set method on non-method shape");
         if (!shape->writable())
             return disable("readonly");
 
@@ -661,20 +658,20 @@ class SetPropCompiler : public PICStubCo
             }
         }
 
         JS_ASSERT(obj == holder);
         if (!pic.inlinePathPatched &&
             !obj->brandedOrHasMethodBarrier() &&
             shape->hasDefaultSetter() &&
             !obj->isDenseArray()) {
-            return patchInline(shape, !obj->hasSlotsArray());
-        } 
-
-        return generateStub(obj->shape(), shape, false, !obj->hasSlotsArray());
+            return patchInline(shape);
+        }
+
+        return generateStub(obj->shape(), shape, false);
     }
 };
 
 static bool
 IsCacheableProtoChain(JSObject *obj, JSObject *holder)
 {
     while (obj != holder) {
         JSObject *proto = obj->getProto();
@@ -816,19 +813,17 @@ class GetPropCompiler : public PICStubCo
     }
 
     LookupStatus generateArgsLengthStub()
     {
         Assembler masm;
 
         Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
 
-        masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-        masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)),
-                    pic.objReg);
+        masm.load32(Address(pic.objReg, JSObject::getArgumentsLengthOffset()), pic.objReg);
         masm.move(pic.objReg, pic.shapeReg);
         Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
                                             Imm32(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT));
         masm.rshift32(Imm32(JSObject::ARGS_PACKED_BITS_COUNT), pic.objReg);
         
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
@@ -894,19 +889,19 @@ class GetPropCompiler : public PICStubCo
         return Lookup_Cacheable;
     }
 
     LookupStatus generateStringObjLengthStub()
     {
         Assembler masm;
 
         Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
-        masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-        masm.loadPayload(Address(pic.objReg, JSObject::JSSLOT_PRIMITIVE_THIS * sizeof(Value)),
-                         pic.objReg);
+
+
+        masm.loadPayload(Address(pic.objReg, JSObject::getPrimitiveThisOffset()), pic.objReg);
         masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
         masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
@@ -1047,33 +1042,33 @@ class GetPropCompiler : public PICStubCo
 
     LookupStatus patchInline(JSObject *holder, const Shape *shape)
     {
         spew("patch", "inline");
         Repatcher repatcher(f.jit());
         GetPropLabels &labels = pic.getPropLabels();
 
         int32 offset;
-        if (!holder->hasSlotsArray()) {
+        if (holder->isFixedSlot(shape->slot)) {
             CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin);
             repatcher.repatchLoadPtrToLEA(istr);
 
             // 
             // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
             // To:           | lea fslots, [obj + DSLOTS_OFFSET]
             //
             // Because the offset is wrong, it's necessary to correct it
             // below.
             //
             int32 diff = int32(JSObject::getFixedSlotOffset(0)) -
-                         int32(offsetof(JSObject, slots));
+                         int32(JSObject::offsetOfSlots());
             JS_ASSERT(diff != 0);
             offset  = (int32(shape->slot) * sizeof(Value)) + diff;
         } else {
-            offset = shape->slot * sizeof(Value);
+            offset = holder->dynamicSlotIndex(shape->slot) * sizeof(Value);
         }
 
         repatcher.repatch(labels.getInlineShapeData(pic.getFastShapeGuard()), obj->shape());
         repatcher.patchAddressOffsetForValueLoad(labels.getValueLoad(pic.fastPathRejoin), offset);
 
         pic.inlinePathPatched = true;
 
         return Lookup_Cacheable;
@@ -1409,24 +1404,24 @@ class ScopeNameCompiler : public PICStub
             masm.loadPayload(addr, pic.objReg);
             masm.loadTypeTag(addr, pic.shapeReg);
             skipOver = masm.jump();
         }
 
         escapedFrame.linkTo(masm.label(), &masm);
 
         {
-            masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
-
             if (kind == VAR)
                 slot += fun->nargs;
-            Address dslot(pic.objReg, (slot + JSObject::CALL_RESERVED_SLOTS) * sizeof(Value));
+
+            slot += JSObject::CALL_RESERVED_SLOTS;
+            Address address = masm.objPropAddress(obj, pic.objReg, slot);
 
             /* Safe because type is loaded first. */
-            masm.loadValueAsComponents(dslot, pic.shapeReg, pic.objReg);
+            masm.loadValueAsComponents(address, pic.shapeReg, pic.objReg);
         }
 
         skipOver.linkTo(masm.label(), &masm);
         Jump done = masm.jump();
 
         // All failures flow to here, so there is a common point to patch.
         for (Jump *pj = fails.begin(); pj != fails.end(); ++pj)
             pj->linkTo(masm.label(), &masm);
@@ -2671,17 +2666,17 @@ SetElementIC::attachHoleStub(JSContext *
         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);
 
     // Store the value back.
-    masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
+    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/tracejit/Writer.cpp
+++ b/js/src/tracejit/Writer.cpp
@@ -261,25 +261,31 @@ couldBeObjectOrString(LIns *ins)
     }
 
     return ret;
 }
 
 static bool
 isConstPrivatePtr(LIns *ins, unsigned slot)
 {
+    // match both inline and indirect slot accesses.
+    uint32 inlineOffset = JSObject::getFixedSlotOffset(slot) + sPayloadOffset;
+    uint32 oolOffset = (slot * sizeof(Value)) + sPayloadOffset;
+
 #if JS_BITS_PER_WORD == 32
     // ins = ldp.slots/c ...[<offset of slot>]
-    return match(ins, LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value) + sPayloadOffset);
+    return match(ins, LIR_ldp, ACCSET_SLOTS, LOAD_CONST, inlineOffset)
+        || match(ins, LIR_ldp, ACCSET_SLOTS, LOAD_CONST, oolOffset);
 #elif JS_BITS_PER_WORD == 64
     // ins_oprnd1 = ldp.slots/c ...[<offset of slot>]
     // ins_oprnd2 = immi 1
     // ins = lshq ins_oprnd1, ins_oprnd2
     return ins->isop(LIR_lshq) &&
-           match(ins->oprnd1(), LIR_ldp, ACCSET_SLOTS, LOAD_CONST, slot * sizeof(Value)) &&
+           (match(ins->oprnd1(), LIR_ldp, ACCSET_SLOTS, LOAD_CONST, inlineOffset)
+         || match(ins->oprnd1(), LIR_ldp, ACCSET_SLOTS, LOAD_CONST, oolOffset)) &&
            ins->oprnd2()->isImmI(1);
 #endif
 }
 
 /*
  * Any time you use an AccSet annotation other than ACCSET_ALL, you are making
  * a promise to Nanojit about the properties of the annotated load/store/call.
  * If that annotation is wrong, it could cause rare and subtle bugs.  So this
--- a/js/src/tracejit/Writer.h
+++ b/js/src/tracejit/Writer.h
@@ -519,17 +519,17 @@ class Writer
 
     nj::LIns *ldiDenseArrayInitializedLength(nj::LIns *array) const {
         return name(lir->insLoad(nj::LIR_ldi, array, offsetof(JSObject, initializedLength),
                                  ACCSET_OBJ_CAPACITY),
                     "capacity");
     }
 
     nj::LIns *ldpObjSlots(nj::LIns *obj) const {
-        return name(lir->insLoad(nj::LIR_ldp, obj, offsetof(JSObject, slots), ACCSET_OBJ_SLOTS),
+        return name(lir->insLoad(nj::LIR_ldp, obj, JSObject::offsetOfSlots(), ACCSET_OBJ_SLOTS),
                     "slots");
     }
 
     nj::LIns *ldiConstTypedArrayLength(nj::LIns *array) const {
         return name(lir->insLoad(nj::LIR_ldi, array, js::TypedArray::lengthOffset(), ACCSET_TARRAY,
                                  nj::LOAD_CONST),
                     "typedArrayLength");
     }
@@ -1174,28 +1174,25 @@ class Writer
         return lir->ins2(op, x, y);
     }
 
     /* Operations involving non-trivial combinations of multiple instructions. */
 
     /*
      * Nb: this "Privatized" refers to the Private API in jsvalue.h.  It
      * doesn't refer to the JSObj::privateData slot!  Confusing.
+     * This should only be used for slots which are known to always be fixed.
      */
     nj::LIns *getObjPrivatizedSlot(nj::LIns *obj, uint32 slot) const {
+        uint32 offset = JSObject::getFixedSlotOffset(slot) + sPayloadOffset;
 #if JS_BITS_PER_WORD == 32
-        nj::LIns *vaddr_ins = ldpObjSlots(obj);
-        return lir->insLoad(nj::LIR_ldi, vaddr_ins,
-                            slot * sizeof(Value) + sPayloadOffset, ACCSET_SLOTS, nj::LOAD_CONST);
-
+        return lir->insLoad(nj::LIR_ldi, obj, offset, ACCSET_SLOTS, nj::LOAD_CONST);
 #elif JS_BITS_PER_WORD == 64
         /* N.B. On 64-bit, privatized value are encoded differently from other pointers. */
-        nj::LIns *vaddr_ins = ldpObjSlots(obj);
-        nj::LIns *v_ins = lir->insLoad(nj::LIR_ldq, vaddr_ins,
-                                       slot * sizeof(Value) + sPayloadOffset,
+        nj::LIns *v_ins = lir->insLoad(nj::LIR_ldq, obj, offset,
                                        ACCSET_SLOTS, nj::LOAD_CONST);
         return lshqN(v_ins, 1);
 #endif
     }
 
     nj::LIns *getDslotAddress(nj::LIns *obj, nj::LIns *idx) const {
         JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this.
         nj::LIns *offset = lshpN(ui2p(idx), 3);
@@ -1211,20 +1208,18 @@ class Writer
     nj::LIns *getStringChar(nj::LIns *str, nj::LIns *idx) const {
         nj::LIns *chars = ldpStringChars(str);
         return name(lir->insLoad(nj::LIR_ldus2ui, addp(chars, lshpN(idx, 1)), 0,
                                  ACCSET_STRING_MCHARS, nj::LOAD_CONST),
                     "strChar");
     }
 
     nj::LIns *getArgsLength(nj::LIns *args) const {
-        uint32 slot = JSObject::JSSLOT_ARGS_LENGTH;
-        nj::LIns *vaddr_ins = ldpObjSlots(args);
-        return name(lir->insLoad(nj::LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset,
-                                 ACCSET_SLOTS),
+        uint32 offset = JSObject::getArgumentsLengthOffset() + sPayloadOffset;
+        return name(lir->insLoad(nj::LIR_ldi, args, offset, ACCSET_SLOTS),
                     "argsLength");
     }
 };
 
 }   /* namespace tjit */
 }   /* namespace js */
 
 #endif /* tracejit_Writer_h___ */