Remove shape numbers and Shape::slotSpan, factor Shape getter/setter into BaseShape, bug 684505.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 28 Sep 2011 15:04:55 -0700
changeset 81234 ff51ddfdf5d1c04f37c272b745454af2d58be08d
parent 77569 44ef245b870628514f0fd9ba667788b56b0ecd65
child 81235 55a63871f966714c7ef78fca2f945538d7ed8f59
push id21565
push userbhackett@mozilla.com
push dateSat, 03 Dec 2011 20:25:52 +0000
treeherdermozilla-central@13afcd4c097c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs684505
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Remove shape numbers and Shape::slotSpan, factor Shape getter/setter into BaseShape, bug 684505.
js/src/assembler/assembler/AbstractMacroAssembler.h
js/src/assembler/assembler/RepatchBuffer.h
js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsbuiltins.cpp
js/src/jscell.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsgcmark.cpp
js/src/jsgcstats.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsopcode.tbl
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jspropertycache.cpp
js/src/jspropertycache.h
js/src/jspropertycacheinlines.h
js/src/jspropertytree.cpp
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsregexpinlines.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jstypedarray.cpp
js/src/jswatchpoint.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/BaseCompiler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/ICLabels.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/MonoIC.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PolyIC.h
js/src/methodjit/StubCalls-inl.h
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
js/src/shell/js.cpp
js/src/tracejit/Writer.cpp
js/src/tracejit/Writer.h
js/src/vm/CallObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/xpconnect/src/nsXPConnect.cpp
--- a/js/src/assembler/assembler/AbstractMacroAssembler.h
+++ b/js/src/assembler/assembler/AbstractMacroAssembler.h
@@ -559,16 +559,21 @@ public:
         return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
     }
 
     ptrdiff_t differenceBetween(DataLabel32 from, Label to)
     {
         return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
     }
 
+    ptrdiff_t differenceBetween(DataLabelPtr from, Label to)
+    {
+        return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
+    }
+
     ptrdiff_t differenceBetween(DataLabelPtr from, Jump to)
     {
         return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_jmp);
     }
 
     ptrdiff_t differenceBetween(DataLabelPtr from, DataLabelPtr to)
     {
         return AssemblerType::getDifferenceBetweenLabels(from.m_label, to.m_label);
--- a/js/src/assembler/assembler/RepatchBuffer.h
+++ b/js/src/assembler/assembler/RepatchBuffer.h
@@ -105,19 +105,19 @@ public:
         MacroAssembler::repatchNearCall(nearCall, destination);
     }
 
     void repatch(CodeLocationDataLabel32 dataLabel32, int32_t value)
     {
         MacroAssembler::repatchInt32(dataLabel32, value);
     }
 
-    void repatch(CodeLocationDataLabelPtr dataLabelPtr, void* value)
+    void repatch(CodeLocationDataLabelPtr dataLabelPtr, const void* value)
     {
-        MacroAssembler::repatchPointer(dataLabelPtr, value);
+        MacroAssembler::repatchPointer(dataLabelPtr, (void*) value);
     }
 
     void repatchLoadPtrToLEA(CodeLocationInstruction instruction)
     {
         MacroAssembler::repatchLoadPtrToLEA(instruction);
     }
 
     void repatchLEAToLoadPtr(CodeLocationInstruction instruction)
--- a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
+++ b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
@@ -1,14 +1,14 @@
 // |jit-test| debug
 setDebug(true);
 x = "notset";
 function main() {
   /* The JSOP_STOP in main. */
-  a = { valueOf: function () { trap(main, 58, "success()"); } };
+  a = { valueOf: function () { trap(main, 57, "success()"); } };
   b = "";
   eval();
   a + b;
   x = "failure";
 }
 function success() { x = "success"; }
 
 main();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2251,16 +2251,20 @@ JS_PrintTraceThingInfo(char *buf, size_t
       case JSTRACE_SCRIPT:
         name = "script";
         break;
 
       case JSTRACE_SHAPE:
         name = "shape";
         break;
 
+      case JSTRACE_BASE_SHAPE:
+        name = "base_shape";
+        break;
+
       case JSTRACE_TYPE_OBJECT:
         name = "type_object";
         break;
 
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
         name = "xml";
         break;
@@ -2314,16 +2318,17 @@ JS_PrintTraceThingInfo(char *buf, size_t
           case JSTRACE_SCRIPT:
           {
             JSScript *script = static_cast<JSScript *>(thing);
             JS_snprintf(buf, bufsize, "%s:%u", script->filename, unsigned(script->lineno));
             break;
           }
 
           case JSTRACE_SHAPE:
+          case JSTRACE_BASE_SHAPE:
           case JSTRACE_TYPE_OBJECT:
             break;
 
 #if JS_HAS_XML_SUPPORT
           case JSTRACE_XML:
           {
             extern const char *js_xml_class_str[];
             JSXML *xml = (JSXML *)thing;
@@ -3214,23 +3219,23 @@ LookupResult(JSContext *cx, JSObject *ob
         vp->setUndefined();
         return JS_TRUE;
     }
 
     if (obj2->isNative()) {
         Shape *shape = (Shape *) prop;
 
         if (shape->isMethod()) {
-            vp->setObject(shape->methodObject());
+            vp->setObject(*obj2->nativeGetMethod(shape));
             return !!obj2->methodReadBarrier(cx, *shape, vp);
         }
 
         /* Peek at the native property's slot value, without doing a Get. */
-        if (obj2->containsSlot(shape->slot)) {
-            *vp = obj2->nativeGetSlot(shape->slot);
+        if (shape->hasSlot()) {
+            *vp = obj2->nativeGetSlot(shape->slot());
             return true;
         }
     } else {
         if (obj2->isDenseArray())
             return js_GetDenseArrayElementValue(cx, obj2, id, vp);
         if (obj2->isProxy()) {
             AutoPropertyDescriptorRooter desc(cx);
             if (!JSProxy::getPropertyDescriptor(cx, obj2, id, false, &desc))
@@ -3591,22 +3596,22 @@ GetPropertyDescriptorById(JSContext *cx,
     desc->obj = obj2;
     if (obj2->isNative()) {
         Shape *shape = (Shape *) prop;
         desc->attrs = shape->attributes();
 
         if (shape->isMethod()) {
             desc->getter = JS_PropertyStub;
             desc->setter = JS_StrictPropertyStub;
-            desc->value.setObject(shape->methodObject());
+            desc->value.setObject(*obj2->nativeGetMethod(shape));
         } else {
             desc->getter = shape->getter();
             desc->setter = shape->setter();
-            if (obj2->containsSlot(shape->slot))
-                desc->value = obj2->nativeGetSlot(shape->slot);
+            if (shape->hasSlot())
+                desc->value = obj2->nativeGetSlot(shape->slot());
             else
                 desc->value.setUndefined();
         }
     } else {
         if (obj2->isProxy()) {
             JSAutoResolveFlags rf(cx, flags);
             return own
                    ? JSProxy::getOwnPropertyDescriptor(cx, obj2, id, false, desc)
@@ -4028,21 +4033,21 @@ JS_NextProperty(JSContext *cx, JSObject 
         /* Native case: private data is a property tree node pointer. */
         JS_ASSERT(iterobj->getParent()->isNative());
         shape = (Shape *) iterobj->getPrivate();
 
         while (shape->previous() && !shape->enumerable())
             shape = shape->previous();
 
         if (!shape->previous()) {
-            JS_ASSERT(JSID_IS_EMPTY(shape->propid));
+            JS_ASSERT(shape->isEmptyShape());
             *idp = JSID_VOID;
         } else {
             iterobj->setPrivate(const_cast<Shape *>(shape->previous()));
-            *idp = shape->propid;
+            *idp = shape->propid();
         }
     } else {
         /* Non-native case: use the ida enumerated when iterobj was created. */
         ida = (JSIdArray *) iterobj->getPrivate();
         JS_ASSERT(i <= ida->length);
         STATIC_ASSUME(i <= ida->length);
         if (i == 0) {
             *idp = JSID_VOID;
@@ -4266,17 +4271,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
                 return NULL;
             }
             obj = obj->getParent();
         }
 
         Value v;
-        if (!obj->getGeneric(cx, r.front().propid, &v))
+        if (!obj->getGeneric(cx, r.front().propid(), &v))
             return NULL;
         clone->getFlatClosureUpvars()[i] = v;
     }
 
     return clone;
 }
 
 JS_PUBLIC_API(JSObject *)
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -346,17 +346,17 @@ JSObject::arrayGetOwnDataElement(JSConte
     jsid id;
     if (!IndexToId(cx, this, i, &hole, &id))
         return false;
 
     const Shape *shape = nativeLookup(cx, id);
     if (!shape || !shape->isDataDescriptor())
         vp->setMagic(JS_ARRAY_HOLE);
     else
-        *vp = getSlot(shape->slot);
+        *vp = getSlot(shape->slot());
     return true;
 }
 
 /*
  * If the property at the given index exists, get its value into location
  * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
  * to JSVAL_VOID. This function assumes that the location pointed by vp is
  * properly rooted and can be used as GC-protected storage for temporaries.
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -209,29 +209,29 @@ AddPropertyHelper(JSContext* cx, JSObjec
     JS_ASSERT(shape->previous() == obj->lastProperty());
 
     if (obj->nativeEmpty()) {
         if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
             return false;
     }
 
     uint32 slot;
-    slot = shape->slot;
+    slot = shape->slot();
     JS_ASSERT(slot == obj->slotSpan());
 
     if (slot < obj->numSlots()) {
         JS_ASSERT(obj->getSlot(slot).isUndefined());
     } else {
         if (!obj->allocSlot(cx, &slot))
             return false;
-        JS_ASSERT(slot == shape->slot);
+        JS_ASSERT(slot == shape->slot());
     }
 
     obj->extend(cx, shape, isDefinitelyAtom);
-    return !js_IsPropertyCacheDisabled(cx);
+    return true;
 }
 
 JSBool FASTCALL
 js_AddProperty(JSContext* cx, JSObject* obj, Shape* shape)
 {
     return AddPropertyHelper(cx, obj, shape, /* isDefinitelyAtom = */false);
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SHAPE, 0, ACCSET_STORE_ANY)
--- a/js/src/jscell.h
+++ b/js/src/jscell.h
@@ -64,16 +64,17 @@ enum AllocKind {
     FINALIZE_OBJECT12_BACKGROUND,
     FINALIZE_OBJECT16,
     FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_FUNCTION,
     FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION,
     FINALIZE_SCRIPT,
     FINALIZE_SHAPE,
+    FINALIZE_BASE_SHAPE,
     FINALIZE_TYPE_OBJECT,
 #if JS_HAS_XML_SUPPORT
     FINALIZE_XML,
 #endif
     FINALIZE_SHORT_STRING,
     FINALIZE_STRING,
     FINALIZE_EXTERNAL_STRING,
     FINALIZE_LAST = FINALIZE_EXTERNAL_STRING
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1599,23 +1599,26 @@ IsJITBrokenHere()
     return isBroken;
 }
 #endif
 
 void
 JSContext::updateJITEnabled()
 {
 #ifdef JS_TRACER
+    traceJitEnabled = false;
+    /*
     traceJitEnabled = ((runOptions & JSOPTION_JIT) &&
                        !IsJITBrokenHere() &&
                        compartment &&
                        !compartment->debugMode() &&
                        (debugHooks == &js_NullDebugHooks ||
                         (debugHooks == &runtime->globalDebugHooks &&
                          !runtime->debuggerInhibitsJIT())));
+    */
 #endif
 #ifdef JS_METHODJIT
     methodJitEnabled = (runOptions & JSOPTION_METHODJIT) &&
                        !IsJITBrokenHere()
 # if defined JS_CPU_X86 || defined JS_CPU_X64
                        && JSC::MacroAssemblerX86Common::getSSEState() >=
                           JSC::MacroAssemblerX86Common::HasSSE2
 # endif
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -367,30 +367,16 @@ struct JSRuntime {
     void setActivityCallback(JSActivityCallback cb, void *arg) {
         activityCallback = cb;
         activityCallbackArg = arg;
     }
 
     JSActivityCallback    activityCallback;
     void                 *activityCallbackArg;
 
-    /*
-     * Shape regenerated whenever a prototype implicated by an "add property"
-     * property cache fill and induced trace guard has a readonly property or a
-     * setter defined on it. This number proxies for the shapes of all objects
-     * along the prototype chain of all objects in the runtime on which such an
-     * add-property result has been cached/traced.
-     *
-     * See bug 492355 for more details.
-     *
-     * This comes early in JSRuntime to minimize the immediate format used by
-     * trace-JITted code that reads it.
-     */
-    uint32              protoHazardShape;
-
     /* Garbage collector state, used by jsgc.c. */
 
     /*
      * Set of all GC chunks with at least one allocated thing. The
      * conservative GC uses it to quickly check if a possible GC thing points
      * into an allocated chunk.
      */
     js::GCChunkSet      gcChunkSet;
@@ -457,17 +443,16 @@ struct JSRuntime {
      * We can pack these flags as only the GC thread writes to them. Atomic
      * updates to packed bytes are not guaranteed, so stores issued by one
      * thread may be lost due to unsynchronized read-modify-write cycles on
      * other threads.
      */
     bool                gcPoke;
     bool                gcMarkAndSweep;
     bool                gcRunning;
-    bool                gcRegenShapes;
 
     /*
      * These options control the zealousness of the GC. The fundamental values
      * are gcNextScheduled and gcDebugCompartmentGC. At every allocation,
      * gcNextScheduled is decremented. When it reaches zero, we do either a
      * full or a compartmental GC, based on gcDebugCompartmentGC.
      *
      * At this point, if gcZeal_ >= 2 then gcNextScheduled is reset to the
@@ -633,31 +618,16 @@ struct JSRuntime {
 #endif
 
   private:
     JSPrincipals        *trustedPrincipals_;
   public:
     void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; }
     JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
 
-    /*
-     * Object shape (property cache structural type) identifier generator.
-     *
-     * Type 0 stands for the empty scope, and must not be regenerated due to
-     * uint32 wrap-around. Since js_GenerateShape (in jsinterp.cpp) uses
-     * atomic pre-increment, the initial value for the first typed non-empty
-     * scope will be 1.
-     *
-     * If this counter overflows into SHAPE_OVERFLOW_BIT (in jsinterp.h), the
-     * cache is disabled, to avoid aliasing two different types. It stays
-     * disabled until a triggered GC at some later moment compresses live
-     * types, minimizing rt->shapeGen in the process.
-     */
-    volatile uint32     shapeGen;
-
     /* Literal table maintained by jsatom.c functions. */
     JSAtomState         atomState;
 
     /* Tables of strings that are pre-allocated in the atomsCompartment. */
     js::StaticStrings   staticStrings;
 
     JSWrapObjectCallback wrapObjectCallback;
     JSPreWrapCallback    preWrapObjectCallback;
@@ -2348,39 +2318,16 @@ js_GetTopStackFrame(JSContext *cx, Frame
 #ifdef JS_METHODJIT
     if (expand)
         js::mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
     return cx->maybefp();
 }
 
-static JS_INLINE JSBool
-js_IsPropertyCacheDisabled(JSContext *cx)
-{
-    return cx->runtime->shapeGen >= js::SHAPE_OVERFLOW_BIT;
-}
-
-static JS_INLINE uint32
-js_RegenerateShapeForGC(JSRuntime *rt)
-{
-    JS_ASSERT(rt->gcRunning);
-    JS_ASSERT(rt->gcRegenShapes);
-
-    /*
-     * Under the GC, compared with js_GenerateShape, we don't need to use
-     * atomic increments but we still must make sure that after an overflow
-     * the shape stays such.
-     */
-    uint32 shape = rt->shapeGen;
-    shape = (shape + 1) | (shape & js::SHAPE_OVERFLOW_BIT);
-    rt->shapeGen = shape;
-    return shape;
-}
-
 namespace js {
 
 template<class T>
 class AutoVectorRooter : protected AutoGCRooter
 {
   public:
     explicit AutoVectorRooter(JSContext *cx, ptrdiff_t tag
                               JS_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -529,16 +529,19 @@ JSCompartment::sweep(JSContext *cx, uint
     if (emptyWithShape && IsAboutToBeFinalized(cx, emptyWithShape))
         emptyWithShape = NULL;
 
     if (initialRegExpShape && IsAboutToBeFinalized(cx, initialRegExpShape))
         initialRegExpShape = NULL;
     if (initialStringShape && IsAboutToBeFinalized(cx, initialStringShape))
         initialStringShape = NULL;
 
+    /* Remove dead base shapes */
+    sweepBaseShapeTable(cx);
+
     sweepBreakpoints(cx);
 
 #ifdef JS_TRACER
     if (hasTraceMonitor())
         traceMonitor()->sweep(cx);
 #endif
 
 #ifdef JS_METHODJIT
@@ -555,16 +558,19 @@ JSCompartment::sweep(JSContext *cx, uint
             canPurgeNativeCalls = false;
     }
     for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->hasJITCode()) {
 #ifdef JS_POLYIC
             mjit::ic::PurgePICs(cx, script);
 #endif
+#ifdef JS_MONOIC
+            mjit::ic::PurgeMICs(cx, script);
+#endif
             if (canPurgeNativeCalls) {
                 if (script->jitNormal)
                     script->jitNormal->purgeNativeCallStubs();
                 if (script->jitCtor)
                     script->jitCtor->purgeNativeCallStubs();
             }
         }
     }
@@ -682,40 +688,16 @@ JSCompartment::purge(JSContext *cx)
             JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
             *listHeadp = NULL;
             listHeadp = &script->u.evalHashLink;
         }
     }
 
     nativeIterCache.purge();
     toSourceCache.destroyIfConstructed();
-
-#ifdef JS_TRACER
-    /*
-     * If we are about to regenerate shapes, we have to flush the JIT cache,
-     * which will eventually abort any current recording.
-     */
-    if (cx->runtime->gcRegenShapes)
-        if (hasTraceMonitor())
-            traceMonitor()->needFlush = JS_TRUE;
-#endif
-
-#if defined JS_METHODJIT && defined JS_MONOIC
-    /*
-     * MICs do not refer to data which can be GC'ed and do not generate stubs
-     * which might need to be discarded, but are sensitive to shape regeneration.
-     */
-    if (cx->runtime->gcRegenShapes) {
-        for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
-            JSScript *script = i.get<JSScript>();
-            if (script->hasJITCode())
-                mjit::ic::PurgeMICs(cx, script);
-        }
-    }
-#endif
 }
 
 MathCache *
 JSCompartment::allocMathCache(JSContext *cx)
 {
     JS_ASSERT(!mathCache);
     mathCache = cx->new_<MathCache>();
     if (!mathCache)
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -478,21 +478,36 @@ struct JS_FRIEND_API(JSCompartment) {
      */
     js::EmptyShape               *emptyArgumentsShape;
     js::EmptyShape               *emptyBlockShape;
     js::EmptyShape               *emptyCallShape;
     js::EmptyShape               *emptyDeclEnvShape;
     js::EmptyShape               *emptyEnumeratorShape;
     js::EmptyShape               *emptyWithShape;
 
-    typedef js::HashSet<js::EmptyShape *,
-                        js::DefaultHasher<js::EmptyShape *>,
-                        js::SystemAllocPolicy> EmptyShapeSet;
+    /*
+     * Set of all unowned base shapes in the compartment, with optional empty
+     * scopes. For sharing between shapes with common state.
+     */
+
+    struct BaseShapeEntry {
+        js::BaseShape *base;
+        js::EmptyShape *empty;
 
-    EmptyShapeSet                emptyShapes;
+        typedef const js::BaseShape *Lookup;
+
+        static inline js::HashNumber hash(const js::BaseShape *base);
+        static inline bool match(const BaseShapeEntry &key, const js::BaseShape *lookup);
+    };
+
+    typedef js::HashSet<BaseShapeEntry, BaseShapeEntry, js::SystemAllocPolicy> BaseShapeSet;
+
+    BaseShapeSet                 baseShapes;
+
+    void sweepBaseShapeTable(JSContext *cx);
 
     /*
      * Initial shapes given to RegExp and String objects, encoding the initial
      * sets of built-in instance properties and the fixed slots where they must
      * be stored (see JSObject::JSSLOT_(REGEXP|STRING)_*). Later property
      * additions may cause these shapes to not be used by a RegExp or String
      * (even along the entire shape parent chain, should the object go into
      * dictionary mode). But because all the initial properties are
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -848,39 +848,39 @@ JS_PropertyIterator(JSObject *obj, JSSco
 
     /* The caller passes null in *iteratorp to get things started. */
     shape = (Shape *) *iteratorp;
     if (!shape) {
         shape = obj->lastProperty();
     } else {
         shape = shape->previous();
         if (!shape->previous()) {
-            JS_ASSERT(JSID_IS_EMPTY(shape->propid));
+            JS_ASSERT(JSID_IS_EMPTY(shape->propid()));
             shape = NULL;
         }
     }
 
     return *iteratorp = reinterpret_cast<JSScopeProperty *>(const_cast<Shape *>(shape));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
                    JSPropertyDesc *pd)
 {
     assertSameCompartment(cx, obj);
     Shape *shape = (Shape *) sprop;
-    pd->id = IdToJsval(shape->propid);
+    pd->id = IdToJsval(shape->propid());
 
     JSBool wasThrowing = cx->isExceptionPending();
     Value lastException = UndefinedValue();
     if (wasThrowing)
         lastException = cx->getPendingException();
     cx->clearPendingException();
 
-    if (!js_GetProperty(cx, obj, shape->propid, &pd->value)) {
+    if (!js_GetProperty(cx, obj, shape->propid(), &pd->value)) {
         if (!cx->isExceptionPending()) {
             pd->flags = JSPD_ERROR;
             pd->value = JSVAL_VOID;
         } else {
             pd->flags = JSPD_EXCEPTION;
             pd->value = cx->getPendingException();
         }
     } else {
@@ -890,31 +890,31 @@ JS_GetPropertyDesc(JSContext *cx, JSObje
     if (wasThrowing)
         cx->setPendingException(lastException);
 
     pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
               |  (!shape->writable()  ? JSPD_READONLY  : 0)
               |  (!shape->configurable() ? JSPD_PERMANENT : 0);
     pd->spare = 0;
     if (shape->getter() == GetCallArg) {
-        pd->slot = shape->shortid;
+        pd->slot = shape->shortid();
         pd->flags |= JSPD_ARGUMENT;
     } else if (shape->getter() == GetCallVar) {
-        pd->slot = shape->shortid;
+        pd->slot = shape->shortid();
         pd->flags |= JSPD_VARIABLE;
     } else {
         pd->slot = 0;
     }
     pd->alias = JSVAL_VOID;
 
-    if (obj->containsSlot(shape->slot)) {
+    if (obj->containsSlot(shape->slot())) {
         for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
             const Shape &aprop = r.front();
-            if (&aprop != shape && aprop.slot == shape->slot) {
-                pd->alias = IdToJsval(aprop.propid);
+            if (&aprop != shape && aprop.slot() == shape->slot()) {
+                pd->alias = IdToJsval(aprop.propid());
                 break;
             }
         }
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1719,17 +1719,17 @@ js_LexicalLookup(JSTreeContext *tc, JSAt
         JS_ASSERT(obj->isStaticBlock());
 
         const Shape *shape = obj->nativeLookup(tc->parser->context, ATOM_TO_JSID(atom));
         if (shape) {
             JS_ASSERT(shape->hasShortID());
 
             if (slotp) {
                 JS_ASSERT(obj->getSlot(JSSLOT_BLOCK_DEPTH).isInt32());
-                *slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid;
+                *slotp = obj->getSlot(JSSLOT_BLOCK_DEPTH).toInt32() + shape->shortid();
             }
             return stmt;
         }
     }
 
     if (slotp)
         *slotp = -1;
     return stmt;
@@ -1783,18 +1783,18 @@ LookupCompileTimeConstant(JSContext *cx,
                 if (shape) {
                     /*
                      * We're compiling code that will be executed immediately,
                      * not re-executed against a different scope chain and/or
                      * variable object.  Therefore we can get constant values
                      * from our variable object here.
                      */
                     if (!shape->writable() && !shape->configurable() &&
-                        shape->hasDefaultGetter() && obj->containsSlot(shape->slot)) {
-                        *constp = obj->getSlot(shape->slot);
+                        shape->hasDefaultGetter() && obj->containsSlot(shape->slot())) {
+                        *constp = obj->getSlot(shape->slot());
                     }
                 }
 
                 if (shape)
                     break;
             }
         }
     } while (cg->parent && (cg = cg->parent->asCodeGenerator()));
@@ -2024,18 +2024,20 @@ EmitEnterBlock(JSContext *cx, JSParseNod
     }
 
     /*
      * If clones of this block will have any extensible parents, then the clones
      * must get unique shapes; see the comments for js::Bindings::
      * extensibleParents.
      */
     if ((cg->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
-        cg->bindings.extensibleParents())
-        blockObj->setBlockOwnShape(cx);
+        cg->bindings.extensibleParents()) {
+        if (!Shape::setExtensibleParents(cx, &blockObj->lastProp))
+            return false;
+    }
 
     return true;
 }
 
 static JSBool
 EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op,
                JSObjectBox *box)
 {
@@ -3946,23 +3948,16 @@ js_EmitFunctionScript(JSContext *cx, JSC
      */
     if (cg->needsEagerArguments()) {
         CG_SWITCH_TO_PROLOG(cg);
         if (js_Emit1(cx, cg, JSOP_ARGUMENTS) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
             return false;
         CG_SWITCH_TO_MAIN(cg);
     }
 
-    if (cg->flags & TCF_FUN_UNBRAND_THIS) {
-        CG_SWITCH_TO_PROLOG(cg);
-        if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0)
-            return false;
-        CG_SWITCH_TO_MAIN(cg);
-    }
-
     return js_EmitTree(cx, cg, body) &&
            js_Emit1(cx, cg, JSOP_STOP) >= 0 &&
            JSScript::NewScriptFromCG(cx, cg);
 }
 
 static bool
 MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
                  JSParseNode *pn, jsatomid *result)
@@ -7120,21 +7115,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                     if (obj->inDictionaryMode())
                         obj = NULL;
                 }
 
                 EMIT_INDEX_OP(op, index);
             }
         }
 
-        if (cg->funbox && cg->funbox->shouldUnbrand(methodInits, slowMethodInits)) {
-            obj = NULL;
-            if (js_Emit1(cx, cg, JSOP_UNBRAND) < 0)
-                return JS_FALSE;
-        }
         if (!EmitEndInit(cx, cg, pn->pn_count))
             return JS_FALSE;
 
         if (obj) {
             /*
              * The object survived and has a predictable shape.  Update the original bytecode,
              * as long as we can do so without using a big index prefix/suffix.
              */
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -207,25 +207,16 @@ struct JSStmtInfo {
  * certain strict warnings as errors, and forbid the use of 'with'. See also
  * TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and JSREPORT_STRICT_ERROR.
  */
 #define TCF_STRICT_MODE_CODE    0x40000
 
 /* bit 0x80000 is unused */
 
 /*
- * Flag signifying that the current function seems to be a constructor that
- * sets this.foo to define "methods", at least one of which can't be a null
- * closure, so we should avoid over-specializing property cache entries and
- * trace inlining guards to method function object identity, which will vary
- * per instance.
- */
-#define TCF_FUN_UNBRAND_THIS   0x100000
-
-/*
  * "Module pattern", i.e., a lambda that is immediately applied and the whole
  * of an expression statement.
  */
 #define TCF_FUN_MODULE_PATTERN 0x200000
 
 /*
  * Flag to prevent a non-escaping function from being optimized into a null
  * closure (i.e., a closure that needs only its global object for free variable
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1199,60 +1199,58 @@ StackFrame::getValidCalleeObject(JSConte
                 /*
                  * While a non-native object is responsible for handling its
                  * entire prototype chain, notable non-natives including dense
                  * and typed arrays have native prototypes, so keep going.
                  */
                 if (!thisp->isNative())
                     continue;
 
-                if (thisp->hasMethodBarrier()) {
-                    const Shape *shape = thisp->nativeLookup(cx, ATOM_TO_JSID(fun->methodAtom()));
-                    if (shape) {
-                        /*
-                         * Two cases follow: the method barrier was not crossed
-                         * yet, so we cross it here; the method barrier *was*
-                         * crossed but after the call, in which case we fetch
-                         * and validate the cloned (unjoined) funobj from the
-                         * method property's slot.
-                         *
-                         * In either case we must allow for the method property
-                         * to have been replaced, or its value overwritten.
-                         */
-                        if (shape->isMethod() && shape->methodObject() == funobj) {
-                            if (!thisp->methodReadBarrier(cx, *shape, vp))
-                                return false;
-                            overwriteCallee(vp->toObject());
+                const Shape *shape = thisp->nativeLookup(cx, ATOM_TO_JSID(fun->methodAtom()));
+                if (shape) {
+                    /*
+                     * Two cases follow: the method barrier was not crossed
+                     * yet, so we cross it here; the method barrier *was*
+                     * crossed but after the call, in which case we fetch
+                     * and validate the cloned (unjoined) funobj from the
+                     * method property's slot.
+                     *
+                     * In either case we must allow for the method property
+                     * to have been replaced, or its value overwritten.
+                     */
+                    if (shape->isMethod() && thisp->nativeGetMethod(shape) == &funobj) {
+                        if (!thisp->methodReadBarrier(cx, *shape, vp))
+                            return false;
+                        overwriteCallee(vp->toObject());
+                        return true;
+                    }
+
+                    if (shape->hasSlot()) {
+                        Value v = thisp->getSlot(shape->slot());
+                        JSObject *clone;
+
+                        if (IsFunctionObject(v, &clone) &&
+                            clone->getFunctionPrivate() == fun &&
+                            clone->hasMethodObj(*thisp)) {
+                            /*
+                             * N.B. If the method barrier was on a function
+                             * with singleton type, then while crossing the
+                             * method barrier CloneFunctionObject will have
+                             * ignored the attempt to clone the function.
+                             */
+                            JS_ASSERT_IF(!clone->hasSingletonType(), clone != &funobj);
+                            *vp = v;
+                            overwriteCallee(*clone);
                             return true;
                         }
-
-                        if (shape->hasSlot()) {
-                            Value v = thisp->getSlot(shape->slot);
-                            JSObject *clone;
-
-                            if (IsFunctionObject(v, &clone) &&
-                                clone->getFunctionPrivate() == fun &&
-                                clone->hasMethodObj(*thisp)) {
-                                /*
-                                 * N.B. If the method barrier was on a function
-                                 * with singleton type, then while crossing the
-                                 * method barrier CloneFunctionObject will have
-                                 * ignored the attempt to clone the function.
-                                 */
-                                JS_ASSERT_IF(!clone->hasSingletonType(), clone != &funobj);
-                                *vp = v;
-                                overwriteCallee(*clone);
-                                return true;
-                            }
-                        }
                     }
-
-                    if (!first_barriered_thisp)
-                        first_barriered_thisp = thisp;
                 }
+
+                if (!first_barriered_thisp)
+                    first_barriered_thisp = thisp;
             } while ((thisp = thisp->getProto()) != NULL);
 
             if (!first_barriered_thisp)
                 return true;
 
             /*
              * At this point, we couldn't find an already-existing clone (or
              * force to exist a fresh clone) created via thisp's method read
@@ -1900,23 +1898,24 @@ JSObject::initBoundFunction(JSContext *c
                             const Value *args, uintN argslen)
 {
     JS_ASSERT(isFunction());
 
     flags |= JSObject::BOUND_FUNCTION;
     setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
     setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
     if (argslen != 0) {
-        /* FIXME? Burn memory on an empty scope whose shape covers the args slots. */
-        EmptyShape *empty = EmptyShape::create(cx, getClass());
-        if (!empty)
+        /*
+         * FIXME? We need to conver to a dictionary in order to increase the
+         * slot span and cover the arguments.
+         */
+        if (!toDictionaryMode(cx))
             return false;
-
-        empty->slotSpan += argslen;
-        setMap(empty);
+        JS_ASSERT(slotSpan() == JSSLOT_FREE(&FunctionClass));
+        lastProp->base()->slotSpan += argslen;
 
         if (!ensureInstanceReservedSlots(cx, argslen))
             return false;
 
         JS_ASSERT(numSlots() >= argslen + FUN_CLASS_RESERVED_SLOTS);
         copySlotRange(FUN_CLASS_RESERVED_SLOTS, args, argslen);
     }
     return true;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -132,16 +132,17 @@ const uint32 Arena::ThingSizes[] = {
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     sizeof(JSFunction),         /* FINALIZE_FUNCTION            */
     sizeof(JSScript),           /* FINALIZE_SCRIPT              */
     sizeof(Shape),              /* FINALIZE_SHAPE               */
+    sizeof(BaseShape),          /* FINALIZE_BASE_SHAPE          */
     sizeof(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
 #if JS_HAS_XML_SUPPORT
     sizeof(JSXML),              /* FINALIZE_XML                 */
 #endif
     sizeof(JSShortString),      /* FINALIZE_SHORT_STRING        */
     sizeof(JSString),           /* FINALIZE_STRING              */
     sizeof(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
 };
@@ -159,16 +160,17 @@ const uint32 Arena::FirstThingOffsets[] 
     OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     OFFSET(JSFunction),         /* FINALIZE_FUNCTION            */
     OFFSET(JSScript),           /* FINALIZE_SCRIPT              */
     OFFSET(Shape),              /* FINALIZE_SHAPE               */
+    OFFSET(BaseShape),          /* FINALIZE_BASE_SHAPE          */
     OFFSET(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
 #if JS_HAS_XML_SUPPORT
     OFFSET(JSXML),              /* FINALIZE_XML                 */
 #endif
     OFFSET(JSShortString),      /* FINALIZE_SHORT_STRING        */
     OFFSET(JSString),           /* FINALIZE_STRING              */
     OFFSET(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
 };
@@ -389,16 +391,19 @@ FinalizeArenas(JSContext *cx, ArenaLists
 	FinalizeTypedArenas<JSObject>(cx, al, thingKind);
         break;
       case FINALIZE_SCRIPT:
 	FinalizeTypedArenas<JSScript>(cx, al, thingKind);
         break;
       case FINALIZE_SHAPE:
 	FinalizeTypedArenas<Shape>(cx, al, thingKind);
         break;
+      case FINALIZE_BASE_SHAPE:
+        FinalizeTypedArenas<BaseShape>(cx, al, thingKind);
+        break;
       case FINALIZE_TYPE_OBJECT:
 	FinalizeTypedArenas<types::TypeObject>(cx, al, thingKind);
         break;
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
 	FinalizeTypedArenas<JSXML>(cx, al, thingKind);
         break;
 #endif
@@ -1417,16 +1422,17 @@ ArenaLists::finalizeStrings(JSContext *c
 
     finalizeNow(cx, FINALIZE_EXTERNAL_STRING);
 }
 
 void
 ArenaLists::finalizeShapes(JSContext *cx)
 {
     finalizeNow(cx, FINALIZE_SHAPE);
+    finalizeNow(cx, FINALIZE_BASE_SHAPE);
     finalizeNow(cx, FINALIZE_TYPE_OBJECT);
 }
 
 void
 ArenaLists::finalizeScripts(JSContext *cx)
 {
     finalizeNow(cx, FINALIZE_SCRIPT);
 }
@@ -2193,28 +2199,16 @@ SweepCompartments(JSContext *cx, JSGCInv
     rt->compartments.resize(write - rt->compartments.begin());
 }
 
 static void
 BeginMarkPhase(JSContext *cx, GCMarker *gcmarker, JSGCInvocationKind gckind GCTIMER_PARAM)
 {
     JSRuntime *rt = cx->runtime;
 
-    /*
-     * Reset the property cache's type id generator so we can compress ids.
-     * Same for the protoHazardShape proxy-shape standing in for all object
-     * prototypes having readonly or setter properties.
-     */
-    if (rt->shapeGen & SHAPE_OVERFLOW_BIT || (rt->gcZeal() && !rt->gcCurrentCompartment)) {
-        JS_ASSERT(!rt->gcCurrentCompartment);
-        rt->gcRegenShapes = true;
-        rt->shapeGen = 0;
-        rt->protoHazardShape = 0;
-    }
-
     for (GCCompartmentsIter c(rt); !c.done(); c.next())
         c->purge(cx);
 
     js_PurgeThreads(cx);
 
     {
         JSContext *iter = NULL;
         while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
@@ -2651,17 +2645,16 @@ GCCycle(JSContext *cx, JSCompartment *co
         cx->gcBackgroundFree = NULL;
         rt->gcHelperThread.startBackgroundSweep(rt, gckind);
     } else {
         JS_ASSERT(!cx->gcBackgroundFree);
     }
 #endif
 
     rt->gcMarkAndSweep = false;
-    rt->gcRegenShapes = false;
     rt->setGCLastBytes(rt->gcBytes, gckind);
     rt->gcCurrentCompartment = NULL;
     rt->gcWeakMapList = NULL;
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
         (*c)->setGCLastBytes((*c)->gcBytes, gckind);
 }
 
@@ -2688,17 +2681,17 @@ js_GC(JSContext *cx, JSCompartment *comp
     if (JS_ON_TRACE(cx)) {
         JS_ASSERT(gckind != GC_LAST_CONTEXT);
         return;
     }
 
     RecordNativeStackTopForGC(cx);
 
     GCCrashData crashData;
-    crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
+    crashData.isRegen = false;
     crashData.isCompartment = !!comp;
     crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
 
     GCTIMER_BEGIN(rt, comp);
 
     struct AutoGCProbe {
         JSCompartment *comp;
         AutoGCProbe(JSCompartment *comp) : comp(comp) {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -838,16 +838,17 @@ MapAllocToTraceKind(AllocKind thingKind)
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_FUNCTION */
         JSTRACE_SCRIPT,     /* FINALIZE_SCRIPT */
         JSTRACE_SHAPE,      /* FINALIZE_SHAPE */
+        JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
         JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
 #if JS_HAS_XML_SUPPORT      /* FINALIZE_XML */
         JSTRACE_XML,
 #endif
         JSTRACE_STRING,     /* FINALIZE_SHORT_STRING */
         JSTRACE_STRING,     /* FINALIZE_STRING */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
     };
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -402,14 +402,20 @@ js_NewGCScript(JSContext *cx)
 }
 
 inline js::Shape *
 js_NewGCShape(JSContext *cx)
 {
     return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
 }
 
+inline js::BaseShape *
+js_NewGCBaseShape(JSContext *cx)
+{
+    return NewGCThing<js::BaseShape>(cx, js::gc::FINALIZE_BASE_SHAPE, sizeof(js::BaseShape));
+}
+
 #if JS_HAS_XML_SUPPORT
 extern JSXML *
 js_NewGCXML(JSContext *cx);
 #endif
 
 #endif /* jsgcinlines_h___ */
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -208,16 +208,25 @@ MarkShape(JSTracer *trc, const Shape *sh
 {
     JS_ASSERT(trc);
     JS_ASSERT(shape);
     JS_SET_TRACING_NAME(trc, name);
     Mark(trc, shape);
 }
 
 void
+MarkBaseShape(JSTracer *trc, BaseShape *base, const char *name)
+{
+    JS_ASSERT(trc);
+    JS_ASSERT(base);
+    JS_SET_TRACING_NAME(trc, name);
+    Mark(trc, base);
+}
+
+void
 MarkTypeObject(JSTracer *trc, types::TypeObject *type, const char *name)
 {
     JS_ASSERT(trc);
     JS_ASSERT(type);
     JS_SET_TRACING_NAME(trc, name);
     if (type == &types::emptyTypeObject)
         return;
     Mark(trc, type);
@@ -321,16 +330,30 @@ PushMarkStack(GCMarker *gcmarker, const 
                      thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
 
     /* We mark shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanShape(gcmarker, thing);
 }
 
 static void
+ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
+
+void
+PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
+{
+    JS_OPT_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                     thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    /* We mark base shapes directly rather than pushing on the stack. */
+    if (thing->markIfUnmarked(gcmarker->getMarkColor()))
+        ScanBaseShape(gcmarker, thing);
+}
+
+static void
 MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
 {
     for (uint32 i = 0; i < len; i++) {
         if (JSAtom *atom = vec[i]) {
             JS_SET_TRACING_INDEX(trc, name, i);
             Mark(trc, atom);
         }
     }
@@ -402,16 +425,19 @@ MarkKind(JSTracer *trc, void *thing, JSG
         MarkString(trc, reinterpret_cast<JSString *>(thing));
         break;
       case JSTRACE_SCRIPT:
         Mark(trc, static_cast<JSScript *>(thing));
         break;
       case JSTRACE_SHAPE:
         Mark(trc, reinterpret_cast<Shape *>(thing));
         break;
+      case JSTRACE_BASE_SHAPE:
+        Mark(trc, reinterpret_cast<BaseShape *>(thing));
+        break;
       case JSTRACE_TYPE_OBJECT:
         MarkTypeObject(trc, reinterpret_cast<types::TypeObject *>(thing), "type_stack");
         break;
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
         Mark(trc, static_cast<JSXML *>(thing));
         break;
 #endif
@@ -554,37 +580,37 @@ MarkRoot(JSTracer *trc, JSXML *thing, co
     MarkXML(trc, thing, name);
 }
 
 static void
 PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
 {
     JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter);
     Shape *shape = (Shape *)trc->debugPrintArg;
-    jsid propid = shape->propid;
+    jsid propid = shape->propid();
     JS_ASSERT(!JSID_IS_VOID(propid));
     const char *name = trc->debugPrintIndex ? js_setter_str : js_getter_str;
 
     if (JSID_IS_ATOM(propid)) {
         size_t n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
         if (n < bufsize)
             JS_snprintf(buf + n, bufsize - n, " %s", name);
-    } else if (JSID_IS_INT(shape->propid)) {
+    } else if (JSID_IS_INT(propid)) {
         JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(propid), name);
     } else {
         JS_snprintf(buf, bufsize, "<object> %s", name);
     }
 }
 
 static void
 PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
 {
     JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
     Shape *shape = (Shape *)trc->debugPrintArg;
-    jsid propid = shape->propid;
+    jsid propid = shape->propid();
     JS_ASSERT(!JSID_IS_VOID(propid));
 
     JS_ASSERT(JSID_IS_ATOM(propid));
     size_t n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
     if (n < bufsize)
         JS_snprintf(buf + n, bufsize - n, " method");
 }
 
@@ -601,38 +627,42 @@ ScanValue(GCMarker *gcmarker, const Valu
         }
     }
 }
 
 static void
 ScanShape(GCMarker *gcmarker, const Shape *shape)
 {
 restart:
-    JSRuntime *rt = gcmarker->context->runtime;
-    if (rt->gcRegenShapes)
-        shape->shapeid = js_RegenerateShapeForGC(rt);
+    PushMarkStack(gcmarker, shape->base());
 
-    if (JSID_IS_STRING(shape->propid))
-        PushMarkStack(gcmarker, JSID_TO_STRING(shape->propid));
-    else if (JS_UNLIKELY(JSID_IS_OBJECT(shape->propid)))
-        PushMarkStack(gcmarker, JSID_TO_OBJECT(shape->propid));
-
-    if (shape->hasGetterValue() && shape->getter())
-        PushMarkStack(gcmarker, shape->getterObject());
-    if (shape->hasSetterValue() && shape->setter())
-        PushMarkStack(gcmarker, shape->setterObject());
-
-    if (shape->isMethod())
-        PushMarkStack(gcmarker, &shape->methodObject());
+    jsid id = shape->maybePropid();
+    if (JSID_IS_STRING(id))
+        PushMarkStack(gcmarker, JSID_TO_STRING(id));
+    else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
+        PushMarkStack(gcmarker, JSID_TO_OBJECT(id));
 
     shape = shape->previous();
     if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
         goto restart;
 }
 
+static void
+ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
+{
+    if (base->flags & BaseShape::HAS_GETTER_OBJECT)
+        PushMarkStack(gcmarker, base->getterObj);
+
+    if (base->flags & BaseShape::HAS_SETTER_OBJECT)
+        PushMarkStack(gcmarker, base->setterObj);
+
+    if (base->base)
+        PushMarkStack(gcmarker, base->base);
+}
+
 static inline void
 ScanRope(GCMarker *gcmarker, JSRope *rope)
 {
     JS_OPT_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
                      rope->compartment() == gcmarker->context->runtime->gcCurrentCompartment
                      || rope->compartment() == gcmarker->context->runtime->atomsCompartment);
     JS_ASSERT(rope->isMarked());
 
@@ -705,30 +735,20 @@ ScanObject(GCMarker *gcmarker, JSObject 
                 PushMarkStack(gcmarker, obj->newType);
             clasp->trace(gcmarker, obj);
         }
     } else {
         if (obj->newType)
             PushMarkStack(gcmarker, obj->newType);
     }
 
-    if (obj->isNative()) {
-        js::Shape *shape = obj->lastProp;
-        PushMarkStack(gcmarker, shape);
+    js::Shape *shape = obj->lastProp;
+    PushMarkStack(gcmarker, shape);
 
-        if (gcmarker->context->runtime->gcRegenShapes) {
-            /* We need to regenerate our shape if hasOwnShape(). */
-            uint32 newShape = shape->shapeid;
-            if (obj->hasOwnShape()) {
-                newShape = js_RegenerateShapeForGC(gcmarker->context->runtime);
-                JS_ASSERT(newShape != shape->shapeid);
-            }
-            obj->objShape = newShape;
-        }
-
+    if (shape->isNative()) {
         uint32 nslots = obj->slotSpan();
         JS_ASSERT(obj->slotSpan() <= obj->numSlots());
         if (nslots > LARGE_OBJECT_CHUNK_SIZE) {
             if (gcmarker->largeStack.push(LargeMarkItem(obj)))
                 return;
         }
 
         obj->scanSlots(gcmarker);
@@ -777,19 +797,19 @@ MarkChildren(JSTracer *trc, JSObject *ob
         MarkTypeObject(trc, obj->newType, "new_type");
     if (JSObject *parent = obj->getParent())
         MarkObject(trc, *parent, "parent");
 
     Class *clasp = obj->getClass();
     if (clasp->trace)
         clasp->trace(trc, obj);
 
-    if (obj->isNative()) {
-        MarkShape(trc, obj->lastProp, "shape");
+    MarkShape(trc, obj->lastProperty(), "shape");
 
+    if (obj->lastProperty()->isNative()) {
         JS_ASSERT(obj->slotSpan() <= obj->numSlots());
         uint32 nslots = obj->slotSpan();
         for (uint32 i = 0; i < nslots; i++) {
             JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
             MarkValueRaw(trc, obj->nativeGetSlot(i));
         }
     }
 }
@@ -852,31 +872,38 @@ MarkChildren(JSTracer *trc, JSScript *sc
         script->jitCtor->trace(trc);
 #endif
 }
 
 void
 MarkChildren(JSTracer *trc, const Shape *shape)
 {
 restart:
-    MarkId(trc, shape->propid, "propid");
+    MarkBaseShape(trc, shape->base(), "base");
 
-    if (shape->hasGetterValue() && shape->getter())
-        MarkObjectWithPrinter(trc, *shape->getterObject(), PrintPropertyGetterOrSetter, shape, 0);
-    if (shape->hasSetterValue() && shape->setter())
-        MarkObjectWithPrinter(trc, *shape->setterObject(), PrintPropertyGetterOrSetter, shape, 1);
-
-    if (shape->isMethod())
-        MarkObjectWithPrinter(trc, shape->methodObject(), PrintPropertyMethod, shape, 0);
+    MarkId(trc, shape->maybePropid(), "propid");
 
     shape = shape->previous();
     if (shape)
         goto restart;
 }
 
+void
+MarkChildren(JSTracer *trc, BaseShape *base)
+{
+    if (base->flags & BaseShape::HAS_GETTER_OBJECT)
+        MarkObjectWithPrinter(trc, *base->getterObj, PrintPropertyGetterOrSetter, base, 0);
+
+    if (base->flags & BaseShape::HAS_SETTER_OBJECT)
+        MarkObjectWithPrinter(trc, *base->setterObj, PrintPropertyGetterOrSetter, base, 0);
+
+    if (base->base)
+        MarkBaseShape(trc, base->base, "base");
+}
+
 static void
 ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
 {
     if (!type->singleton) {
         unsigned count = type->getPropertyCount();
         for (unsigned i = 0; i < count; i++) {
             types::Property *prop = type->getProperty(i);
             if (prop && JSID_IS_STRING(prop->id))
@@ -1008,16 +1035,20 @@ JS_TraceChildren(JSTracer *trc, void *th
       case JSTRACE_SCRIPT:
 	MarkChildren(trc, static_cast<JSScript *>(thing));
         break;
 
       case JSTRACE_SHAPE:
 	MarkChildren(trc, static_cast<Shape *>(thing));
         break;
 
+      case JSTRACE_BASE_SHAPE:
+        MarkChildren(trc, static_cast<BaseShape *>(thing));
+        break;
+
       case JSTRACE_TYPE_OBJECT:
         MarkChildren(trc, (types::TypeObject *)thing);
         break;
 
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
         MarkChildren(trc, static_cast<JSXML *>(thing));
         break;
--- a/js/src/jsgcstats.cpp
+++ b/js/src/jsgcstats.cpp
@@ -173,16 +173,20 @@ GCMarker::dumpConservativeRoots()
           case JSTRACE_SCRIPT: {
             fprintf(fp, "shape");
             break;
           }
           case JSTRACE_SHAPE: {
             fprintf(fp, "shape");
             break;
           }
+          case JSTRACE_BASE_SHAPE: {
+            fprintf(fp, "base_shape");
+            break;
+          }
           case JSTRACE_TYPE_OBJECT: {
             fprintf(fp, "type_object");
             break;
           }
 # if JS_HAS_XML_SUPPORT
           case JSTRACE_XML: {
             JSXML *xml = (JSXML *) thing;
             fprintf(fp, "xml %u", (unsigned)xml->xml_class);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -755,17 +755,17 @@ TypeSet::addFilterPrimitives(JSContext *
     add(cx, ArenaNew<TypeConstraintFilterPrimitive>(cx->compartment->pool, target, filter));
 }
 
 /* If id is a normal slotful 'own' property of an object, get its shape. */
 static inline const Shape *
 GetSingletonShape(JSContext *cx, JSObject *obj, jsid id)
 {
     const Shape *shape = obj->nativeLookup(cx, id);
-    if (shape && shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT)
+    if (shape && shape->hasDefaultGetterOrIsMethod() && shape->hasSlot())
         return shape;
     return NULL;
 }
 
 void
 ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32 offset)
 {
     TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
@@ -774,17 +774,17 @@ ScriptAnalysis::pruneTypeBarriers(JSCont
         if (barrier->target->hasType(barrier->type)) {
             /* Barrier is now obsolete, it can be removed. */
             *pbarrier = barrier->next;
             continue;
         }
         if (barrier->singleton) {
             JS_ASSERT(barrier->type.isPrimitive(JSVAL_TYPE_UNDEFINED));
             const Shape *shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId);
-            if (shape && !barrier->singleton->nativeGetSlot(shape->slot).isUndefined()) {
+            if (shape && !barrier->singleton->nativeGetSlot(shape->slot()).isUndefined()) {
                 /*
                  * When we analyzed the script the singleton had an 'own'
                  * property which was undefined (probably a 'var' variable
                  * added to a global object), but now it is defined. The only
                  * way it can become undefined again is if an explicit assign
                  * or deletion on the property occurs, which will update the
                  * type set for the property directly and trigger construction
                  * of a normal type barrier.
@@ -1001,17 +1001,17 @@ PropertyAccess(JSContext *cx, JSScript *
             if (object->singleton && !JSID_IS_VOID(id)) {
                 /*
                  * Add a singleton type barrier on the object if it has an
                  * 'own' property which is currently undefined. We'll be able
                  * to remove the barrier after the property becomes defined,
                  * even if no undefined value is ever observed at pc.
                  */
                 const Shape *shape = GetSingletonShape(cx, object->singleton, id);
-                if (shape && object->singleton->nativeGetSlot(shape->slot).isUndefined())
+                if (shape && object->singleton->nativeGetSlot(shape->slot()).isUndefined())
                     script->analysis()->addSingletonTypeBarrier(cx, pc, target, object->singleton, id);
             }
         } else {
             types->addSubset(cx, target);
         }
     }
 }
 
@@ -2507,30 +2507,30 @@ struct types::ObjectTableKey
     jsid *ids;
     uint32 nslots;
     uint32 nfixed;
     JSObject *proto;
 
     typedef JSObject * Lookup;
 
     static inline uint32 hash(JSObject *obj) {
-        return (uint32) (JSID_BITS(obj->lastProperty()->propid) ^
+        return (uint32) (JSID_BITS(obj->lastProperty()->propid()) ^
                          obj->slotSpan() ^ obj->numFixedSlots() ^
                          ((uint32)(size_t)obj->getProto() >> 2));
     }
 
     static inline bool match(const ObjectTableKey &v, JSObject *obj) {
         if (obj->slotSpan() != v.nslots ||
             obj->numFixedSlots() != v.nfixed ||
             obj->getProto() != v.proto) {
             return false;
         }
         const Shape *shape = obj->lastProperty();
-        while (!JSID_IS_EMPTY(shape->propid)) {
-            if (shape->propid != v.ids[shape->slot])
+        while (!shape->isEmptyShape()) {
+            if (shape->propid() != v.ids[shape->slot()])
                 return false;
             shape = shape->previous();
         }
         return true;
     }
 };
 
 struct types::ObjectTableEntry
@@ -2572,21 +2572,21 @@ TypeCompartment::fixObjectType(JSContext
         Type *types = p->value.types;
         for (unsigned i = 0; i < obj->slotSpan(); i++) {
             Type ntype = GetValueTypeForTable(cx, obj->getSlot(i));
             if (ntype != types[i]) {
                 if (NumberTypes(ntype, types[i])) {
                     if (types[i].isPrimitive(JSVAL_TYPE_INT32)) {
                         types[i] = Type::DoubleType();
                         const Shape *shape = baseShape;
-                        while (!JSID_IS_EMPTY(shape->propid)) {
-                            if (shape->slot == i) {
+                        while (!shape->isEmptyShape()) {
+                            if (shape->slot() == i) {
                                 Type type = Type::DoubleType();
                                 if (!p->value.object->unknownProperties()) {
-                                    jsid id = MakeTypeId(cx, shape->propid);
+                                    jsid id = MakeTypeId(cx, shape->propid());
                                     p->value.object->addPropertyType(cx, id, type);
                                 }
                                 break;
                             }
                             shape = shape->previous();
                         }
                     }
                 } else {
@@ -2612,22 +2612,22 @@ TypeCompartment::fixObjectType(JSContext
 
         Type *types = (Type *) cx->calloc_(obj->slotSpan() * sizeof(Type));
         if (!types) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         const Shape *shape = baseShape;
-        while (!JSID_IS_EMPTY(shape->propid)) {
-            ids[shape->slot] = shape->propid;
-            types[shape->slot] = GetValueTypeForTable(cx, obj->getSlot(shape->slot));
+        while (!shape->isEmptyShape()) {
+            ids[shape->slot()] = shape->propid();
+            types[shape->slot()] = GetValueTypeForTable(cx, obj->getSlot(shape->slot()));
             if (!objType->unknownProperties()) {
-                jsid id = MakeTypeId(cx, shape->propid);
-                objType->addPropertyType(cx, id, types[shape->slot]);
+                jsid id = MakeTypeId(cx, shape->propid());
+                objType->addPropertyType(cx, id, types[shape->slot()]);
             }
             shape = shape->previous();
         }
 
         ObjectTableKey key;
         key.ids = ids;
         key.nslots = obj->slotSpan();
         key.nfixed = obj->numFixedSlots();
@@ -2677,18 +2677,18 @@ TypeObject::getFromPrototypes(JSContext 
     proto->type()->getFromPrototypes(cx, id, protoTypes);
 }
 
 static inline void
 UpdatePropertyType(JSContext *cx, TypeSet *types, JSObject *obj, const Shape *shape, bool force)
 {
     if (shape->hasGetterValue() || shape->hasSetterValue()) {
         types->addType(cx, Type::UnknownType());
-    } else if (shape->hasDefaultGetterOrIsMethod() && shape->slot != SHAPE_INVALID_SLOT) {
-        const Value &value = obj->nativeGetSlot(shape->slot);
+    } else if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
+        const Value &value = obj->nativeGetSlot(shape->slot());
 
         /*
          * Don't add initial undefined types for singleton properties that are
          * not collated into the JSID_VOID property (see propertySet comment).
          */
         if (!force && value.isUndefined())
             return;
 
@@ -2714,22 +2714,22 @@ TypeObject::addProperty(JSContext *cx, j
          * which don't go through a barrier when read by the VM or jitcode.
          * We don't need to handle arrays or other JIT'ed non-natives as
          * these are not (yet) singletons.
          */
 
         if (JSID_IS_VOID(id)) {
             /* Go through all shapes on the object to get integer-valued properties. */
             const Shape *shape = singleton->lastProperty();
-            while (!JSID_IS_EMPTY(shape->propid)) {
-                if (JSID_IS_VOID(MakeTypeId(cx, shape->propid)))
+            while (!shape->isEmptyShape()) {
+                if (JSID_IS_VOID(MakeTypeId(cx, shape->propid())))
                     UpdatePropertyType(cx, &base->types, singleton, shape, true);
                 shape = shape->previous();
             }
-        } else {
+        } else if (!JSID_IS_EMPTY(id)) {
             const Shape *shape = singleton->nativeLookup(cx, id);
             if (shape)
                 UpdatePropertyType(cx, &base->types, singleton, shape, false);
         }
     }
 
     *pprop = base;
 
@@ -2745,24 +2745,24 @@ TypeObject::addDefiniteProperties(JSCont
 {
     if (unknownProperties())
         return true;
 
     /* Mark all properties of obj as definite properties of this type. */
     AutoEnterTypeInference enter(cx);
 
     const Shape *shape = obj->lastProperty();
-    while (!JSID_IS_EMPTY(shape->propid)) {
-        jsid id = MakeTypeId(cx, shape->propid);
-        if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot) &&
-            shape->slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
+    while (!shape->isEmptyShape()) {
+        jsid id = MakeTypeId(cx, shape->propid());
+        if (!JSID_IS_VOID(id) && obj->isFixedSlot(shape->slot()) &&
+            shape->slot() <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT)) {
             TypeSet *types = getProperty(cx, id, true);
             if (!types)
                 return false;
-            types->setDefinite(shape->slot);
+            types->setDefinite(shape->slot());
         }
         shape = shape->previous();
     }
 
     return true;
 }
 
 bool
@@ -2773,18 +2773,18 @@ TypeObject::matchDefiniteProperties(JSOb
         Property *prop = getProperty(i);
         if (!prop)
             continue;
         if (prop->types.isDefiniteProperty()) {
             unsigned slot = prop->types.definiteSlot();
 
             bool found = false;
             const Shape *shape = obj->lastProperty();
-            while (!JSID_IS_EMPTY(shape->propid)) {
-                if (shape->slot == slot && shape->propid == prop->id) {
+            while (!shape->isEmptyShape()) {
+                if (shape->slot() == slot && shape->propid() == prop->id) {
                     found = true;
                     break;
                 }
                 shape = shape->previous();
             }
             if (!found)
                 return false;
         }
@@ -3321,17 +3321,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_THROWING:
       case JSOP_GOSUB:
       case JSOP_GOSUBX:
       case JSOP_RETSUB:
       case JSOP_CONDSWITCH:
       case JSOP_DEFAULT:
       case JSOP_DEFAULTX:
       case JSOP_POPN:
-      case JSOP_UNBRANDTHIS:
       case JSOP_STARTXML:
       case JSOP_STARTXMLEXPR:
       case JSOP_DEFXMLNS:
       case JSOP_SHARPINIT:
       case JSOP_INDEXBASE:
       case JSOP_INDEXBASE1:
       case JSOP_INDEXBASE2:
       case JSOP_INDEXBASE3:
@@ -3953,20 +3952,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_CASE:
       case JSOP_CASEX:
         poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
         break;
 
-      case JSOP_UNBRAND:
-        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
-        break;
-
       case JSOP_GENERATOR:
         if (script->hasFunction) {
             if (script->hasGlobal()) {
                 JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx);
                 if (!proto)
                     return false;
                 TypeObject *object = proto->getNewType(cx);
                 if (!object)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -538,22 +538,21 @@ js::OnUnknownMethod(JSContext *cx, Value
 #if JS_HAS_XML_SUPPORT
         /* Extract the function name from function::name qname. */
         if (vp[0].isObject()) {
             obj = &vp[0].toObject();
             if (js_GetLocalNameFromFunctionQName(obj, &id, cx))
                 vp[0] = IdToValue(id);
         }
 #endif
-        obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
+
+        obj = NewNonFunction<WithProto::Given>(cx, &js_NoSuchMethodClass, NULL, NULL);
         if (!obj)
             return false;
 
-        obj->init(cx, &js_NoSuchMethodClass, &emptyTypeObject, NULL, NULL, false);
-        obj->setSharedNonNativeMap();
         obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
         obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
         vp[0].setObject(*obj);
     }
     return true;
 }
 
 static JS_REQUIRES_STACK JSBool
@@ -1439,37 +1438,34 @@ inline InterpreterFrames::~InterpreterFr
 }
 
 /*
  * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
  * single-thread DEBUG js shell testing to verify property cache hits.
  */
 #if defined DEBUG && !defined JS_THREADSAFE
 
-# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \
+# define ASSERT_VALID_PROPERTY_CACHE_HIT(obj,pobj,entry)                      \
     JS_BEGIN_MACRO                                                            \
-        if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj,  \
+        if (!AssertValidPropertyCacheHit(cx, script, regs, obj, pobj,         \
                                          entry)) {                            \
             goto error;                                                       \
         }                                                                     \
     JS_END_MACRO
 
 static bool
 AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, FrameRegs& regs,
-                            ptrdiff_t pcoff, JSObject *start, JSObject *found,
+                            JSObject *start, JSObject *found,
                             PropertyCacheEntry *entry)
 {
     uint32 sample = cx->runtime->gcNumber;
     PropertyCacheEntry savedEntry = *entry;
 
     JSAtom *atom;
-    if (pcoff >= 0)
-        GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
-    else
-        atom = cx->runtime->atomState.lengthAtom;
+    GET_ATOM_FROM_BYTECODE(script, regs.pc, 0, atom);
 
     JSObject *obj, *pobj;
     JSProperty *prop;
     JSBool ok;
 
     if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
         bool global = js_CodeSpec[*regs.pc].format & JOF_GNAME;
         ok = js_FindProperty(cx, ATOM_TO_JSID(atom), global, &obj, &pobj, &prop);
@@ -1480,44 +1476,23 @@ AssertValidPropertyCacheHit(JSContext *c
     if (!ok)
         return false;
     if (cx->runtime->gcNumber != sample)
         JS_PROPERTY_CACHE(cx).restore(&savedEntry);
     JS_ASSERT(prop);
     JS_ASSERT(pobj == found);
 
     const Shape *shape = (Shape *) prop;
-    if (entry->vword.isSlot()) {
-        JS_ASSERT(entry->vword.toSlot() == shape->slot);
-        JS_ASSERT(!shape->isMethod());
-    } else if (entry->vword.isShape()) {
-        JS_ASSERT(entry->vword.toShape() == shape);
-        JS_ASSERT_IF(shape->isMethod(),
-                     shape->methodObject() == pobj->nativeGetSlot(shape->slot).toObject());
-    } else {
-        Value v;
-        JS_ASSERT(entry->vword.isFunObj());
-        JS_ASSERT(!entry->vword.isNull());
-        JS_ASSERT(pobj->brandedOrHasMethodBarrier());
-        JS_ASSERT(shape->hasDefaultGetterOrIsMethod());
-        JS_ASSERT(pobj->containsSlot(shape->slot));
-        v = pobj->nativeGetSlot(shape->slot);
-        JS_ASSERT(entry->vword.toFunObj() == v.toObject());
-
-        if (shape->isMethod()) {
-            JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
-            JS_ASSERT(shape->methodObject() == v.toObject());
-        }
-    }
+    JS_ASSERT(entry->prop == shape);
 
     return true;
 }
 
 #else
-# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
+# define ASSERT_VALID_PROPERTY_CACHE_HIT(obj,pobj,entry) ((void) 0)
 #endif
 
 /*
  * Ensure that the intrepreter switch can close call-bytecode cases in the
  * same way as non-call bytecodes.
  */
 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
@@ -2619,33 +2594,33 @@ BEGIN_CASE(JSOP_PICK)
     regs.sp[-1] = lval;
 }
 END_CASE(JSOP_PICK)
 
 #define NATIVE_GET(cx,obj,pobj,shape,getHow,vp)                               \
     JS_BEGIN_MACRO                                                            \
         if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {         \
             /* Fast path for Object instance properties. */                   \
-            JS_ASSERT((shape)->slot != SHAPE_INVALID_SLOT ||                  \
+            JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT ||                \
                       !shape->hasDefaultSetter());                            \
-            if (((shape)->slot != SHAPE_INVALID_SLOT))                        \
-                *(vp) = (pobj)->nativeGetSlot((shape)->slot);                 \
+            if (((shape)->slot() != SHAPE_INVALID_SLOT))                      \
+                *(vp) = (pobj)->nativeGetSlot((shape)->slot());               \
             else                                                              \
                 (vp)->setUndefined();                                         \
         } else {                                                              \
             if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))              \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #define NATIVE_SET(cx,obj,shape,entry,strict,vp)                              \
     JS_BEGIN_MACRO                                                            \
         if (shape->hasDefaultSetter() &&                                      \
-            (shape)->slot != SHAPE_INVALID_SLOT &&                            \
-            !(obj)->brandedOrHasMethodBarrier()) {                            \
+            (shape)->hasSlot() &&                                             \
+            !(shape)->isMethod()) {                                           \
             /* Fast path for, e.g., plain Object instance properties. */      \
             (obj)->nativeSetSlotWithType(cx, shape, *vp);                     \
         } else {                                                              \
             if (!js_NativeSet(cx, obj, shape, false, strict, vp))             \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
@@ -2741,17 +2716,17 @@ BEGIN_CASE(JSOP_BINDNAME)
         if (!obj->getParent())
             break;
 
         PropertyCacheEntry *entry;
         JSObject *obj2;
         JSAtom *atom;
         JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
         if (!atom) {
-            ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
+            ASSERT_VALID_PROPERTY_CACHE_HIT(obj, obj2, entry);
             break;
         }
 
         jsid id = ATOM_TO_JSID(atom);
         obj = js_FindIdentifierBase(cx, &regs.fp()->scopeChain(), id);
         if (!obj)
             goto error;
     } while (0);
@@ -3230,183 +3205,37 @@ BEGIN_CASE(JSOP_TYPEOF)
     regs.sp[-1].setString(atom);
 }
 END_CASE(JSOP_TYPEOF)
 
 BEGIN_CASE(JSOP_VOID)
     regs.sp[-1].setUndefined();
 END_CASE(JSOP_VOID)
 
-{
-    /*
-     * Property incops are followed by an equivalent decomposed version,
-     * and we have the option of running either. If type inference is enabled
-     * we run the decomposed version to accumulate observed types and
-     * overflows which inference can process, otherwise we run the fat opcode
-     * as doing so is faster and is what the tracer needs while recording.
-     */
-    JSObject *obj;
-    JSAtom *atom;
-    jsid id;
-    jsint i;
-
 BEGIN_CASE(JSOP_INCELEM)
 BEGIN_CASE(JSOP_DECELEM)
 BEGIN_CASE(JSOP_ELEMINC)
 BEGIN_CASE(JSOP_ELEMDEC)
-
-    if (cx->typeInferenceEnabled()) {
-        len = JSOP_INCELEM_LENGTH;
-        DO_NEXT_OP(len);
-    }
-
-    /*
-     * Delay fetching of id until we have the object to ensure the proper
-     * evaluation order. See bug 372331.
-     */
-    id = JSID_VOID;
-    i = -2;
-    goto fetch_incop_obj;
+    /* No-op */
+END_CASE(JSOP_INCELEM)
 
 BEGIN_CASE(JSOP_INCPROP)
 BEGIN_CASE(JSOP_DECPROP)
 BEGIN_CASE(JSOP_PROPINC)
 BEGIN_CASE(JSOP_PROPDEC)
-
-    if (cx->typeInferenceEnabled()) {
-        len = JSOP_INCPROP_LENGTH;
-        DO_NEXT_OP(len);
-    }
-
-    LOAD_ATOM(0, atom);
-    id = ATOM_TO_JSID(atom);
-    i = -1;
-
-  fetch_incop_obj:
-    FETCH_OBJECT(cx, i, obj);
-    if (JSID_IS_VOID(id))
-        FETCH_ELEMENT_ID(obj, -1, id);
-    goto do_incop;
-
 BEGIN_CASE(JSOP_INCNAME)
 BEGIN_CASE(JSOP_DECNAME)
 BEGIN_CASE(JSOP_NAMEINC)
 BEGIN_CASE(JSOP_NAMEDEC)
 BEGIN_CASE(JSOP_INCGNAME)
 BEGIN_CASE(JSOP_DECGNAME)
 BEGIN_CASE(JSOP_GNAMEINC)
 BEGIN_CASE(JSOP_GNAMEDEC)
-{
-    if (cx->typeInferenceEnabled()) {
-        len = JSOP_INCNAME_LENGTH;
-        DO_NEXT_OP(len);
-    }
-
-    obj = &regs.fp()->scopeChain();
-
-    bool global = (js_CodeSpec[op].format & JOF_GNAME);
-    if (global)
-        obj = obj->getGlobal();
-
-    JSObject *obj2;
-    PropertyCacheEntry *entry;
-    JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
-    if (!atom) {
-        ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
-        if (obj == obj2 && entry->vword.isSlot()) {
-            uint32 slot = entry->vword.toSlot();
-            const Value &rref = obj->nativeGetSlot(slot);
-            int32_t tmp;
-            if (JS_LIKELY(rref.isInt32() && CanIncDecWithoutOverflow(tmp = rref.toInt32()))) {
-                int32_t inc = tmp + ((js_CodeSpec[op].format & JOF_INC) ? 1 : -1);
-                if (!(js_CodeSpec[op].format & JOF_POST))
-                    tmp = inc;
-                obj->nativeSetSlot(slot, Int32Value(inc));
-                PUSH_INT32(tmp);
-                len = JSOP_INCNAME_LENGTH + GetDecomposeLength(regs.pc, JSOP_INCNAME_LENGTH);
-                DO_NEXT_OP(len);
-            }
-        }
-        LOAD_ATOM(0, atom);
-    }
-
-    id = ATOM_TO_JSID(atom);
-    JSProperty *prop;
-    if (!js_FindPropertyHelper(cx, id, true, global, &obj, &obj2, &prop))
-        goto error;
-    if (!prop) {
-        atomNotDefined = atom;
-        goto atom_not_defined;
-    }
-}
-
-do_incop:
-{
-    /*
-     * We need a root to store the value to leave on the stack until
-     * we have done with obj->setProperty.
-     */
-    PUSH_NULL();
-    if (!obj->getGeneric(cx, id, &regs.sp[-1]))
-        goto error;
-
-    const JSCodeSpec *cs = &js_CodeSpec[op];
-    JS_ASSERT(cs->ndefs == 1);
-    JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) >= JOF_TMPSLOT2);
-
-    uint32 format = cs->format;
-    uint32 setPropFlags = (JOF_MODE(format) == JOF_NAME)
-                          ? JSRESOLVE_ASSIGNING
-                          : JSRESOLVE_ASSIGNING | JSRESOLVE_QUALIFIED;
-
-    Value &ref = regs.sp[-1];
-    int32_t tmp;
-    if (JS_LIKELY(ref.isInt32() && CanIncDecWithoutOverflow(tmp = ref.toInt32()))) {
-        int incr = (format & JOF_INC) ? 1 : -1;
-        if (format & JOF_POST)
-            ref.getInt32Ref() = tmp + incr;
-        else
-            ref.getInt32Ref() = tmp += incr;
-
-        {
-            JSAutoResolveFlags rf(cx, setPropFlags);
-            if (!obj->setProperty(cx, id, &ref, script->strictModeCode))
-                goto error;
-        }
-
-        /*
-         * We must set regs.sp[-1] to tmp for both post and pre increments
-         * as the setter overwrites regs.sp[-1].
-         */
-        ref.setInt32(tmp);
-    } else {
-        /* We need an extra root for the result. */
-        PUSH_NULL();
-        if (!DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
-            goto error;
-
-        {
-            JSAutoResolveFlags rf(cx, setPropFlags);
-            if (!obj->setProperty(cx, id, &regs.sp[-1], script->strictModeCode))
-                goto error;
-        }
-
-        regs.sp--;
-    }
-
-    if (cs->nuses == 0) {
-        /* regs.sp[-1] already contains the result of name increment. */
-    } else {
-        regs.sp[-1 - cs->nuses] = regs.sp[-1];
-        regs.sp -= cs->nuses;
-    }
-    len = cs->length + GetDecomposeLength(regs.pc, cs->length);
-    DO_NEXT_OP(len);
-}
-}
+    /* No-op */
+END_CASE(JSOP_INCPROP)
 
 {
     int incr, incr2;
     uint32 slot;
     Value *vp;
 
     /* Position cases so the most frequent i++ does not need a jump. */
 BEGIN_CASE(JSOP_DECARG)
@@ -3462,29 +3291,16 @@ BEGIN_CASE(JSOP_LOCALINC)
 }
 
 BEGIN_CASE(JSOP_THIS)
     if (!ComputeThis(cx, regs.fp()))
         goto error;
     PUSH_COPY(regs.fp()->thisValue());
 END_CASE(JSOP_THIS)
 
-BEGIN_CASE(JSOP_UNBRANDTHIS)
-{
-    if (!ComputeThis(cx, regs.fp()))
-        goto error;
-    Value &thisv = regs.fp()->thisValue();
-    if (thisv.isObject()) {
-        JSObject *obj = &thisv.toObject();
-        if (obj->isNative())
-            obj->unbrand(cx);
-    }
-}
-END_CASE(JSOP_UNBRANDTHIS)
-
 BEGIN_CASE(JSOP_GETPROP)
 BEGIN_CASE(JSOP_GETXPROP)
 BEGIN_CASE(JSOP_LENGTH)
 {
     Value rval;
     do {
         Value *vp = &regs.sp[-1];
 
@@ -3533,29 +3349,20 @@ BEGIN_CASE(JSOP_LENGTH)
         VALUE_TO_OBJECT(cx, vp, obj);
         JSObject *aobj = js_GetProtoIfDenseArray(obj);
 
         PropertyCacheEntry *entry;
         JSObject *obj2;
         JSAtom *atom;
         JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
         if (!atom) {
-            ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
-            if (entry->vword.isFunObj()) {
-                rval.setObject(entry->vword.toFunObj());
-            } else if (entry->vword.isSlot()) {
-                uint32 slot = entry->vword.toSlot();
-                rval = obj2->nativeGetSlot(slot);
-            } else {
-                JS_ASSERT(entry->vword.isShape());
-                const Shape *shape = entry->vword.toShape();
-                NATIVE_GET(cx, obj, obj2, shape,
-                           regs.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
-                           &rval);
-            }
+            ASSERT_VALID_PROPERTY_CACHE_HIT(aobj, obj2, entry);
+            NATIVE_GET(cx, obj, obj2, entry->prop,
+                       regs.fp()->hasImacropc() ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
+                       &rval);
             break;
         }
 
         jsid id = ATOM_TO_JSID(atom);
         if (JS_LIKELY(!aobj->getOps()->getProperty)
             ? !js_GetPropertyHelper(cx, obj, id,
                                     (regs.fp()->hasImacropc() ||
                                      regs.pc[JSOP_GETPROP_LENGTH] == JSOP_IFEQ)
@@ -3604,27 +3411,18 @@ BEGIN_CASE(JSOP_CALLPROP)
     JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
     Value rval;
 
     PropertyCacheEntry *entry;
     JSObject *obj2;
     JSAtom *atom;
     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
     if (!atom) {
-        ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
-        if (entry->vword.isFunObj()) {
-            rval.setObject(entry->vword.toFunObj());
-        } else if (entry->vword.isSlot()) {
-            uint32 slot = entry->vword.toSlot();
-            rval = obj2->nativeGetSlot(slot);
-        } else {
-            JS_ASSERT(entry->vword.isShape());
-            const Shape *shape = entry->vword.toShape();
-            NATIVE_GET(cx, &objv.toObject(), obj2, shape, JSGET_NO_METHOD_BARRIER, &rval);
-        }
+        ASSERT_VALID_PROPERTY_CACHE_HIT(aobj, obj2, entry);
+        NATIVE_GET(cx, &objv.toObject(), obj2, entry->prop, JSGET_NO_METHOD_BARRIER, &rval);
         regs.sp[-1] = rval;
         assertSameCompartment(cx, regs.sp[-1]);
         PUSH_COPY(lval);
     } else {
         /*
          * Cache miss: use the immediate atom that was loaded for us under
          * PropertyCache::test.
          */
@@ -3662,21 +3460,16 @@ BEGIN_CASE(JSOP_CALLPROP)
         if (!OnUnknownMethod(cx, regs.sp - 2))
             goto error;
     }
 #endif
     TypeScript::Monitor(cx, script, regs.pc, rval);
 }
 END_CASE(JSOP_CALLPROP)
 
-BEGIN_CASE(JSOP_UNBRAND)
-    JS_ASSERT(regs.sp - regs.fp()->slots() >= 1);
-    regs.sp[-1].toObject().unbrand(cx);
-END_CASE(JSOP_UNBRAND)
-
 BEGIN_CASE(JSOP_SETGNAME)
 BEGIN_CASE(JSOP_SETNAME)
 BEGIN_CASE(JSOP_SETPROP)
 BEGIN_CASE(JSOP_SETMETHOD)
 {
     Value rval = regs.sp[-1];
     JS_ASSERT_IF(op == JSOP_SETMETHOD, IsFunctionObject(rval));
     Value &lref = regs.sp[-2];
@@ -3712,100 +3505,40 @@ BEGIN_CASE(JSOP_SETMETHOD)
         JSObject *obj2;
         JSAtom *atom;
         if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
             /*
              * Property cache hit, only partially confirmed by testForSet. We
              * know that the entry applies to regs.pc and that obj's shape
              * matches.
              *
-             * The entry predicts either a new property to be added directly to
-             * obj by this set, or on an existing "own" property, or on a
-             * prototype property that has a setter.
+             * The entry predicts a set either an existing "own" property, or
+             * on a prototype property that has a setter.
              */
-            const Shape *shape = entry->vword.toShape();
+            const Shape *shape = entry->prop;
             JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
-            JS_ASSERT_IF(shape->hasSlot(), entry->vcapTag() == 0);
-
-            /*
-             * Fastest path: check whether obj already has the cached shape and
-             * call NATIVE_SET and break to get out of the do-while(0). But we
-             * can call NATIVE_SET only for a direct or proto-setter hit.
-             */
-            if (!entry->adding()) {
-                if (entry->vcapTag() == 0 ||
-                    ((obj2 = obj->getProto()) && obj2->shape() == entry->vshape()))
-                {
+            JS_ASSERT_IF(shape->hasSlot(), !entry->vindex);
+
+            if (entry->vindex == 0 ||
+                ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
 #ifdef DEBUG
-                    if (entry->directHit()) {
-                        JS_ASSERT(obj->nativeContains(cx, *shape));
-                    } else {
-                        JS_ASSERT(obj2->nativeContains(cx, *shape));
-                        JS_ASSERT(entry->vcapTag() == 1);
-                        JS_ASSERT(entry->kshape != entry->vshape());
-                        JS_ASSERT(!shape->hasSlot());
-                    }
-#endif
-
-                    PCMETER(cache->pchits++);
-                    PCMETER(cache->setpchits++);
-                    NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
-                    break;
-                }
-            } else {
-                JS_ASSERT(obj->isExtensible());
-
-                if (obj->nativeEmpty()) {
-                    if (!obj->ensureClassReservedSlotsForEmptyObject(cx))
-                        goto error;
+                if (entry->directHit()) {
+                    JS_ASSERT(obj->nativeContains(cx, *shape));
+                } else {
+                    JS_ASSERT(obj2->nativeContains(cx, *shape));
+                    JS_ASSERT(entry->vindex == 1);
+                    JS_ASSERT(entry->kshape != entry->pshape);
+                    JS_ASSERT(!shape->hasSlot());
                 }
-
-                uint32 slot;
-                if (shape->previous() == obj->lastProperty() &&
-                    entry->vshape() == rt->protoHazardShape &&
-                    shape->hasDefaultSetter() &&
-                    obj->getClass()->addProperty == JS_PropertyStub) {
-                    slot = shape->slot;
-                    JS_ASSERT(slot == obj->slotSpan());
-
-                    /*
-                     * Fast path: adding a plain old property that was once at
-                     * the frontier of the property tree, whose slot is next to
-                     * claim among the already-allocated slots in obj, where
-                     * shape->table has not been created yet.
-                     */
-                    PCMETER(cache->pchits++);
-                    PCMETER(cache->addpchits++);
-
-                    if (slot < obj->numSlots()) {
-                        JS_ASSERT(obj->getSlot(slot).isUndefined());
-                    } else {
-                        if (!obj->allocSlot(cx, &slot))
-                            goto error;
-                        JS_ASSERT(slot == shape->slot);
-                    }
-
-                    /* Simply extend obj's property tree path with shape! */
-                    obj->extend(cx, shape);
-
-                    /*
-                     * No method change check here because here we are adding a
-                     * new property, not updating an existing slot's value that
-                     * might contain a method of a branded shape.
-                     */
-                    TRACE_1(AddProperty, obj);
-                    obj->nativeSetSlotWithType(cx, shape, rval);
-
-                    /*
-                     * Purge the property cache of the id we may have just
-                     * shadowed in obj's scope and proto chains.
-                     */
-                    js_PurgeScopeChain(cx, obj, shape->propid);
-                    break;
-                }
+#endif
+
+                PCMETER(cache->pchits++);
+                PCMETER(cache->setpchits++);
+                NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
+                break;
             }
             PCMETER(cache->setpcmisses++);
 
             LOAD_ATOM(0, atom);
         } else {
             JS_ASSERT(atom);
         }
 
@@ -4113,36 +3846,26 @@ BEGIN_CASE(JSOP_NAME)
 BEGIN_CASE(JSOP_CALLNAME)
 {
     JSObject *obj = &regs.fp()->scopeChain();
 
     bool global = js_CodeSpec[op].format & JOF_GNAME;
     if (global)
         obj = obj->getGlobal();
 
-    const Shape *shape;
     Value rval;
 
     PropertyCacheEntry *entry;
     JSObject *obj2;
     JSAtom *atom;
     JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
     if (!atom) {
-        ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
-        if (entry->vword.isFunObj()) {
-            PUSH_OBJECT(entry->vword.toFunObj());
-        } else if (entry->vword.isSlot()) {
-            uintN slot = entry->vword.toSlot();
-            PUSH_COPY(obj2->nativeGetSlot(slot));
-        } else {
-            JS_ASSERT(entry->vword.isShape());
-            shape = entry->vword.toShape();
-            NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval);
-            PUSH_COPY(rval);
-        }
+        ASSERT_VALID_PROPERTY_CACHE_HIT(obj, obj2, entry);
+        NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval);
+        PUSH_COPY(rval);
 
         TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
 
         JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
         if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
             PUSH_IMPLICIT_THIS(cx, obj, regs.sp[-1]);
         len = JSOP_NAME_LENGTH;
         DO_NEXT_OP(len);
@@ -4165,17 +3888,17 @@ BEGIN_CASE(JSOP_CALLNAME)
         goto atom_not_defined;
     }
 
     /* Take the slow path if prop was not found in a native object. */
     if (!obj->isNative() || !obj2->isNative()) {
         if (!obj->getGeneric(cx, id, &rval))
             goto error;
     } else {
-        shape = (Shape *)prop;
+        Shape *shape = (Shape *)prop;
         JSObject *normalized = obj;
         if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
             normalized = js_UnwrapWithObject(cx, normalized);
         NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval);
     }
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, regs.pc, rval);
@@ -5148,59 +4871,34 @@ BEGIN_CASE(JSOP_INITMETHOD)
     JS_ASSERT(regs.sp - regs.fp()->base() >= 2);
     Value rval = regs.sp[-1];
 
     /* Load the object being initialized into lval/obj. */
     JSObject *obj = &regs.sp[-2].toObject();
     JS_ASSERT(obj->isObject());
 
     /*
-     * Probe the property cache.
-     *
-     * On a hit, if the cached shape has a non-default setter, it must be
-     * __proto__. If shape->previous() != obj->lastProperty(), there must be a
-     * repeated property name. The fast path does not handle these two cases.
+     * Probe the property cache to see if this is a set on an existing property
+     * added by a NEWOBJECT or a previous INITPROP. If the cached shape has a
+     * non-default setter, it must be __proto__, so don't handle this.
      */
     PropertyCacheEntry *entry;
-    const Shape *shape;
-    if (JS_PROPERTY_CACHE(cx).testForInit(rt, regs.pc, obj, &shape, &entry) &&
-        shape->hasDefaultSetter() &&
-        shape->previous() == obj->lastProperty())
-    {
+    JSObject *obj2;
+    JSAtom *atom;
+    if (JS_PROPERTY_CACHE(cx).testForSet(cx, regs.pc, obj, &entry, &obj2, &atom) &&
+        entry->prop->hasDefaultSetter() &&
+        entry->vindex == 0) {
+        JS_ASSERT(obj == obj2);
         /* Fast path. Property cache hit. */
-        uint32 slot = shape->slot;
-
-        JS_ASSERT(slot == obj->slotSpan());
-        JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
-        if (slot < obj->numSlots()) {
-            JS_ASSERT(obj->getSlot(slot).isUndefined());
-        } else {
-            if (!obj->allocSlot(cx, &slot))
-                goto error;
-            JS_ASSERT(slot == shape->slot);
-        }
-
-        /* A new object, or one we just extended in a recent initprop op. */
-        JS_ASSERT(!obj->lastProperty() ||
-                  obj->shape() == obj->lastProperty()->shapeid);
-        obj->extend(cx, shape);
-
-        /*
-         * No method change check here because here we are adding a new
-         * property, not updating an existing slot's value that might
-         * contain a method of a branded shape.
-         */
-        TRACE_1(AddProperty, obj);
-        obj->nativeSetSlotWithType(cx, shape, rval);
+        obj->nativeSetSlotWithType(cx, entry->prop, rval);
     } else {
         PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
+        LOAD_ATOM(0, atom);
 
         /* Get the immediate property name into id. */
-        JSAtom *atom;
-        LOAD_ATOM(0, atom);
         jsid id = ATOM_TO_JSID(atom);
 
         uintN defineHow = (op == JSOP_INITMETHOD)
                           ? DNP_CACHE_RESULT | DNP_SET_METHOD
                           : DNP_CACHE_RESULT;
         if (JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
             ? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)
             : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -205,18 +205,18 @@ EnumerateNativeProperties(JSContext *cx,
                           AutoIdVector *props)
 {
     size_t initialLength = props->length();
 
     /* Collect all unique properties from this object's scope. */
     for (Shape::Range r = pobj->lastProperty()->all(); !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
 
-        if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid) &&
-            !Enumerate(cx, obj, pobj, shape.propid, shape.enumerable(), flags, ht, props))
+        if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) &&
+            !Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
         {
             return false;
         }
     }
 
     ::Reverse(props->begin() + initialLength, props->end());
     return true;
 }
@@ -426,32 +426,32 @@ NewIteratorObject(JSContext *cx, uintN f
     return NewBuiltinClassInstance(cx, &IteratorClass);
 }
 
 NativeIterator *
 NativeIterator::allocateIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
 {
     size_t plength = props.length();
     NativeIterator *ni = (NativeIterator *)
-        cx->malloc_(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
+        cx->malloc_(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(Shape *));
     if (!ni)
         return NULL;
     ni->props_array = ni->props_cursor = (jsid *) (ni + 1);
     ni->props_end = (jsid *)ni->props_array + plength;
     if (plength)
         memcpy(ni->props_array, props.begin(), plength * sizeof(jsid));
     return ni;
 }
 
 inline void
 NativeIterator::init(JSObject *obj, uintN flags, uint32 slength, uint32 key)
 {
     this->obj = obj;
     this->flags = flags;
-    this->shapes_array = (uint32 *) this->props_end;
+    this->shapes_array = (const Shape **) this->props_end;
     this->shapes_length = slength;
     this->shapes_key = key;
 }
 
 static inline void
 RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
 {
     /* Register non-escaping native enumerators (for-in) with the current context. */
@@ -490,17 +490,17 @@ VectorToKeyIterator(JSContext *cx, JSObj
          * computed for the cache lookup earlier, as constructing iterobj could
          * have triggered a shape-regenerating GC.  Don't bother with regenerating
          * the shape key; if such a GC *does* occur, we can only get hits through
          * the one-slot lastNativeIterator cache.
          */
         JSObject *pobj = obj;
         size_t ind = 0;
         do {
-            ni->shapes_array[ind++] = pobj->shape();
+            ni->shapes_array[ind++] = pobj->lastProperty();
             pobj = pobj->getProto();
         } while (pobj);
         JS_ASSERT(ind == slength);
     }
 
     iterobj->setNativeIterator(ni);
     vp->setObject(*iterobj);
 
@@ -558,17 +558,17 @@ UpdateNativeIterator(NativeIterator *ni,
     // Update the object for which the native iterator is associated, so
     // SuppressDeletedPropertyHelper will recognize the iterator as a match.
     ni->obj = obj;
 }
 
 bool
 GetIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
 {
-    Vector<uint32, 8> shapes(cx);
+    Vector<const Shape *, 8> shapes(cx);
     uint32 key = 0;
 
     bool keysOnly = (flags == JSITER_ENUMERATE);
 
     if (obj) {
         /* Enumerate Iterator.prototype directly. */
         JSIteratorOp op = obj->getClass()->ext.iteratorObject;
         if (op && (obj->getClass() != &IteratorClass || obj->getNativeIterator())) {
@@ -588,19 +588,19 @@ GetIterator(JSContext *cx, JSObject *obj
              * will result in a miss.
              */
             JSObject *last = cx->compartment->nativeIterCache.last;
             JSObject *proto = obj->getProto();
             if (last) {
                 NativeIterator *lastni = last->getNativeIterator();
                 if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
                     obj->isNative() &&
-                    obj->shape() == lastni->shapes_array[0] &&
+                    obj->lastProperty() == lastni->shapes_array[0] &&
                     proto && proto->isNative() &&
-                    proto->shape() == lastni->shapes_array[1] &&
+                    proto->lastProperty() == lastni->shapes_array[1] &&
                     !proto->getProto()) {
                     vp->setObject(*last);
                     UpdateNativeIterator(lastni, obj);
                     RegisterEnumerator(cx, last, lastni);
                     return true;
                 }
             }
 
@@ -613,19 +613,19 @@ GetIterator(JSContext *cx, JSObject *obj
             JSObject *pobj = obj;
             do {
                 if (!pobj->isNative() ||
                     obj->getOps()->enumerate ||
                     pobj->getClass()->enumerate != JS_EnumerateStub) {
                     shapes.clear();
                     goto miss;
                 }
-                uint32 shape = pobj->shape();
-                key = (key + (key << 16)) ^ shape;
-                if (!shapes.append(shape))
+                const Shape *shape = pobj->lastProperty();
+                key = (key + (key << 16)) ^ ((jsuword)shape >> 3);
+                if (!shapes.append((Shape *) shape))
                     return false;
                 pobj = pobj->getProto();
             } while (pobj);
 
             JSObject *iterobj = cx->compartment->nativeIterCache.get(key);
             if (iterobj) {
                 NativeIterator *ni = iterobj->getNativeIterator();
                 if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -68,17 +68,17 @@
 
 namespace js {
 
 struct NativeIterator {
     JSObject  *obj;
     jsid      *props_array;
     jsid      *props_cursor;
     jsid      *props_end;
-    uint32    *shapes_array;
+    const Shape **shapes_array;
     uint32    shapes_length;
     uint32    shapes_key;
     uint32    flags;
     JSObject  *next;  /* Forms cx->enumerators list, garbage otherwise. */
 
     bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; }
 
     inline jsid *begin() const {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -108,18 +108,16 @@
 #include "jsscriptinlines.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
-JS_FRIEND_DATA(js::Shape) Shape::sharedNonNative(SHAPELESS);
-
 Class js::ObjectClass = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
@@ -2937,28 +2935,28 @@ js_Object(JSContext *cx, uintN argc, Val
 JSObject *
 js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent,
                       gc::AllocKind kind, const Shape *shape)
 {
     JSObject *res = NewObjectWithType(cx, type, parent, kind);
     if (!res)
         return NULL;
 
-    if (JSID_IS_EMPTY(shape->propid))
+    if (shape->isEmptyShape())
         return res;
 
     /* Get all the ids in the object, in order. */
     js::AutoIdVector ids(cx);
-    for (unsigned i = 0; i <= shape->slot; i++) {
+    for (unsigned i = 0; i <= shape->slot(); i++) {
         if (!ids.append(JSID_VOID))
             return NULL;
     }
     const js::Shape *nshape = shape;
-    while (!JSID_IS_EMPTY(nshape->propid)) {
-        ids[nshape->slot] = nshape->propid;
+    while (!nshape->isEmptyShape()) {
+        ids[nshape->slot()] = nshape->propid();
         nshape = nshape->previous();
     }
 
     /* Construct the new shape. */
     for (unsigned i = 0; i < ids.length(); i++) {
         if (!DefineNativeProperty(cx, res, ids[i], js::UndefinedValue(), NULL, NULL,
                                   JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) {
             return NULL;
@@ -3109,17 +3107,17 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_
 JSObject * FASTCALL
 js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot)
 {
 #ifdef DEBUG
     JS_ASSERT(ctor->isFunction());
     JS_ASSERT(ctor->getFunctionPrivate()->isInterpreted());
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
     const Shape *shape = ctor->nativeLookup(cx, id);
-    JS_ASSERT(shape->slot == protoSlot);
+    JS_ASSERT(shape->slot() == protoSlot);
     JS_ASSERT(!shape->configurable());
     JS_ASSERT(!shape->isMethod());
 #endif
 
     JSObject *parent = ctor->getParent();
     JSObject *proto;
     const Value &protov = ctor->getSlot(protoSlot);
     if (protov.isObject()) {
@@ -3231,17 +3229,17 @@ js_InferFlags(JSContext *cx, uintN defau
     JSScript *script = cx->stack.currentScript(&pc);
     if (!script || !pc)
         return defaultFlags;
 
     cs = &js_CodeSpec[js_GetOpcode(cx, script, pc)];
     format = cs->format;
     if (JOF_MODE(format) != JOF_NAME)
         flags |= JSRESOLVE_QUALIFIED;
-    if (format & (JOF_SET | JOF_FOR)) {
+    if (format & JOF_SET) {
         flags |= JSRESOLVE_ASSIGNING;
     } else if (cs->length >= 0) {
         pc += cs->length;
         if (pc < script->code + script->length && Detecting(cx, pc))
             flags |= JSRESOLVE_DETECTING;
     }
     if (format & JOF_DECLARING)
         flags |= JSRESOLVE_DECLARING;
@@ -3535,17 +3533,18 @@ js_CloneBlockObject(JSContext *cx, JSObj
 
     JSObject *clone = js_NewGCObject(cx, kind);
     if (!clone)
         return NULL;
 
     StackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
 
     /* The caller sets parent on its own. */
-    clone->initClonedBlock(cx, type, priv);
+    if (!clone->initClonedBlock(cx, type, priv))
+        return NULL;
 
     if (!clone->ensureInstanceReservedSlots(cx, count + 1))
         return NULL;
 
     clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
 
     JS_ASSERT(clone->isClonedBlock());
     return clone;
@@ -3632,22 +3631,26 @@ block_setProperty(JSContext *cx, JSObjec
     return true;
 }
 
 const Shape *
 JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index)
 {
     JS_ASSERT(isStaticBlock());
 
-    /* Use JSPROP_ENUMERATE to aid the disassembler. */
+    /*
+     * Use JSPROP_ENUMERATE to aid the disassembler, and don't convert this
+     * object to dictionary mode so that we can clone the block's shape later.
+     */
     uint32 slot = JSSLOT_FREE(&BlockClass) + index;
     const Shape *shape = addProperty(cx, id,
                                      block_getProperty, block_setProperty,
                                      slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
-                                     Shape::HAS_SHORTID, index);
+                                     Shape::HAS_SHORTID, index,
+                                     /* allowDictionary = */ false);
     if (!shape)
         return NULL;
     if (slot >= numSlots() && !growSlots(cx, slot + 1))
         return NULL;
     return shape;
 }
 
 JSBool
@@ -3696,20 +3699,20 @@ JSObject::copyPropertiesFrom(JSContext *
         const Shape *shape = shapes[--n];
         uintN attrs = shape->attributes();
         PropertyOp getter = shape->getter();
         if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter))
             return false;
         StrictPropertyOp setter = shape->setter();
         if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter))
             return false;
-        Value v = shape->hasSlot() ? obj->getSlot(shape->slot) : UndefinedValue();
+        Value v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue();
         if (!cx->compartment->wrap(cx, &v))
             return false;
-        if (!defineProperty(cx, shape->propid, v, getter, setter, attrs))
+        if (!defineProperty(cx, shape->propid(), v, getter, setter, attrs))
             return false;
     }
     return true;
 }
 
 static bool
 CopySlots(JSContext *cx, JSObject *from, JSObject *to)
 {
@@ -3801,16 +3804,26 @@ JSObject::ReserveForTradeGuts(JSContext 
      * When performing multiple swaps between objects which may have different
      * numbers of fixed slots, we reserve all space ahead of time so that the
      * swaps can be performed infallibly.
      */
 
     if (a->structSize() == b->structSize())
         return true;
 
+    /*
+     * If either object is native, it needs a new shape to preserve the
+     * invariant that objects with the same shape have the same number of
+     * inline slots.
+     */
+    if (a->isNative() && !a->generateOwnShape(cx))
+        return false;
+    if (b->isNative() && !b->generateOwnShape(cx))
+        return false;
+
     /* The avals/bvals vectors hold all original values from the objects. */
 
     unsigned acap = a->numSlots();
     unsigned bcap = b->numSlots();
 
     if (!reserved.avals.reserve(acap))
         return false;
     if (!reserved.bvals.reserve(bcap))
@@ -3888,26 +3901,16 @@ JSObject::TradeGuts(JSContext *cx, JSObj
         memcpy(b, tmp, size);
     } else {
         /*
          * If the objects are of differing sizes, use the space we reserved
          * earlier to save the slots from each object and then copy them into
          * the new layout for the other object.
          */
 
-        /*
-         * If either object is native, it needs a new shape to preserve the
-         * invariant that objects with the same shape have the same number of
-         * inline slots.
-         */
-        if (a->isNative())
-            a->generateOwnShape(cx);
-        if (b->isNative())
-            b->generateOwnShape(cx);
-
         unsigned acap = a->numSlots();
         unsigned bcap = b->numSlots();
 
         for (size_t i = 0; i < acap; i++)
             reserved.avals.infallibleAppend(a->getSlot(i));
 
         for (size_t i = 0; i < bcap; i++)
             reserved.bvals.infallibleAppend(b->getSlot(i));
@@ -4094,33 +4097,33 @@ js_XDRBlockObject(JSXDRState *xdr, JSObj
                 return false;
         }
     } else {
         AutoShapeVector shapes(cx);
         shapes.growBy(count);
 
         for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
             shape = &r.front();
-            shapes[shape->shortid] = shape;
+            shapes[shape->shortid()] = shape;
         }
 
         /*
          * XDR the block object's properties. We know that there are 'count'
          * properties to XDR, stored as id/shortid pairs.
          */
         for (uintN i = 0; i < count; i++) {
             shape = shapes[i];
             JS_ASSERT(shape->getter() == block_getProperty);
 
-            jsid propid = shape->propid;
+            jsid propid = shape->propid();
             JS_ASSERT(JSID_IS_ATOM(propid));
             JSAtom *atom = JSID_TO_ATOM(propid);
 
 #ifdef DEBUG
-            uint16 shortid = uint16(shape->shortid);
+            uint16 shortid = uint16(shape->shortid());
             JS_ASSERT(shortid == i);
 #endif
 
             /* XDR the real id. */
             if (!js_XDRAtom(xdr, &atom))
                 return false;
         }
     }
@@ -4698,23 +4701,23 @@ SetProto(JSContext *cx, JSObject *obj, J
     }
 
     if (proto && proto->isXML()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
         return false;
     }
 
     /*
-     * Regenerate property cache shape ids for all of the scopes along the
-     * old prototype chain to invalidate their property cache entries, in
-     * case any entries were filled by looking up through obj.
+     * Regenerate shapes for all of the scopes along the old prototype chain,
+     * in case any entries were filled by looking up through obj.
      */
     JSObject *oldproto = obj;
     while (oldproto && oldproto->isNative()) {
-        oldproto->protoShapeChange(cx);
+        if (!oldproto->protoShapeChange(cx))
+            return false;
         oldproto = oldproto->getProto();
     }
 
     if (checkForCycles) {
         for (JSObject *obj2 = proto; obj2; obj2 = obj2->getProto()) {
             if (obj2 == obj) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE,
                                      js_proto_str);
@@ -4848,18 +4851,18 @@ js_FindClassObject(JSContext *cx, JSObje
     }
 
     JS_ASSERT(obj->isNative());
     if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, &pobj, &prop))
         return false;
     Value v = UndefinedValue();
     if (prop && pobj->isNative()) {
         shape = (Shape *) prop;
-        if (pobj->containsSlot(shape->slot)) {
-            v = pobj->nativeGetSlot(shape->slot);
+        if (pobj->containsSlot(shape->slot())) {
+            v = pobj->nativeGetSlot(shape->slot());
             if (v.isPrimitive())
                 v.setUndefined();
         }
     }
     *vp = v;
     return true;
 }
 
@@ -4932,20 +4935,20 @@ js_ConstructObject(JSContext *cx, Class 
 
 bool
 JSObject::allocSlot(JSContext *cx, uint32 *slotp)
 {
     uint32 slot = slotSpan();
     JS_ASSERT(slot >= JSSLOT_FREE(getClass()));
 
     /*
-     * If this object is in dictionary mode and it has a property table, try to
-     * pull a free slot from the property table's slot-number freelist.
+     * If this object is in dictionary mode, try to pull a free slot from the
+     * property table's slot-number freelist.
      */
-    if (inDictionaryMode() && lastProp->hasTable()) {
+    if (inDictionaryMode()) {
         PropertyTable *table = lastProp->getTable();
         uint32 last = table->freelist;
         if (last != SHAPE_INVALID_SLOT) {
 #ifdef DEBUG
             JS_ASSERT(last < slot);
             uint32 next = getSlot(last).toPrivateUint32();
             JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
 #endif
@@ -4960,96 +4963,101 @@ JSObject::allocSlot(JSContext *cx, uint3
     }
 
     if (slot >= numSlots() && !growSlots(cx, slot + 1))
         return false;
 
     /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
     JS_ASSERT(getSlot(slot).isUndefined());
     *slotp = slot;
+
+    if (inDictionaryMode())
+        lastProp->base()->slotSpan = slot + 1;
+
     return true;
 }
 
-bool
+void
 JSObject::freeSlot(JSContext *cx, uint32 slot)
 {
     uint32 limit = slotSpan();
     JS_ASSERT(slot < limit);
 
-    if (inDictionaryMode() && lastProp->hasTable()) {
+    if (inDictionaryMode()) {
         uint32 &last = lastProp->getTable()->freelist;
 
         /* Can't afford to check the whole freelist, but let's check the head. */
         JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < limit && last != slot);
 
         /*
-         * Freeing a slot other than the last one mapped by this object's
-         * shape (and not a reserved slot; see bug 595230): push the slot onto
-         * the dictionary property table's freelist. We want to let the last
-         * slot be freed by shrinking the dslots vector; see js_TraceObject.
+         * Place all freed slots other than reserved slots (bug 595230) on the
+         * dictionary's free list.
          */
-        if (JSSLOT_FREE(getClass()) <= slot && slot + 1 < limit) {
+        if (JSSLOT_FREE(getClass()) <= slot) {
             JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
             setSlot(slot, PrivateUint32Value(last));
             last = slot;
-            return true;
+            return;
         }
     }
     setSlot(slot, UndefinedValue());
-    return false;
-}
-
-static JSBool
+}
+
+static bool
 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
 {
     const Shape *shape;
 
     while (obj) {
         if (!obj->isNative()) {
             obj = obj->getProto();
             continue;
         }
         shape = obj->nativeLookup(cx, id);
         if (shape) {
             PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
-            obj->shadowingShapeChange(cx, *shape);
+            if (!obj->shadowingShapeChange(cx, *shape))
+                return false;
 
             if (!obj->getParent()) {
                 /*
                  * All scope chains end in a global object, so this will change
                  * the global shape. jstracer.cpp assumes that the global shape
                  * never changes on trace, so we must deep-bail here.
                  */
                 LeaveTrace(cx);
             }
-            return JS_TRUE;
+            return true;
         }
         obj = obj->getProto();
     }
-    return JS_FALSE;
-}
-
-void
+
+    return true;
+}
+
+bool
 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
 {
     JS_ASSERT(obj->isDelegate());
     PurgeProtoChain(cx, obj->getProto(), id);
 
     /*
      * We must purge the scope chain only for Call objects as they are the only
      * kind of cacheable non-global object that can gain properties after outer
      * properties with the same names have been cached or traced. Call objects
      * may gain such properties via eval introducing new vars; see bug 490364.
      */
     if (obj->isCall()) {
         while ((obj = obj->getParent()) != NULL) {
-            if (PurgeProtoChain(cx, obj, id))
-                break;
+            if (!PurgeProtoChain(cx, obj, id))
+                return false;
         }
     }
+
+    return true;
 }
 
 const Shape *
 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
                      PropertyOp getter, StrictPropertyOp setter, uint32 slot,
                      uintN attrs, uintN flags, intN shortid)
 {
     JS_ASSERT(!(flags & Shape::METHOD));
@@ -5057,17 +5065,18 @@ js_AddNativeProperty(JSContext *cx, JSOb
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     /*
      * Purge the property cache of now-shadowed id in obj's scope chain. Do
      * this optimistically (assuming no failure below) before locking obj, so
      * we can lock the shadowed scope.
      */
-    js_PurgeScopeChain(cx, obj, id);
+    if (!js_PurgeScopeChain(cx, obj, id))
+        return NULL;
 
     if (!obj->ensureClassReservedSlots(cx))
         return NULL;
 
     return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
 }
 
 const Shape *
@@ -5075,32 +5084,24 @@ js_ChangeNativePropertyAttrs(JSContext *
                              const Shape *shape, uintN attrs, uintN mask,
                              PropertyOp getter, StrictPropertyOp setter)
 {
     if (!obj->ensureClassReservedSlots(cx))
         return NULL;
 
     /*
      * Check for freezing an object with shape-memoized methods here, on a
-     * shape-by-shape basis. Note that getter may be a pun of the method's
-     * joined function object value, to indicate "no getter change". In this
-     * case we must null getter to get the desired JS_PropertyStub behavior.
+     * shape-by-shape basis.
      */
     if ((attrs & JSPROP_READONLY) && shape->isMethod()) {
-        JSObject *funobj = &shape->methodObject();
-        Value v = ObjectValue(*funobj);
+        Value v = ObjectValue(*obj->nativeGetMethod(shape));
 
         shape = obj->methodReadBarrier(cx, *shape, &v);
         if (!shape)
             return NULL;
-
-        if (CastAsObject(getter) == funobj) {
-            JS_ASSERT(!(attrs & JSPROP_GETTER));
-            getter = NULL;
-        }
     }
 
     return obj->changeProperty(cx, shape, attrs, mask, getter, setter);
 }
 
 JSBool
 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                   PropertyOp getter, StrictPropertyOp setter, uintN attrs)
@@ -5125,20 +5126,20 @@ js_DefineElement(JSContext *cx, JSObject
  * both while saving cycles for classes that stub their addProperty hook.
  */
 static inline bool
 CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp)
 {
     if (clasp->addProperty != JS_PropertyStub) {
         Value nominal = *vp;
 
-        if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->propid, vp))
+        if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->propid(), vp))
             return false;
         if (*vp != nominal) {
-            if (obj->containsSlot(shape->slot))
+            if (obj->containsSlot(shape->slot()))
                 obj->nativeSetSlotWithType(cx, shape, *vp);
         }
     }
     return true;
 }
 
 namespace js {
 
@@ -5194,26 +5195,20 @@ DefineNativeProperty(JSContext *cx, JSOb
         }
     }
 
     /*
      * Purge the property cache of any properties named by id that are about
      * to be shadowed in obj's scope chain unless it is known a priori that it
      * is not possible. We do this before locking obj to avoid nesting locks.
      */
-    if (!(defineHow & DNP_DONT_PURGE))
-        js_PurgeScopeChain(cx, obj, id);
-
-    /*
-     * Check whether a readonly property or setter is being defined on a known
-     * prototype object. See the comment in jscntxt.h before protoHazardShape's
-     * member declaration.
-     */
-    if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER)))
-        cx->runtime->protoHazardShape = js_GenerateShape(cx);
+    if (!(defineHow & DNP_DONT_PURGE)) {
+        if (!js_PurgeScopeChain(cx, obj, id))
+            return NULL;
+    }
 
     /* Use the object's class getter and setter by default. */
     Class *clasp = obj->getClass();
     if (!(defineHow & DNP_SET_METHOD)) {
         if (!getter && !(attrs & JSPROP_GETTER))
             getter = clasp->getProperty;
         if (!setter && !(attrs & JSPROP_SETTER))
             setter = clasp->setProperty;
@@ -5245,96 +5240,58 @@ DefineNativeProperty(JSContext *cx, JSOb
         /* Add a new property, or replace an existing one of the same id. */
         if (defineHow & DNP_SET_METHOD) {
             JS_ASSERT(clasp == &ObjectClass);
             JS_ASSERT(IsFunctionObject(value));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
             JS_ASSERT(!getter && !setter);
 
             JSObject *funobj = &value.toObject();
-            if (funobj->getFunctionPrivate() == funobj) {
+            if (funobj->getFunctionPrivate() == funobj)
                 flags |= Shape::METHOD;
-                getter = CastAsPropertyOp(funobj);
-            }
         }
 
         if (const Shape *existingShape = obj->nativeLookup(cx, id)) {
             if (existingShape->hasSlot())
-                AbortRecordingIfUnexpectedGlobalWrite(cx, obj, existingShape->slot);
+                AbortRecordingIfUnexpectedGlobalWrite(cx, obj, existingShape->slot());
 
             if (existingShape->isMethod() &&
-                ObjectValue(existingShape->methodObject()) == valueCopy)
+                ObjectValue(*obj->nativeGetMethod(existingShape)) == valueCopy)
             {
                 /*
                  * Redefining an existing shape-memoized method object without
                  * changing the property's value, perhaps to change attributes.
                  * Clone now via the method read barrier.
-                 *
-                 * But first, assert that our caller is not trying to preserve
-                 * the joined function object value as the getter object for
-                 * the redefined property. The joined function object cannot
-                 * yet have leaked, so only an internal code path could attempt
-                 * such a thing. Any such path would be a bug to fix.
                  */
-                JS_ASSERT(existingShape->getter() != getter);
-
                 if (!obj->methodReadBarrier(cx, *existingShape, &valueCopy))
                     return NULL;
             }
         } else {
             adding = true;
         }
 
-        uint32 oldShape = obj->shape();
         shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
                                  attrs, flags, shortid);
         if (!shape)
             return NULL;
-
-        /*
-         * If shape is a joined method, the above call to putProperty suffices
-         * to update the object's shape id if need be (because the shape's hash
-         * identity includes the method value).
-         *
-         * But if scope->branded(), the object's shape id may not have changed
-         * and we may be overwriting a cached function-valued property (note
-         * how methodWriteBarrier checks previous vs. would-be current value).
-         * See bug 560998.
-         */
-        if (obj->shape() == oldShape && obj->branded() && shape->slot != SHAPE_INVALID_SLOT) {
-            DebugOnly<const Shape*> newshape =
-                obj->methodWriteBarrier(cx, *shape, valueCopy);
-            JS_ASSERT(newshape == shape);
-        }
     }
 
     /* Store valueCopy before calling addProperty, in case the latter GC's. */
-    if (obj->containsSlot(shape->slot))
-        obj->nativeSetSlot(shape->slot, valueCopy);
+    if (shape->hasSlot() && obj->containsSlot(shape->slot()))
+        obj->nativeSetSlot(shape->slot(), valueCopy);
 
     /* XXXbe called with lock held */
     if (!CallAddPropertyHook(cx, clasp, obj, shape, &valueCopy)) {
         obj->removeProperty(cx, id);
         return NULL;
     }
 
-    if (defineHow & DNP_CACHE_RESULT) {
+    if (defineHow & DNP_CACHE_RESULT)
         JS_ASSERT_NOT_ON_TRACE(cx);
-        if (adding) {
-            JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape, true);
-            TRACE_1(AddProperty, obj);
-        }
-    }
     return shape;
-
-#ifdef JS_TRACER
-  error:
-    /* TRACE_1 jumps here on error. */
-    return NULL;
-#endif
 }
 
 } /* namespace js */
 
 /*
  * Call obj's resolve hook.
  *
  * cx, start, id, and flags are the parameters initially passed to the ongoing
@@ -5701,56 +5658,51 @@ js_FindIdentifierBase(JSContext *cx, JSO
 }
 
 static JS_ALWAYS_INLINE JSBool
 js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *pobj,
                    const Shape *shape, uintN getHow, Value *vp)
 {
     LeaveTraceIfGlobalObject(cx, pobj);
 
-    uint32 slot;
     int32 sample;
 
     JS_ASSERT(pobj->isNative());
 
-    slot = shape->slot;
-    if (slot != SHAPE_INVALID_SLOT) {
-        *vp = pobj->nativeGetSlot(slot);
+    if (shape->hasSlot()) {
+        *vp = pobj->nativeGetSlot(shape->slot());
         JS_ASSERT(!vp->isMagic());
         JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetterOrIsMethod(),
-                     js::types::TypeHasProperty(cx, pobj->type(), shape->propid, *vp));
+                     js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp));
     } else {
         vp->setUndefined();
     }
     if (shape->hasDefaultGetter())
         return true;
 
-    if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
-        JS_ASSERT(shape->methodObject() == vp->toObject());
+    if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER))
         return true;
-    }
 
     jsbytecode *pc;
     JSScript *script = cx->stack.currentScript(&pc);
     if (script && script->hasAnalysis() && !cx->fp()->hasImacropc()) {
         analyze::Bytecode *code = script->analysis()->maybeCode(pc);
         if (code)
             code->accessGetter = true;
     }
 
     sample = cx->runtime->propertyRemovals;
     if (!shape->get(cx, receiver, obj, pobj, vp))
         return false;
 
-    if (pobj->containsSlot(slot) &&
-        (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
-         pobj->nativeContains(cx, *shape))) {
-        if (!pobj->methodWriteBarrier(cx, *shape, *vp))
-            return false;
-        pobj->nativeSetSlot(slot, *vp);
+    /* Update slotful shapes according to the value produced by the getter. */
+    if (shape->hasSlot() && pobj->nativeContains(cx, *shape)) {
+        /* Method shapes were removed by methodReadBarrier under shape->get(). */
+        JS_ASSERT(!shape->isMethod());
+        pobj->nativeSetSlot(shape->slot(), *vp);
     }
 
     return true;
 }
 
 JSBool
 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow,
              Value *vp)
@@ -5758,66 +5710,60 @@ js_NativeGet(JSContext *cx, JSObject *ob
     return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
 }
 
 JSBool
 js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool strict, Value *vp)
 {
     LeaveTraceIfGlobalObject(cx, obj);
 
-    AddTypePropertyId(cx, obj, shape->propid, *vp);
-
-    uint32 slot;
-    int32 sample;
+    AddTypePropertyId(cx, obj, shape->propid(), *vp);
 
     JS_ASSERT(obj->isNative());
 
-    slot = shape->slot;
-    if (slot != SHAPE_INVALID_SLOT) {
+    if (shape->hasSlot()) {
+        uint32 slot = shape->slot();
         JS_ASSERT(obj->containsSlot(slot));
 
-        /* If shape has a stub setter, keep obj locked and just store *vp. */
+        /* If shape has a stub setter, just store *vp. */
         if (shape->hasDefaultSetter()) {
             if (!added) {
                 AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot);
 
-                /* FIXME: This should pass *shape, not slot, but see bug 630354. */
-                if (!obj->methodWriteBarrier(cx, slot, *vp))
+                if (shape->isMethod() && !obj->methodShapeChange(cx, *shape))
                     return false;
             }
             obj->nativeSetSlot(slot, *vp);
             return true;
         }
     } else {
         /*
          * Allow API consumers to create shared properties with stub setters.
          * Such properties effectively function as data descriptors which are
          * not writable, so attempting to set such a property should do nothing
          * or throw if we're in strict mode.
          */
         if (!shape->hasGetterValue() && shape->hasDefaultSetter())
             return js_ReportGetterOnlyAssignment(cx);
     }
 
-    sample = cx->runtime->propertyRemovals;
+    int32 sample = cx->runtime->propertyRemovals;
     if (!shape->set(cx, obj, strict, vp))
         return false;
-    
-    JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot);
-    slot = shape->slot;
-
-    if (obj->containsSlot(slot) &&
+
+    /*
+     * Update any slot for the shape with the value produced by the setter,
+     * unless the setter deleted the shape.
+     */
+    if (shape->hasSlot() &&
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
          obj->nativeContains(cx, *shape))) {
-        if (!added) {
-            AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot);
-            if (!obj->methodWriteBarrier(cx, *shape, *vp))
-                return false;
-        }
-        obj->setSlot(slot, *vp);
+        if (!added)
+            AbortRecordingIfUnexpectedGlobalWrite(cx, obj, shape->slot());
+        obj->setSlot(shape->slot(), *vp);
     }
 
     return true;
 }
 
 static JS_ALWAYS_INLINE JSBool
 js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
                            uint32 getHow, Value *vp)
@@ -6190,53 +6136,49 @@ js_SetPropertyHelper(JSContext *cx, JSOb
              * that the property's getter and setter receive the shortid, not
              * id, when they are called on the shadowing property that we are
              * about to create in obj.
              */
             if (!shape->hasSlot()) {
                 defineHow &= ~DNP_SET_METHOD;
                 if (shape->hasShortID()) {
                     flags = Shape::HAS_SHORTID;
-                    shortid = shape->shortid;
+                    shortid = shape->shortid();
                 }
                 attrs &= ~JSPROP_SHARED;
                 getter = shape->getter();
                 setter = shape->setter();
             } else {
                 /* Restore attrs to the ECMA default for new properties. */
                 attrs = JSPROP_ENUMERATE;
             }
 
             /*
              * Forget we found the proto-property now that we've copied any
              * needed member values.
              */
             shape = NULL;
         }
 
-        JS_ASSERT_IF(shape && shape->isMethod(), pobj->hasMethodBarrier());
-        JS_ASSERT_IF(shape && shape->isMethod(),
-                     pobj->getSlot(shape->slot).toObject() == shape->methodObject());
         if (shape && (defineHow & DNP_SET_METHOD)) {
             /*
              * JSOP_SETMETHOD is assigning to an existing own property. If it
              * is an identical method property, do nothing. Otherwise downgrade
              * to ordinary assignment. Either way, do not fill the property
              * cache, as the interpreter has no fast path for these unusual
              * cases.
              */
-            bool identical = shape->isMethod() && shape->methodObject() == vp->toObject();
-            if (!identical) {
-                shape = obj->methodShapeChange(cx, *shape);
-                if (!shape)
-                    return false;
-                if (!CloneFunctionForSetMethod(cx, vp))
-                    return false;
-            }
-            return identical || js_NativeSet(cx, obj, shape, false, strict, vp);
+            if (shape->isMethod() && obj->nativeGetMethod(shape) == &vp->toObject())
+                return true;
+            shape = obj->methodShapeChange(cx, *shape);
+            if (!shape)
+                return false;
+            if (!CloneFunctionForSetMethod(cx, vp))
+                return false;
+            return js_NativeSet(cx, obj, shape, false, strict, vp);
         }
     }
 
     added = false;
     if (!shape) {
         if (!obj->isExtensible()) {
             /* Error in strict mode code, warn with strict option, otherwise do nothing. */
             if (strict)
@@ -6245,64 +6187,63 @@ js_SetPropertyHelper(JSContext *cx, JSOb
                 return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
             return JS_TRUE;
         }
 
         /*
          * Purge the property cache of now-shadowed id in obj's scope chain.
          * Do this early, before locking obj to avoid nesting locks.
          */
-        js_PurgeScopeChain(cx, obj, id);
+        if (!js_PurgeScopeChain(cx, obj, id))
+            return JS_FALSE;
 
         /* Find or make a property descriptor with the right heritage. */
         if (!obj->ensureClassReservedSlots(cx))
             return JS_FALSE;
 
         /*
          * Check for Object class here to avoid defining a method on a class
          * with magic resolve, addProperty, getProperty, etc. hooks.
          */
         if ((defineHow & DNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
             JS_ASSERT(IsFunctionObject(*vp));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
             JSObject *funobj = &vp->toObject();
             JSFunction *fun = funobj->getFunctionPrivate();
-            if (fun == funobj) {
+            if (fun == funobj)
                 flags |= Shape::METHOD;
-                getter = CastAsPropertyOp(funobj);
-            }
         }
 
         shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
                                  attrs, flags, shortid);
         if (!shape)
             return JS_FALSE;
 
         if (defineHow & DNP_CACHE_RESULT)
             TRACE_1(AddProperty, obj);
 
         /*
          * Initialize the new property value (passed to setter) to undefined.
          * Note that we store before calling addProperty, to match the order
          * in DefineNativeProperty.
          */
-        if (obj->containsSlot(shape->slot))
-            obj->nativeSetSlot(shape->slot, UndefinedValue());
+        if (obj->containsSlot(shape->slot()))
+            obj->nativeSetSlot(shape->slot(), UndefinedValue());
 
         /* XXXbe called with obj locked */
         if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) {
             obj->removeProperty(cx, id);
             return JS_FALSE;
         }
         added = true;
     }
 
-    if (defineHow & DNP_CACHE_RESULT)
-        JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape, added);
+    if ((defineHow & DNP_CACHE_RESULT) && !added)
+        JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape);
 
     return js_NativeSet(cx, obj, shape, added, strict, vp);
 
 #ifdef JS_TRACER
   error: // TRACE_1 jumps here in case of error.
     return JS_FALSE;
 #endif
 }
@@ -6412,56 +6353,53 @@ js_DeleteProperty(JSContext *cx, JSObjec
     shape = (Shape *)prop;
     if (!shape->configurable()) {
         if (strict)
             return obj->reportNotConfigurable(cx, id);
         rval->setBoolean(false);
         return true;
     }
 
-    if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, SHAPE_USERID(shape), rval))
+    if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, shape->getUserId(), rval))
         return false;
     if (rval->isFalse())
         return true;
 
-    if (obj->containsSlot(shape->slot)) {
-        const Value &v = obj->nativeGetSlot(shape->slot);
+    if (shape->hasSlot() && obj->containsSlot(shape->slot())) {
+        const Value &v = obj->nativeGetSlot(shape->slot());
         GCPoke(cx, v);
 
         /*
          * Delete is rare enough that we can take the hit of checking for an
          * active cloned method function object that must be homed to a callee
          * slot on the active stack frame before this delete completes, in case
          * someone saved the clone and checks it against foo.caller for a foo
          * called from the active method.
          *
          * We do not check suspended frames. They can't be reached via caller,
          * so the only way they could have the method's joined function object
          * as callee is through an API abusage. We break any such edge case.
          */
-        if (obj->hasMethodBarrier()) {
-            JSObject *funobj;
-
-            if (IsFunctionObject(v, &funobj)) {
-                JSFunction *fun = funobj->getFunctionPrivate();
-
-                if (fun != funobj) {
-                    for (StackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
-                        if (fp->isFunctionFrame() &&
-                            fp->callee() == fun->compiledFunObj() &&
-                            fp->thisValue().isObject())
-                        {
-                            JSObject *tmp = &fp->thisValue().toObject();
-                            do {
-                                if (tmp == obj) {
-                                    fp->overwriteCallee(*funobj);
-                                    break;
-                                }
-                            } while ((tmp = tmp->getProto()) != NULL);
-                        }
+        JSObject *funobj;
+        if (IsFunctionObject(v, &funobj)) {
+            JSFunction *fun = funobj->getFunctionPrivate();
+
+            if (fun != funobj) {
+                for (StackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
+                    if (fp->isFunctionFrame() &&
+                        fp->callee() == fun->compiledFunObj() &&
+                        fp->thisValue().isObject())
+                    {
+                        JSObject *tmp = &fp->thisValue().toObject();
+                        do {
+                            if (tmp == obj) {
+                                fp->overwriteCallee(*funobj);
+                                break;
+                            }
+                        } while ((tmp = tmp->getProto()) != NULL);
                     }
                 }
             }
         }
     }
 
     return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
 }
@@ -6476,18 +6414,18 @@ js_DeleteElement(JSContext *cx, JSObject
 }
 
 namespace js {
 
 bool
 HasDataProperty(JSContext *cx, JSObject *obj, jsid methodid, Value *vp)
 {
     if (const Shape *shape = obj->nativeLookup(cx, methodid)) {
-        if (shape->hasDefaultGetterOrIsMethod() && obj->containsSlot(shape->slot)) {
-            *vp = obj->nativeGetSlot(shape->slot);
+        if (shape->hasDefaultGetterOrIsMethod() && obj->containsSlot(shape->slot())) {
+            *vp = obj->nativeGetSlot(shape->slot());
             return true;
         }
     }
 
     return false;
 }
 
 /*
@@ -6649,18 +6587,18 @@ CheckAccess(JSContext *cx, JSObject *obj
                 *attrsp = 0;
             }
             break;
         }
 
         shape = (Shape *)prop;
         *attrsp = shape->attributes();
         if (!writing) {
-            if (pobj->containsSlot(shape->slot))
-                *vp = pobj->nativeGetSlot(shape->slot);
+            if (shape->hasSlot())
+                *vp = pobj->nativeGetSlot(shape->slot());
             else
                 vp->setUndefined();
         }
     }
 
     /*
      * If obj's class has a stub (null) checkAccess hook, use the per-runtime
      * checkObjectAccess callback, if configured.
@@ -6927,19 +6865,19 @@ js_PrintObjectSlotName(JSTracer *trc, ch
     JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
 
     JSObject *obj = (JSObject *)trc->debugPrintArg;
     uint32 slot = (uint32)trc->debugPrintIndex;
 
     const Shape *shape;
     if (obj->isNative()) {
         shape = obj->lastProperty();
-        while (shape->previous() && shape->slot != slot)
+        while (shape->previous() && shape->slot() != slot)
             shape = shape->previous();
-        if (shape->slot != slot)
+        if (shape->slot() != slot)
             shape = NULL;
     } else {
         shape = NULL;
     }
 
     if (!shape) {
         const char *slotname = NULL;
         if (obj->isGlobal()) {
@@ -6949,17 +6887,17 @@ js_PrintObjectSlotName(JSTracer *trc, ch
 #undef JS_PROTO
         }
       found:
         if (slotname)
             JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
         else
             JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
     } else {
-        jsid propid = shape->propid;
+        jsid propid = shape->propid();
         if (JSID_IS_INT(propid)) {
             JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
         } else if (JSID_IS_ATOM(propid)) {
             PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
         } else {
             JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
         }
     }
@@ -6977,28 +6915,28 @@ LastConfigurableShape(JSObject *obj)
     return NULL;
 }
 
 bool
 js_ClearNative(JSContext *cx, JSObject *obj)
 {
     /* Remove all configurable properties from obj. */
     while (const Shape *shape = LastConfigurableShape(obj)) {
-        if (!obj->removeProperty(cx, shape->propid))
+        if (!obj->removeProperty(cx, shape->propid()))
             return false;
     }
 
     /* Set all remaining writable plain data properties to undefined. */
     for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
         const Shape *shape = &r.front();
         if (shape->isDataDescriptor() &&
             shape->writable() &&
             shape->hasDefaultSetter() &&
-            obj->containsSlot(shape->slot)) {
-            obj->setSlot(shape->slot, UndefinedValue());
+            obj->containsSlot(shape->slot())) {
+            obj->setSlot(shape->slot(), UndefinedValue());
         }
     }
     return true;
 }
 
 bool
 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp)
 {
@@ -7201,25 +7139,25 @@ js_DumpId(jsid id)
     fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
     dumpValue(IdToValue(id));
     fputc('\n', stderr);
 }
 
 static void
 DumpProperty(JSObject *obj, const Shape &shape)
 {
-    jsid id = shape.propid;
+    jsid id = shape.propid();
     uint8 attrs = shape.attributes();
 
     fprintf(stderr, "    ((Shape *) %p) ", (void *) &shape);
     if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
     if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
     if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
     if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
-    if (shape.isMethod()) fprintf(stderr, "method=%p ", (void *) &shape.methodObject());
+    if (shape.isMethod()) fprintf(stderr, "method ");
 
     if (shape.hasGetterValue())
         fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
     else if (!shape.hasDefaultGetter())
         fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp()));
 
     if (shape.hasSetterValue())
         fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject());
@@ -7227,21 +7165,23 @@ DumpProperty(JSObject *obj, const Shape 
         fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp()));
 
     if (JSID_IS_ATOM(id))
         dumpString(JSID_TO_STRING(id));
     else if (JSID_IS_INT(id))
         fprintf(stderr, "%d", (int) JSID_TO_INT(id));
     else
         fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
-    fprintf(stderr, ": slot %d", shape.slot);
-    if (obj->containsSlot(shape.slot)) {
+
+    uint32 slot = (shape.hasSlot() && !shape.hasMissingSlot()) ? shape.slot() : SHAPE_INVALID_SLOT;
+    fprintf(stderr, ": slot %d", slot);
+    if (obj->containsSlot(slot)) {
         fprintf(stderr, " = ");
-        dumpValue(obj->getSlot(shape.slot));
-    } else if (shape.slot != SHAPE_INVALID_SLOT) {
+        dumpValue(obj->getSlot(slot));
+    } else if (slot != SHAPE_INVALID_SLOT) {
         fprintf(stderr, " (INVALID!)");
     }
     fprintf(stderr, "\n");
 }
 
 JS_FRIEND_API(void)
 js_DumpObject(JSObject *obj)
 {
@@ -7249,21 +7189,18 @@ js_DumpObject(JSObject *obj)
     Class *clasp = obj->getClass();
     fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
 
     fprintf(stderr, "flags:");
     uint32 flags = obj->flags;
     if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate");
     if (flags & JSObject::SYSTEM) fprintf(stderr, " system");
     if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not_extensible");
-    if (flags & JSObject::BRANDED) fprintf(stderr, " branded");
     if (flags & JSObject::GENERIC) fprintf(stderr, " generic");
-    if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier");
     if (flags & JSObject::INDEXED) fprintf(stderr, " indexed");
-    if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape");
     if (flags & JSObject::HAS_EQUALITY) fprintf(stderr, " has_equality");
 
     bool anyFlags = flags != 0;
     if (obj->isNative()) {
         if (obj->inDictionaryMode()) {
             fprintf(stderr, " inDictionaryMode");
             anyFlags = true;
         }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -246,21 +246,16 @@ struct PropDesc {
     inline bool checkGetter(JSContext *cx);
     inline bool checkSetter(JSContext *cx);
 };
 
 typedef Vector<PropDesc, 1> PropDescArray;
 
 } /* namespace js */
 
-enum {
-    INVALID_SHAPE = 0x8fffffff,
-    SHAPELESS = 0xffffffff
-};
-
 /*
  * On success, and if id was found, return true with *objp non-null and with a
  * property of *objp stored in *propp. If successful but id was not found,
  * return true with both *objp and *propp null.
  */
 extern JS_FRIEND_API(JSBool)
 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                   JSProperty **propp);
@@ -445,16 +440,20 @@ 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;
 
+#if JS_BITS_PER_WORD == 32
+    void *padding;
+#endif
+
     /*
      * Private pointer to the last added property and methods to manipulate the
      * list it links among properties in this scope.
      */
     js::Shape           *lastProp;
 
   private:
     js::Class           *clasp;
@@ -477,51 +476,44 @@ struct JSObject : js::gc::Cell {
 
     inline bool nativeContains(JSContext *cx, jsid id);
     inline bool nativeContains(JSContext *cx, const js::Shape &shape);
 
     enum {
         DELEGATE                  =       0x01,
         SYSTEM                    =       0x02,
         NOT_EXTENSIBLE            =       0x04,
-        BRANDED                   =       0x08,
         GENERIC                   =       0x10,
-        METHOD_BARRIER            =       0x20,
         INDEXED                   =       0x40,
-        OWN_SHAPE                 =       0x80,
-        METHOD_THRASH_COUNT_MASK  =      0x300,
-        METHOD_THRASH_COUNT_SHIFT =          8,
-        METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT,
         BOUND_FUNCTION            =      0x400,
         HAS_EQUALITY              =      0x800,
         VAROBJ                    =     0x1000,
         WATCHED                   =     0x2000,
         PACKED_ARRAY              =     0x4000,
         ITERATED                  =     0x8000,
         SINGLETON_TYPE            =    0x10000,
         LAZY_TYPE                 =    0x20000,
 
         /* The top 5 bits of an object's flags are its number of fixed slots. */
         FIXED_SLOTS_SHIFT         =         27,
         FIXED_SLOTS_MASK          =       0x1f << FIXED_SLOTS_SHIFT,
 
-        UNUSED_FLAG_BITS          = 0x07FC0000
+        UNUSED_FLAG_BITS          = 0x07FC30A0
     };
 
     /*
      * Impose a sane upper bound, originally checked only for dense arrays, on
      * number of slots in an object.
      */
     enum {
         NSLOTS_BITS     = 29,
         NSLOTS_LIMIT    = JS_BIT(NSLOTS_BITS)
     };
 
     uint32      flags;                      /* flags */
-    uint32      objShape;                   /* copy of lastProp->shape, or override if different */
 
     union {
         /* If prototype, type of values using this as their prototype. */
         js::types::TypeObject *newType;
 
         /* If dense array, the initialized length (see jsarray.cpp). */
         jsuword initializedLength;
     };
@@ -562,181 +554,84 @@ struct JSObject : js::gc::Cell {
 
     const js::ObjectOps *getOps() const {
         return &getClass()->ops;
     }
 
     inline void trace(JSTracer *trc);
     inline void scanSlots(js::GCMarker *gcmarker);
 
-    uint32 shape() const {
-        JS_ASSERT(objShape != INVALID_SHAPE);
-        return objShape;
-    }
-
     bool isDelegate() const     { return !!(flags & DELEGATE); }
     void setDelegate()          { flags |= DELEGATE; }
     void clearDelegate()        { flags &= ~DELEGATE; }
 
     bool isBoundFunction() const { return !!(flags & BOUND_FUNCTION); }
 
     static void setDelegateNullSafe(JSObject *obj) {
         if (obj)
             obj->setDelegate();
     }
 
     bool isSystem() const       { return !!(flags & SYSTEM); }
     void setSystem()            { flags |= SYSTEM; }
 
-    /*
-     * A branded object contains plain old methods (function-valued properties
-     * without magic getters and setters), and its shape evolves whenever a
-     * function value changes.
-     */
-    bool branded()              { return !!(flags & BRANDED); }
-
-    /*
-     * NB: these return false on shape overflow but do not report any error.
-     * Callers who depend on shape guarantees should therefore bail off trace,
-     * e.g., on false returns.
-     */
-    bool brand(JSContext *cx);
-    bool unbrand(JSContext *cx);
-
     bool generic()              { return !!(flags & GENERIC); }
     void setGeneric()           { flags |= GENERIC; }
 
-    uintN getMethodThrashCount() const {
-        return (flags & METHOD_THRASH_COUNT_MASK) >> METHOD_THRASH_COUNT_SHIFT;
-    }
-
-    void setMethodThrashCount(uintN count) {
-        JS_ASSERT(count <= METHOD_THRASH_COUNT_MAX);
-        flags = (flags & ~METHOD_THRASH_COUNT_MASK) | (count << METHOD_THRASH_COUNT_SHIFT);
-    }
-
     bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); }
     void assertSpecialEqualitySynced() const {
         JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality());
     }
 
     /* Sets an object's HAS_EQUALITY flag based on its clasp. */
     inline void syncSpecialEquality();
 
     bool watched() const { return !!(flags & WATCHED); }
 
-    void setWatched(JSContext *cx) {
+    bool setWatched(JSContext *cx) {
         if (!watched()) {
             flags |= WATCHED;
-            generateOwnShape(cx);
+            return generateOwnShape(cx);
         }
+        return true;
     }
 
    /* See StackFrame::varObj. */
    inline bool isVarObj() const { return flags & VAROBJ; }
    inline void makeVarObj() { flags |= VAROBJ; }
   private:
-    void generateOwnShape(JSContext *cx);
-
-    inline void setOwnShape(uint32 s);
-    inline void clearOwnShape();
+    bool generateOwnShape(JSContext *cx, js::Shape *newShape = NULL);
 
   public:
     inline bool nativeEmpty() const;
-
-    bool hasOwnShape() const    { return !!(flags & OWN_SHAPE); }
-
     inline void setMap(js::Shape *amap);
 
-    inline void setSharedNonNativeMap();
-
     /* Functions for setting up scope chain object maps and shapes. */
-    void initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent);
-    void initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *priv);
+    bool initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent);
+    bool initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *priv);
     void setBlockOwnShape(JSContext *cx);
 
-    void deletingShapeChange(JSContext *cx, const js::Shape &shape);
     const js::Shape *methodShapeChange(JSContext *cx, const js::Shape &shape);
-    bool methodShapeChange(JSContext *cx, uint32 slot);
-    void protoShapeChange(JSContext *cx);
-    void shadowingShapeChange(JSContext *cx, const js::Shape &shape);
-    bool globalObjectOwnShapeChange(JSContext *cx);
-
-    void extensibleShapeChange(JSContext *cx) {
-        /* This will do for now. */
-        generateOwnShape(cx);
-    }
+    bool protoShapeChange(JSContext *cx);
+    bool shadowingShapeChange(JSContext *cx, const js::Shape &shape);
 
-    /*
-     * A scope has a method barrier when some compiler-created "null closure"
-     * function objects (functions that do not use lexical bindings above their
-     * scope, only free variable names) that have a correct JSSLOT_PARENT value
-     * thanks to the COMPILE_N_GO optimization are stored as newly added direct
-     * property values of the scope's object.
-     *
-     * The de-facto standard JS language requires each evaluation of such a
-     * closure to result in a unique (according to === and observable effects)
-     * function object. ES3 tried to allow implementations to "join" such
-     * objects to a single compiler-created object, but this makes an overt
-     * mutation hazard, also an "identity hazard" against interoperation among
-     * implementations that join and do not join.
-     *
-     * To stay compatible with the de-facto standard, we store the compiler-
-     * created function object as the method value and set the METHOD_BARRIER
-     * flag.
-     *
-     * The method value is part of the method property tree node's identity, so
-     * it effectively  brands the scope with a predictable shape corresponding
-     * to the method value, but without the overhead of setting the BRANDED
-     * flag, which requires assigning a new shape peculiar to each branded
-     * scope. Instead the shape is shared via the property tree among all the
-     * scopes referencing the method property tree node.
-     *
-     * Then when reading from a scope for which scope->hasMethodBarrier() is
-     * true, we count on the scope's qualified/guarded shape being unique and
-     * add a read barrier that clones the compiler-created function object on
-     * demand, reshaping the scope.
-     *
-     * This read barrier is bypassed when evaluating the callee sub-expression
-     * of a call expression (see the JOF_CALLOP opcodes in jsopcode.tbl), since
-     * such ops do not present an identity or mutation hazard. The compiler
-     * performs this optimization only for null closures that do not use their
-     * own name or equivalent built-in references (arguments.callee).
-     *
-     * The BRANDED write barrier, JSObject::methodWriteBarrer, must check for
-     * METHOD_BARRIER too, and regenerate this scope's shape if the method's
-     * value is in fact changing.
-     */
-    bool hasMethodBarrier()     { return !!(flags & METHOD_BARRIER); }
-    void setMethodBarrier()     { flags |= METHOD_BARRIER; }
-
-    /*
-     * Test whether this object may be branded due to method calls, which means
-     * any assignment to a function-valued property must regenerate shape; else
-     * test whether this object has method properties, which require a method
-     * write barrier.
-     */
-    bool brandedOrHasMethodBarrier() { return !!(flags & (BRANDED | METHOD_BARRIER)); }
+    bool extensibleShapeChange(JSContext *cx) {
+        /* This will do for now. */
+        return generateOwnShape(cx);
+    }
 
     /*
      * Read barrier to clone a joined function object stored as a method.
      * Defined in jsobjinlines.h, but not declared inline per standard style in
      * order to avoid gcc warnings.
      */
     const js::Shape *methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp);
 
-    /*
-     * Write barrier to check for a change of method value. Defined inline in
-     * jsobjinlines.h after methodReadBarrier. The slot flavor is required by
-     * JSOP_*GVAR, which deals in slots not shapes, while not deoptimizing to
-     * map slot to shape unless JSObject::flags show that this is necessary.
-     * The methodShapeChange overload (above) parallels this.
-     */
-    const js::Shape *methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v);
-    bool methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v);
+    /* Whether method shapes can be added to this object. */
+    inline bool canHaveMethodBarrier() const;
 
     bool isIndexed() const          { return !!(flags & INDEXED); }
     void setIndexed()               { flags |= INDEXED; }
 
     /*
      * Return true if this object is a native one that has been converted from
      * shared-immutable prototype-rooted shape storage to dictionary-shapes in
      * a doubly-linked list.
@@ -868,16 +763,17 @@ struct JSObject : js::gc::Cell {
         JS_ASSERT(slot < capacity);
         size_t fixed = numFixedSlots();
         if (slot < fixed)
             return fixedSlots()[slot];
         return slots[slot - fixed];
     }
 
     inline const js::Value &nativeGetSlot(uintN slot) const;
+    inline JSFunction *nativeGetMethod(const js::Shape *shape) const;
 
     void setSlot(uintN slot, const js::Value &value) {
         JS_ASSERT(slot < capacity);
         getSlotRef(slot) = value;
     }
 
     inline void nativeSetSlot(uintN slot, const js::Value &value);
     inline void nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value);
@@ -900,17 +796,16 @@ struct JSObject : js::gc::Cell {
     }
 
     void setFixedSlot(uintN slot, const js::Value &value) {
         JS_ASSERT(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);
 
     /*
      * Whether this is the only object which has its specified type. This
      * object will have its type constructed lazily as needed by analysis.
@@ -1333,26 +1228,24 @@ struct JSObject : js::gc::Cell {
                                       js::types::TypeObject *type,
                                       JSObject *parent,
                                       void *priv,
                                       js::gc::AllocKind kind);
 
     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.
+     * Allocate and free an object slot.
      *
      * FIXME: bug 593129 -- slot allocation should be done by object methods
      * after calling object-parameter-free shape methods, avoiding coupling
      * logic across the object vs. shape module wall.
      */
     bool allocSlot(JSContext *cx, uint32 *slotp);
-    bool freeSlot(JSContext *cx, uint32 slot);
+    void freeSlot(JSContext *cx, uint32 slot);
 
   public:
     bool reportReadOnly(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR);
     bool reportNotConfigurable(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR);
     bool reportNotExtensible(JSContext *cx, uintN report = JSREPORT_ERROR);
 
     /*
      * Get the property with the given id, then call it as a function with the
@@ -1371,17 +1264,17 @@ struct JSObject : js::gc::Cell {
      * Notes:
      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
      * 2. !isExtensible() checking must be done by callers.
      */
     const js::Shape *addPropertyInternal(JSContext *cx, jsid id,
                                          JSPropertyOp getter, JSStrictPropertyOp setter,
                                          uint32 slot, uintN attrs,
                                          uintN flags, intN shortid,
-                                         js::Shape **spp);
+                                         js::Shape **spp, bool allowDictionary);
 
     bool toDictionaryMode(JSContext *cx);
 
     struct TradeGutsReserved;
     static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                                     TradeGutsReserved &reserved);
 
     static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b,
@@ -1389,17 +1282,17 @@ struct JSObject : js::gc::Cell {
 
     void updateFixedSlots(uintN fixed);
 
   public:
     /* Add a property whose id is not yet in this scope. */
     const js::Shape *addProperty(JSContext *cx, jsid id,
                                  JSPropertyOp getter, JSStrictPropertyOp setter,
                                  uint32 slot, uintN attrs,
-                                 uintN flags, intN shortid);
+                                 uintN flags, intN shortid, bool allowDictionary = true);
 
     /* Add a data property whose id is not yet in this scope. */
     const js::Shape *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) {
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0);
     }
 
     /* Add or overwrite a property for id in this scope. */
@@ -1520,18 +1413,16 @@ struct JSObject : js::gc::Cell {
     inline JSObject *getThrowTypeError() const;
 
     JS_FRIEND_API(JSObject *) clone(JSContext *cx, JSObject *proto, JSObject *parent);
     JS_FRIEND_API(bool) copyPropertiesFrom(JSContext *cx, JSObject *obj);
     bool swap(JSContext *cx, JSObject *other);
 
     const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index);
 
-    inline bool canHaveMethodBarrier() const;
-
     inline bool isArguments() const { return isNormalArguments() || isStrictArguments(); }
     inline bool isArrayBuffer() const { return clasp == &js::ArrayBufferClass; }
     inline bool isNormalArguments() const { return clasp == &js::NormalArgumentsObjectClass; }
     inline bool isStrictArguments() const { return clasp == &js::StrictArgumentsObjectClass; }
     inline bool isArray() const { return isSlowArray() || isDenseArray(); }
     inline bool isDenseArray() const { return clasp == &js::ArrayClass; }
     inline bool isSlowArray() const { return clasp == &js::SlowArrayClass; }
     inline bool isNumber() const { return clasp == &js::NumberClass; }
@@ -1869,24 +1760,25 @@ extern jsid
 js_CheckForStringIndex(jsid id);
 
 /*
  * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent
  * scope, else it reshapes the scope and prototype chains it links. It calls
  * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
  * (i.e., obj has ever been on a prototype or parent chain).
  */
-extern void
+extern bool
 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id);
 
-inline void
+inline bool
 js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
 {
     if (obj->isDelegate())
-        js_PurgeScopeChainHelper(cx, obj, id);
+        return js_PurgeScopeChainHelper(cx, obj, id);
+    return true;
 }
 
 /*
  * Find or create a property named by id in obj's scope, with the given getter
  * and setter, slot, attributes, and other members.
  */
 extern const js::Shape *
 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -86,51 +86,23 @@ JSObject::preventExtensions(JSContext *c
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
             return false;
         }
     } else {
         if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
             return false;
     }
 
-    if (isNative())
-        extensibleShapeChange(cx);
+    if (isNative() && !extensibleShapeChange(cx))
+        return false;
 
     flags |= NOT_EXTENSIBLE;
     return true;
 }
 
-inline bool
-JSObject::brand(JSContext *cx)
-{
-    JS_ASSERT(!generic());
-    JS_ASSERT(!branded());
-    JS_ASSERT(isNative());
-    JS_ASSERT(!cx->typeInferenceEnabled());
-    generateOwnShape(cx);
-    if (js_IsPropertyCacheDisabled(cx))  // check for rt->shapeGen overflow
-        return false;
-    flags |= BRANDED;
-    return true;
-}
-
-inline bool
-JSObject::unbrand(JSContext *cx)
-{
-    JS_ASSERT(isNative());
-    if (branded()) {
-        generateOwnShape(cx);
-        if (js_IsPropertyCacheDisabled(cx))  // check for rt->shapeGen overflow
-            return false;
-        flags &= ~BRANDED;
-    }
-    setGeneric();
-    return true;
-}
-
 inline JSBool
 JSObject::setAttributes(JSContext *cx, jsid id, uintN *attrsp)
 {
     js::types::MarkTypePropertyConfigured(cx, this, id);
     js::AttributesOp op = getOps()->setAttributes;
     return (op ? op : js_SetAttributes)(cx, this, id, attrsp);
 }
 
@@ -201,79 +173,60 @@ JSObject::finalize(JSContext *cx)
 
     finish(cx);
 }
 
 /* 
  * Initializer for Call objects for functions and eval frames. Set class,
  * parent, map, and shape, and allocate slots.
  */
-inline void
+inline bool
 JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent)
 {
     init(cx, &js::CallClass, &js::types::emptyTypeObject, parent, NULL, false);
-    lastProp = bindings.lastShape();
+    setMap(bindings.lastShape());
 
     /*
      * If |bindings| is for a function that has extensible parents, that means
-     * its Call should have its own shape; see js::Bindings::extensibleParents.
+     * its Call should have its own shape; see js::BaseShape::extensibleParents.
      */
-    if (bindings.extensibleParents())
-        setOwnShape(js_GenerateShape(cx));
-    else
-        objShape = lastProp->shapeid;
+    if (lastProp->extensibleParents())
+        return generateOwnShape(cx);
+    return true;
 }
 
 /*
  * Initializer for cloned block objects. Set class, prototype, frame, map, and
  * shape.
  */
-inline void
+inline bool
 JSObject::initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *frame)
 {
     init(cx, &js::BlockClass, type, NULL, frame, false);
 
     /* Cloned blocks copy their prototype's map; it had better be shareable. */
-    JS_ASSERT(!getProto()->inDictionaryMode() || getProto()->lastProp->frozen());
-    lastProp = getProto()->lastProp;
+    JS_ASSERT(!getProto()->inDictionaryMode());
+    setMap(getProto()->lastProp);
 
-    /*
-     * If the prototype has its own shape, that means the clone should, too; see
-     * js::Bindings::extensibleParents.
-     */
-    if (getProto()->hasOwnShape())
-        setOwnShape(js_GenerateShape(cx));
-    else
-        objShape = lastProp->shapeid;
-}
-
-/* 
- * Mark a compile-time block as OWN_SHAPE, indicating that its run-time clones
- * also need unique shapes. See js::Bindings::extensibleParents.
- */
-inline void
-JSObject::setBlockOwnShape(JSContext *cx) {
-    JS_ASSERT(isStaticBlock());
-    setOwnShape(js_GenerateShape(cx));
+    if (lastProp->extensibleParents())
+        return generateOwnShape(cx);
+    return true;
 }
 
 /*
  * Property read barrier for deferred cloning of compiler-created function
  * objects optimized as typically non-escaping, ad-hoc methods in obj.
  */
 inline const js::Shape *
 JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp)
 {
-    JS_ASSERT(canHaveMethodBarrier());
-    JS_ASSERT(hasMethodBarrier());
     JS_ASSERT(nativeContains(cx, shape));
     JS_ASSERT(shape.isMethod());
-    JS_ASSERT(shape.methodObject() == vp->toObject());
     JS_ASSERT(shape.writable());
-    JS_ASSERT(shape.slot != SHAPE_INVALID_SLOT);
+    JS_ASSERT(shape.hasSlot());
     JS_ASSERT(shape.hasDefaultSetter());
     JS_ASSERT(!isGlobal());  /* i.e. we are not changing the global shape */
 
     JSObject *funobj = &vp->toObject();
     JSFunction *fun = funobj->getFunctionPrivate();
     JS_ASSERT(fun == funobj);
     JS_ASSERT(fun->isNullClosure());
 
@@ -282,57 +235,31 @@ JSObject::methodReadBarrier(JSContext *c
         return NULL;
     funobj->setMethodObj(*this);
 
     /*
      * Replace the method property with an ordinary data property. This is
      * equivalent to this->setProperty(cx, shape.id, vp) except that any
      * watchpoint on the property is not triggered.
      */
-    uint32 slot = shape.slot;
+    uint32 slot = shape.slot();
     const js::Shape *newshape = methodShapeChange(cx, shape);
     if (!newshape)
         return NULL;
     JS_ASSERT(!newshape->isMethod());
-    JS_ASSERT(newshape->slot == slot);
+    JS_ASSERT(newshape->slot() == slot);
     vp->setObject(*funobj);
     nativeSetSlot(slot, *vp);
     return newshape;
 }
 
-static JS_ALWAYS_INLINE bool
-ChangesMethodValue(const js::Value &prev, const js::Value &v)
-{
-    JSObject *prevObj;
-    return prev.isObject() && (prevObj = &prev.toObject())->isFunction() &&
-           (!v.isObject() || &v.toObject() != prevObj);
-}
-
-inline const js::Shape *
-JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v)
+inline bool
+JSObject::canHaveMethodBarrier() const
 {
-    if (brandedOrHasMethodBarrier() && shape.slot != SHAPE_INVALID_SLOT) {
-        const js::Value &prev = nativeGetSlot(shape.slot);
-
-        if (ChangesMethodValue(prev, v))
-            return methodShapeChange(cx, shape);
-    }
-    return &shape;
-}
-
-inline bool
-JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v)
-{
-    if (brandedOrHasMethodBarrier()) {
-        const js::Value &prev = nativeGetSlot(slot);
-
-        if (ChangesMethodValue(prev, v))
-            return methodShapeChange(cx, slot);
-    }
-    return true;
+    return isObject() || isFunction() || isPrimitive() || isDate();
 }
 
 inline const js::Value *
 JSObject::getRawSlots()
 {
     JS_ASSERT(isGlobal());
     return slots;
 }
@@ -382,22 +309,16 @@ JSObject::getReservedSlot(uintN index) c
 
 inline void
 JSObject::setReservedSlot(uintN index, const js::Value &v)
 {
     JS_ASSERT(index < JSSLOT_FREE(getClass()));
     setSlot(index, v);
 }
 
-inline bool
-JSObject::canHaveMethodBarrier() const
-{
-    return isObject() || isFunction() || isPrimitive() || isDate();
-}
-
 inline const js::Value &
 JSObject::getPrimitiveThis() const
 {
     JS_ASSERT(isPrimitive());
     return getFixedSlot(JSSLOT_PRIMITIVE_THIS);
 }
 
 inline void
@@ -849,25 +770,16 @@ inline void
 JSObject::init(JSContext *cx, js::Class *aclasp, js::types::TypeObject *type,
                JSObject *parent, void *priv, bool denseArray)
 {
     clasp = aclasp;
     flags = capacity << FIXED_SLOTS_SHIFT;
 
     JS_ASSERT(denseArray == (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 = INVALID_SHAPE;
-#endif
-
     privateData = priv;
 
     /*
      * Fill the fixed slots with undefined if needed.  This object must
      * already have its capacity filled in, as by js_NewGCObject. If inference
      * is disabled, NewArray will backfill holes up to the array's capacity
      * and unset the PACKED_ARRAY flag.
      */
@@ -938,31 +850,35 @@ JSObject::principals(JSContext *cx)
     if (JSObjectPrincipalsFinder finder = cb ? cb->findObjectPrincipals : NULL)
         return finder(cx, this);
     return cx->compartment ? cx->compartment->principals : NULL;
 }
 
 inline uint32
 JSObject::slotSpan() const
 {
-    return lastProp->slotSpan;
+    if (inDictionaryMode()) {
+        JS_ASSERT(lastProp->base()->isOwned());
+        return lastProp->base()->slotSpan;
+    }
+    uint32 free = JSSLOT_FREE(getClass());
+    return lastProp->hasMissingSlot() ? free : JS_MAX(free, lastProp->maybeSlot() + 1);
 }
 
 inline bool
 JSObject::containsSlot(uint32 slot) const
 {
     return slot < slotSpan();
 }
 
 inline void
 JSObject::setMap(js::Shape *amap)
 {
-    JS_ASSERT(!hasOwnShape());
+    JS_ASSERT_IF(lastProp && isNative(), !inDictionaryMode());
     lastProp = amap;
-    objShape = lastProp->shapeid;
 }
 
 inline js::Value &
 JSObject::nativeGetSlotRef(uintN slot)
 {
     JS_ASSERT(isNative());
     JS_ASSERT(containsSlot(slot));
     return getSlotRef(slot);
@@ -971,57 +887,59 @@ JSObject::nativeGetSlotRef(uintN slot)
 inline const js::Value &
 JSObject::nativeGetSlot(uintN slot) const
 {
     JS_ASSERT(isNative());
     JS_ASSERT(containsSlot(slot));
     return getSlot(slot);
 }
 
+inline JSFunction *
+JSObject::nativeGetMethod(const js::Shape *shape) const
+{
+    /*
+     * For method shapes, this object must have an uncloned function object in
+     * the shape's slot.
+     */
+    JS_ASSERT(shape->isMethod());
+#ifdef DEBUG
+    JSObject *obj = &nativeGetSlot(shape->slot()).toObject();
+    JS_ASSERT(obj->isFunction() && obj->getFunctionPrivate() == obj);
+#endif
+
+    return (JSFunction *) &nativeGetSlot(shape->slot()).toObject();
+}
+
 inline void
 JSObject::nativeSetSlot(uintN slot, const js::Value &value)
 {
     JS_ASSERT(isNative());
     JS_ASSERT(containsSlot(slot));
     return setSlot(slot, value);
 }
 
 inline void
 JSObject::nativeSetSlotWithType(JSContext *cx, const js::Shape *shape, const js::Value &value)
 {
-    nativeSetSlot(shape->slot, value);
-    js::types::AddTypePropertyId(cx, this, shape->propid, value);
+    nativeSetSlot(shape->slot(), value);
+    js::types::AddTypePropertyId(cx, this, shape->propid(), value);
 }
 
 inline bool
 JSObject::isNative() const
 {
     return lastProp->isNative();
 }
 
 inline bool
 JSObject::isNewborn() const
 {
     return !lastProp;
 }
 
-inline void
-JSObject::clearOwnShape()
-{
-    flags &= ~OWN_SHAPE;
-    objShape = lastProp->shapeid;
-}
-
-inline void
-JSObject::setOwnShape(uint32 s)
-{
-    flags |= OWN_SHAPE;
-    objShape = s;
-}
-
 inline js::Shape **
 JSObject::nativeSearch(JSContext *cx, jsid id, bool adding)
 {
     return js::Shape::search(cx, &lastProp, id, adding);
 }
 
 inline const js::Shape *
 JSObject::nativeLookup(JSContext *cx, jsid id)
@@ -1034,24 +952,24 @@ inline bool
 JSObject::nativeContains(JSContext *cx, jsid id)
 {
     return nativeLookup(cx, id) != NULL;
 }
 
 inline bool
 JSObject::nativeContains(JSContext *cx, const js::Shape &shape)
 {
-    return nativeLookup(cx, shape.propid) == &shape;
+    return nativeLookup(cx, shape.propid()) == &shape;
 }
 
 inline const js::Shape *
 JSObject::lastProperty() const
 {
-    JS_ASSERT(isNative());
-    JS_ASSERT(!JSID_IS_VOID(lastProp->propid));
+    JS_ASSERT_IF(!isNative(), lastProp->isEmptyShape());
+    JS_ASSERT_IF(!lastProp->isEmptyShape(), !JSID_IS_EMPTY(lastProp->propid()));
     return lastProp;
 }
 
 inline bool
 JSObject::nativeEmpty() const
 {
     return lastProperty()->isEmptyShape();
 }
@@ -1076,38 +994,29 @@ JSObject::hasPropertyTable() const
 
 /*
  * FIXME: shape must not be null, should use a reference here and other places.
  */
 inline void
 JSObject::setLastProperty(const js::Shape *shape)
 {
     JS_ASSERT(!inDictionaryMode());
-    JS_ASSERT(!JSID_IS_VOID(shape->propid));
-    JS_ASSERT_IF(lastProp, !JSID_IS_VOID(lastProp->propid));
     JS_ASSERT(shape->compartment() == compartment());
 
     lastProp = const_cast<js::Shape *>(shape);
 }
 
 inline void
 JSObject::removeLastProperty()
 {
     JS_ASSERT(!inDictionaryMode());
-    JS_ASSERT(!JSID_IS_VOID(lastProp->parent->propid));
 
     lastProp = lastProp->parent;
 }
 
-inline void
-JSObject::setSharedNonNativeMap()
-{
-    setMap(&js::Shape::sharedNonNative);
-}
-
 inline JSBool
 JSObject::lookupElement(JSContext *cx, uint32 index, JSObject **objp, JSProperty **propp)
 {
     js::LookupElementOp op = getOps()->lookupElement;
     return (op ? op : js_LookupElement)(cx, this, index, objp, propp);
 }
 
 inline JSBool
@@ -1231,16 +1140,29 @@ InitScopeForObject(JSContext* cx, JSObje
 
   bad:
     /* The GC nulls map initially. It should still be null on error. */
     JS_ASSERT(obj->isNewborn());
     return false;
 }
 
 static inline bool
+InitNonNativeObject(JSContext *cx, JSObject *obj, js::Class *clasp)
+{
+    JS_ASSERT(!clasp->isNative());
+
+    js::EmptyShape *empty = js::BaseShape::lookupEmpty(cx, clasp);
+    if (!empty)
+        return false;
+
+    obj->setMap(empty);
+    return true;
+}
+
+static inline bool
 CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp)
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST);
     /* If the class has no finalizer or a finalizer that is safe to call on
      * a different thread, we change the finalize kind. For example,
      * FINALIZE_OBJECT0 calls the finalizer on the main thread,
      * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
@@ -1277,33 +1199,32 @@ NewNativeClassInstance(JSContext *cx, Cl
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC.
      */
 
     if (CanBeFinalizedInBackground(kind, clasp))
         kind = GetBackgroundAllocKind(kind);
 
     JSObject* obj = js_NewGCObject(cx, kind);
-
-    if (obj) {
-        /*
-         * Default parent to the parent of the prototype, which was set from
-         * the parent of the prototype's constructor.
-         */
-        bool denseArray = (clasp == &ArrayClass);
-        obj->init(cx, clasp, type, parent, NULL, denseArray);
+    if (!obj)
+        return NULL;
 
-        JS_ASSERT(type->canProvideEmptyShape(clasp));
-        js::EmptyShape *empty = type->getEmptyShape(cx, clasp, kind);
+    /*
+     * Default parent to the parent of the prototype, which was set from
+     * the parent of the prototype's constructor.
+     */
+    bool denseArray = (clasp == &ArrayClass);
+    obj->init(cx, clasp, type, parent, NULL, denseArray);
 
-        if (empty)
-            obj->setMap(empty);
-        else
-            obj = NULL;
-    }
+    JS_ASSERT(type->canProvideEmptyShape(clasp));
+    js::EmptyShape *empty = type->getEmptyShape(cx, clasp, kind);
+    if (!empty)
+        return NULL;
+
+    obj->setMap(empty);
 
     return obj;
 }
 
 static inline JSObject *
 NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
@@ -1453,23 +1374,20 @@ NewObject(JSContext *cx, js::Class *clas
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
     obj->init(cx, clasp, type,
               (!parent && proto) ? proto->getParent() : parent,
               NULL, clasp == &ArrayClass);
 
-    if (clasp->isNative()) {
-        if (!InitScopeForObject(cx, obj, clasp, type, kind)) {
-            obj = NULL;
-            goto out;
-        }
-    } else {
-        obj->setSharedNonNativeMap();
+    if (clasp->isNative()
+        ? !InitScopeForObject(cx, obj, clasp, type, kind)
+        : !InitNonNativeObject(cx, obj, clasp)) {
+        obj = NULL;
     }
 
 out:
     Probes::createObject(cx, obj);
     return obj;
 }
 } /* namespace detail */
 
@@ -1626,17 +1544,16 @@ CopyInitializerObject(JSContext *cx, JSO
     JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
 
     if (!obj || !obj->ensureSlots(cx, baseobj->numSlots()))
         return NULL;
 
     obj->setType(type);
     obj->flags = baseobj->flags;
     obj->lastProp = baseobj->lastProp;
-    obj->objShape = baseobj->objShape;
 
     return obj;
 }
 
 inline bool
 DefineConstructorAndPrototype(JSContext *cx, GlobalObject *global,
                               JSProtoKey key, JSObject *ctor, JSObject *proto)
 {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -396,22 +396,22 @@ ToDisassemblySource(JSContext *cx, jsval
             char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
             if (!source)
                 return false;
 
             Shape::Range r = obj->lastProperty()->all();
             while (!r.empty()) {
                 const Shape &shape = r.front();
                 JSAutoByteString bytes;
-                if (!js_AtomToPrintableString(cx, JSID_TO_ATOM(shape.propid), &bytes))
+                if (!js_AtomToPrintableString(cx, JSID_TO_ATOM(shape.propid()), &bytes))
                     return false;
 
                 r.popFront();
                 source = JS_sprintf_append(source, "%s: %d%s",
-                                           bytes.ptr(), shape.shortid,
+                                           bytes.ptr(), shape.shortid(),
                                            !r.empty() ? ", " : "");
                 if (!source)
                     return false;
             }
 
             source = JS_sprintf_append(source, "}");
             if (!source)
                 return false;
@@ -1472,20 +1472,20 @@ GetLocal(SprintStack *ss, jsint i)
             jsint count = OBJ_BLOCK_COUNT(cx, obj);
 
             if (jsuint(i - depth) < jsuint(count)) {
                 jsint slot = i - depth;
 
                 for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
                     const Shape &shape = r.front();
 
-                    if (shape.shortid == slot) {
-                        LOCAL_ASSERT(JSID_IS_ATOM(shape.propid));
-
-                        JSAtom *atom = JSID_TO_ATOM(shape.propid);
+                    if (shape.shortid() == slot) {
+                        LOCAL_ASSERT(JSID_IS_ATOM(shape.propid()));
+
+                        JSAtom *atom = JSID_TO_ATOM(shape.propid());
                         const char *rval = QuoteString(&ss->sprinter, atom, 0);
                         if (!rval)
                             return NULL;
 
                         RETRACT(&ss->sprinter, rval);
                         return rval;
                     }
                 }
@@ -2136,17 +2136,17 @@ Decompile(SprintStack *ss, jsbytecode *p
              * Rewrite non-get ops to their "get" format if the error is in
              * the bytecode at pc, so we don't decompile more than the error
              * expression.
              */
             StackFrame *fp = js_GetScriptedCaller(cx, NULL);
             uint32 format = cs->format;
             if (((fp && pc == fp->pcQuadratic(cx)) ||
                  (pc == startpc && nuses != 0)) &&
-                format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
+                format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_VARPROP)) {
                 uint32 mode = JOF_MODE(format);
                 if (mode == JOF_NAME) {
                     /*
                      * JOF_NAME does not imply JOF_ATOM, so we must check for
                      * the QARG and QVAR format types, and translate those to
                      * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
                      * to JSOP_NAME.
                      */
@@ -2773,18 +2773,18 @@ Decompile(SprintStack *ss, jsbytecode *p
                 MUST_FLOW_THROUGH("enterblock_out");
 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
                                                    goto enterblock_out)
                 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
                     const Shape &shape = r.front();
 
                     if (!shape.hasShortID())
                         continue;
-                    LOCAL_ASSERT_OUT(shape.shortid < argc);
-                    atomv[shape.shortid] = JSID_TO_ATOM(shape.propid);
+                    LOCAL_ASSERT_OUT(shape.shortid() < argc);
+                    atomv[shape.shortid()] = JSID_TO_ATOM(shape.propid());
                 }
                 ok = JS_TRUE;
                 for (i = 0; i < argc; i++) {
                     atom = atomv[i];
                     rval = QuoteString(&ss->sprinter, atom, 0);
                     if (!rval ||
                         !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
                         ok = JS_FALSE;
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -105,17 +105,16 @@ typedef enum JSOp {
 #define JOF_VARPROP       (5U<<5) /* x.prop for this, arg, var, or local x */
 #define JOF_MODEMASK      (7U<<5) /* mask for above addressing modes */
 #define JOF_SET           (1U<<8) /* set (i.e., assignment) operation */
 #define JOF_DEL           (1U<<9) /* delete operation */
 #define JOF_DEC          (1U<<10) /* decrement (--, not ++) opcode */
 #define JOF_INC          (2U<<10) /* increment (++, not --) opcode */
 #define JOF_INCDEC       (3U<<10) /* increment or decrement opcode */
 #define JOF_POST         (1U<<12) /* postorder increment or decrement */
-#define JOF_FOR          (1U<<13) /* for-in property op (akin to JOF_SET) */
 #define JOF_ASSIGNING     JOF_SET /* hint for Class.resolve, used for ops
                                      that do simplex assignment */
 #define JOF_DETECTING    (1U<<14) /* object detection for JSNewResolveOp */
 #define JOF_BACKPATCH    (1U<<15) /* backpatch placeholder during codegen */
 #define JOF_LEFTASSOC    (1U<<16) /* left-associative operator */
 #define JOF_DECLARING    (1U<<17) /* var, const, or function declaration op */
 #define JOF_INDEXBASE    (1U<<18) /* atom segment base setting prefix op */
 #define JOF_CALLOP       (1U<<19) /* call operation that pushes function and
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -582,15 +582,13 @@ OPDEF(JSOP_LAMBDA_FC,     224,"lambda_fc
  */
 OPDEF(JSOP_OBJTOP,        225,"objtop",        NULL,  3,  0,  0,  0,  JOF_UINT16)
 
 /*
  * Joined function object as method optimization support.
  */
 OPDEF(JSOP_SETMETHOD,     226,"setmethod",     NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INITMETHOD,    227,"initmethod",    NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
-OPDEF(JSOP_UNBRAND,       228,"unbrand",       NULL,  1,  1,  1,  0,  JOF_BYTE)
-OPDEF(JSOP_UNBRANDTHIS,   229,"unbrandthis",   NULL,  1,  0,  0,  0,  JOF_BYTE)
 
-OPDEF(JSOP_SHARPINIT,     230,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
+OPDEF(JSOP_SHARPINIT,     228,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
 
 /* Pop the stack, convert to a jsid (int or string), and push back. */
-OPDEF(JSOP_TOID,          231, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_TOID,          229, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -327,30 +327,16 @@ JSFunctionBox::inAnyDynamicScope() const
 }
 
 bool
 JSFunctionBox::scopeIsExtensible() const
 {
     return tcflags & TCF_FUN_EXTENSIBLE_SCOPE;
 }
 
-bool
-JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
-{
-    if (slowMethods != 0) {
-        for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
-            if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
-                return true;
-            if (funbox->inLoop)
-                return true;
-        }
-    }
-    return false;
-}
-
 void
 Parser::trace(JSTracer *trc)
 {
     JSObjectBox *objbox = traceListHead;
     while (objbox) {
         MarkObject(trc, *objbox->object, "parser.object");
         if (objbox->isFunctionBox)
             static_cast<JSFunctionBox *>(objbox)->bindings.trace(trc);
@@ -1159,17 +1145,17 @@ Compiler::defineGlobals(JSContext *cx, G
          * the global directly during compilation and avoid having to emit type
          * checks every time it is accessed in the script.
          */
         const Shape *shape =
             DefineNativeProperty(cx, globalObj, id, rval, JS_PropertyStub, JS_StrictPropertyStub,
                                  JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, DNP_SKIP_TYPE);
         if (!shape)
             return false;
-        def.knownSlot = shape->slot;
+        def.knownSlot = shape->slot();
     }
 
     js::Vector<JSScript *, 16> worklist(cx);
     if (!worklist.append(script))
         return false;
 
     /*
      * Recursively walk through all scripts we just compiled. For each script,
@@ -1451,17 +1437,17 @@ CheckStrictParameters(JSContext *cx, JST
 
     /* name => whether we've warned about the name already */
     HashMap<JSAtom *, bool> parameters(cx);
     if (!parameters.init(tc->bindings.countArgs()))
         return false;
 
     /* Start with lastVariable(), not lastArgument(), for destructuring. */
     for (Shape::Range r = tc->bindings.lastVariable(); !r.empty(); r.popFront()) {
-        jsid id = r.front().propid;
+        jsid id = r.front().propid();
         if (!JSID_IS_ATOM(id))
             continue;
 
         JSAtom *name = JSID_TO_ATOM(id);
 
         if (name == argumentsAtom || name == evalAtom) {
             if (!ReportBadParameter(cx, tc, name, JSMSG_BAD_BINDING))
                 return false;
@@ -1989,17 +1975,18 @@ MatchOrInsertSemicolon(JSContext *cx, To
 bool
 Parser::analyzeFunctions(JSTreeContext *tc)
 {
     cleanFunctionList(&tc->functionList);
     if (!tc->functionList)
         return true;
     if (!markFunArgs(tc->functionList))
         return false;
-    markExtensibleScopeDescendants(tc->functionList, false);
+    if (!markExtensibleScopeDescendants(tc->functionList, false))
+        return false;
     setFunctionKinds(tc->functionList, &tc->flags);
     return true;
 }
 
 /*
  * Mark as funargs any functions that reach up to one or more upvars across an
  * already-known funarg. The parser will flag the o_m lambda as a funarg in:
  *
@@ -2431,19 +2418,16 @@ ConsiderUnbranding(JSFunctionBox *funbox
         uintN methodSets = 0, slowMethodSets = 0;
 
         for (JSParseNode *method = funbox->methods; method; method = method->pn_link) {
             JS_ASSERT(method->isOp(JSOP_LAMBDA) || method->isOp(JSOP_LAMBDA_FC));
             ++methodSets;
             if (!method->pn_funbox->joinable())
                 ++slowMethodSets;
         }
-
-        if (funbox->shouldUnbrand(methodSets, slowMethodSets))
-            funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
     }
 }
 
 void
 Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
 {
     for (; funbox; funbox = funbox->siblings) {
         JSParseNode *fn = funbox->node;
@@ -2549,35 +2533,39 @@ Parser::setFunctionKinds(JSFunctionBox *
  * and functions that contain function statements (definitions not appearing
  * within the top statement list, which don't take effect unless they are
  * evaluated). Such call objects may acquire bindings that shadow variables
  * defined in enclosing scopes, so any enclosed functions must have their
  * bindings' extensibleParents flags set, and enclosed compiler-created blocks
  * must have their OWN_SHAPE flags set; the comments for
  * js::Bindings::extensibleParents explain why.
  */
-void
+bool
 Parser::markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent) 
 {
     for (; funbox; funbox = funbox->siblings) {
         /*
          * It would be nice to use fun->kind() here to recognize functions
          * that will never consult their parent chains, and thus don't need
          * their 'extensible parents' flag set. Filed as bug 619750. 
          */
 
-        JS_ASSERT(!funbox->bindings.extensibleParents());
-        if (hasExtensibleParent)
-            funbox->bindings.setExtensibleParents();
+        if (hasExtensibleParent) {
+            if (!funbox->bindings.setExtensibleParents(context))
+                return false;
+        }
 
         if (funbox->kids) {
-            markExtensibleScopeDescendants(funbox->kids,
-                                           hasExtensibleParent || funbox->scopeIsExtensible());
-        }
-    }
+            if (!markExtensibleScopeDescendants(funbox->kids,
+                                                hasExtensibleParent || funbox->scopeIsExtensible()))
+                return false;
+        }
+    }
+
+    return true;
 }
 
 const char js_argument_str[] = "argument";
 const char js_variable_str[] = "variable";
 const char js_unknown_str[]  = "unknown";
 
 const char *
 JSDefinition::kindString(Kind kind)
@@ -3575,31 +3563,31 @@ BindLet(JSContext *cx, BindData *data, J
 
     /*
      * Store pn temporarily in what would be shape-mapped slots in a cloned
      * block object (once the prototype's final population is known, after all
      * 'let' bindings for this block have been parsed). We free these slots in
      * jsemit.cpp:EmitEnterBlock so they don't tie up unused space in the so-
      * called "static" prototype Block.
      */
-    blockObj->setSlot(shape->slot, PrivateValue(pn));
+    blockObj->setSlot(shape->slot(), PrivateValue(pn));
     return true;
 }
 
 static void
 PopStatement(JSTreeContext *tc)
 {
     JSStmtInfo *stmt = tc->topStmt;
 
     if (stmt->flags & SIF_SCOPE) {
         JSObject *obj = stmt->blockBox->object;
         JS_ASSERT(!obj->isClonedBlock());
 
         for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
-            JSAtom *atom = JSID_TO_ATOM(r.front().propid);
+            JSAtom *atom = JSID_TO_ATOM(r.front().propid());
 
             /* Beware the empty destructuring dummy. */
             if (atom == tc->parser->context->runtime->atomState.emptyAtom)
                 continue;
             tc->decls.remove(atom);
         }
     }
     js_PopStatement(tc);
@@ -3666,17 +3654,17 @@ DefineGlobal(JSParseNode *pn, JSCodeGene
                 globalObj != holder ||
                 shape->configurable() ||
                 !shape->hasSlot() ||
                 !shape->hasDefaultGetterOrIsMethod() ||
                 !shape->hasDefaultSetter()) {
                 return true;
             }
             
-            def = GlobalScope::GlobalDef(shape->slot);
+            def = GlobalScope::GlobalDef(shape->slot());
         } else {
             def = GlobalScope::GlobalDef(atom, funbox);
         }
 
         if (!globalScope->defs.append(def))
             return false;
 
         jsatomid index = globalScope->names.count();
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -1043,28 +1043,16 @@ struct JSFunctionBox : public JSObjectBo
      */
     bool inAnyDynamicScope() const;
 
     /* 
      * Must this function's descendants be marked as having an extensible
      * ancestor?
      */
     bool scopeIsExtensible() const;
-
-    /*
-     * Unbrand an object being initialized or constructed if any method cannot
-     * be joined to one compiler-created null closure shared among N different
-     * closure environments.
-     *
-     * We despecialize from caching function objects, caching slots or shapes
-     * instead, because an unbranded object may still have joined methods (for
-     * which shape->isMethod), since PropertyCache::fill gives precedence to
-     * joined methods over branded methods.
-     */
-    bool shouldUnbrand(uintN methods, uintN slowMethods) const;
 };
 
 struct JSFunctionBoxQueue {
     JSFunctionBox       **vector;
     size_t              head, tail;
     size_t              lengthMask;
 
     size_t count()  { return head - tail; }
@@ -1175,17 +1163,17 @@ struct Parser : private js::AutoGCRooter
     /*
      * Analyze the tree of functions nested within a single compilation unit,
      * starting at funbox, recursively walking its kids, then following its
      * siblings, their kids, etc.
      */
     bool analyzeFunctions(JSTreeContext *tc);
     void cleanFunctionList(JSFunctionBox **funbox);
     bool markFunArgs(JSFunctionBox *funbox);
-    void markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent);
+    bool markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent);
     void setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags);
 
     void trace(JSTracer *trc);
 
     /*
      * Report a parse (compile) error.
      */
     inline bool reportErrorNumber(JSParseNode *pn, uintN flags, uintN errorNumber, ...);
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -41,64 +41,46 @@
 #include "jspropertycache.h"
 #include "jscntxt.h"
 #include "jsnum.h"
 #include "jsobjinlines.h"
 #include "jspropertycacheinlines.h"
 
 using namespace js;
 
-JS_STATIC_ASSERT(sizeof(PCVal) == sizeof(jsuword));
-
 JS_REQUIRES_STACK PropertyCacheEntry *
 PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *pobj,
-                    const Shape *shape, JSBool adding)
+                    const Shape *shape)
 {
-    jsuword kshape, vshape;
     JSOp op;
     const JSCodeSpec *cs;
-    PCVal vword;
     PropertyCacheEntry *entry;
 
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(!cx->runtime->gcRunning);
 
-    if (js_IsPropertyCacheDisabled(cx)) {
-        PCMETER(disfills++);
-        return JS_NO_PROP_CACHE_FILL;
-    }
-
     /*
      * Check for fill from js_SetPropertyHelper where the setter removed shape
      * from pobj (via unwatch or delete, e.g.).
      */
     if (!pobj->nativeContains(cx, *shape)) {
         PCMETER(oddfills++);
         return JS_NO_PROP_CACHE_FILL;
     }
 
     /*
-     * Dictionary-mode objects have unique shapes, so there is no way to cache
-     * a prediction of the next shape when adding.
-     */
-    if (adding && obj->inDictionaryMode()) {
-        PCMETER(add2dictfills++);
-        return JS_NO_PROP_CACHE_FILL;
-    }
-
-    /*
      * Check for overdeep scope and prototype chain. Because resolve, getter,
      * and setter hooks can change the prototype chain using JS_SetPrototype
      * after LookupPropertyWithFlags has returned, we calculate the protoIndex
      * here and not in LookupPropertyWithFlags.
      *
      * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
      * before any running script might consult a parent-linked scope chain. If
      * this requirement is not satisfied, the fill in progress will never hit,
-     * but vcap vs. scope shape tests ensure nothing malfunctions.
+     * but scope shape tests ensure nothing malfunctions.
      */
     JS_ASSERT_IF(obj == pobj, scopeIndex == 0);
 
     JSObject *tmp = obj;
     for (uintN i = 0; i != scopeIndex; i++)
         tmp = tmp->getParent();
 
     uintN protoIndex = 0;
@@ -112,185 +94,55 @@ PropertyCache::fill(JSContext *cx, JSObj
          */
         if (!tmp || !tmp->isNative()) {
             PCMETER(noprotos++);
             return JS_NO_PROP_CACHE_FILL;
         }
         ++protoIndex;
     }
 
-    if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
+    if (scopeIndex > PCINDEX_SCOPEMASK || protoIndex > PCINDEX_PROTOMASK) {
         PCMETER(longchains++);
         return JS_NO_PROP_CACHE_FILL;
     }
 
     /*
      * Optimize the cached vword based on our parameters and the current pc's
      * opcode format flags.
      */
     jsbytecode *pc;
     JSScript *script = cx->stack.currentScript(&pc);
     op = js_GetOpcode(cx, script, pc);
     cs = &js_CodeSpec[op];
-    kshape = 0;
 
-    do {
-        /*
-         * Check for a prototype "plain old method" callee computation. What
-         * is a plain old method? It's a function-valued property with stub
-         * getter, so get of a function is idempotent.
-         */
-        if (cs->format & JOF_CALLOP) {
-            if (shape->isMethod()) {
-                /*
-                 * A compiler-created function object, AKA a method, already
-                 * memoized in the property tree.
-                 */
-                JS_ASSERT(pobj->hasMethodBarrier());
-                JSObject &funobj = shape->methodObject();
-                JS_ASSERT(funobj == pobj->nativeGetSlot(shape->slot).toObject());
-                vword.setFunObj(funobj);
-                break;
-            }
-
-            /*
-             * N.B. Objects are not branded if type inference is enabled, to
-             * allow property accesses without shape checks in JIT code.
-             */
-            if (!pobj->generic() && shape->hasDefaultGetter() && pobj->containsSlot(shape->slot) &&
-                !cx->typeInferenceEnabled()) {
-                const Value &v = pobj->nativeGetSlot(shape->slot);
-                JSObject *funobj;
-
-                if (IsFunctionObject(v, &funobj)) {
-                    /*
-                     * Great, we have a function-valued prototype property
-                     * where the getter is JS_PropertyStub. The type id in
-                     * pobj does not evolve with changes to property values,
-                     * however.
-                     *
-                     * So here, on first cache fill for this method, we brand
-                     * obj with a new shape and set the JSObject::BRANDED flag.
-                     * Once this flag is set, any property assignment that
-                     * changes the value from or to a different function object
-                     * will result in shape being regenerated.
-                     */
-                    if (!pobj->branded()) {
-                        PCMETER(brandfills++);
-#ifdef DEBUG_notme
-                        JSFunction *fun = JSVAL_TO_OBJECT(v)->getFunctionPrivate();
-                        JSAutoByteString funNameBytes;
-                        if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
-                            fprintf(stderr,
-                                    "branding %p (%s) for funobj %p (%s), shape %lu\n",
-                                    pobj, pobj->getClass()->name, JSVAL_TO_OBJECT(v), funName,
-                                    obj->shape());
-                        }
-#endif
-                        if (!pobj->brand(cx))
-                            return JS_NO_PROP_CACHE_FILL;
-                    }
-                    vword.setFunObj(*funobj);
-                    break;
-                }
-            }
-        } else if ((cs->format & (JOF_SET | JOF_FOR | JOF_INCDEC)) && obj->watched()) {
-            return JS_NO_PROP_CACHE_FILL;
-        }
-
-        /*
-         * If getting a value via a stub getter, or doing an INCDEC op
-         * with stub getters and setters, we can cache the slot.
-         */
-        if (!(cs->format & (JOF_SET | JOF_FOR)) &&
-            (!(cs->format & JOF_INCDEC) || (shape->hasDefaultSetter() && shape->writable())) &&
-            shape->hasDefaultGetter() &&
-            pobj->containsSlot(shape->slot)) {
-            /* Great, let's cache shape's slot and use it on cache hit. */
-            vword.setSlot(shape->slot);
-        } else {
-            /* Best we can do is to cache shape (still a nice speedup). */
-            vword.setShape(shape);
-            if (adding &&
-                pobj->shape() == shape->shapeid) {
-                /*
-                 * Our caller added a new property. We also know that a setter
-                 * that js_NativeSet might have run has not mutated pobj, so
-                 * the added property is still the last one added, and pobj is
-                 * not branded.
-                 *
-                 * We want to cache under pobj's shape before the property
-                 * addition to bias for the case when the mutator opcode
-                 * always adds the same property. This allows us to optimize
-                 * periodic execution of object initializers or other explicit
-                 * initialization sequences such as
-                 *
-                 *   obj = {}; obj.x = 1; obj.y = 2;
-                 *
-                 * We assume that on average the win from this optimization is
-                 * greater than the cost of an extra mismatch per loop owing to
-                 * the bias for the following case:
-                 *
-                 *   obj = {}; ... for (...) { ... obj.x = ... }
-                 *
-                 * On the first iteration of such a for loop, JSOP_SETPROP
-                 * fills the cache with the shape of the newly created object
-                 * obj, not the shape of obj after obj.x has been assigned.
-                 * That mismatches obj's shape on the second iteration. Note
-                 * that on the third and subsequent iterations the cache will
-                 * be hit because the shape is no longer updated.
-                 */
-                JS_ASSERT(shape == pobj->lastProperty());
-                JS_ASSERT(!pobj->nativeEmpty());
-
-                kshape = shape->previous()->shapeid;
-
-                /*
-                 * When adding we predict no prototype object will later gain a
-                 * readonly property or setter.
-                 */
-                vshape = cx->runtime->protoHazardShape;
-            }
-        }
-    } while (0);
-
-    if (kshape == 0) {
-        kshape = obj->shape();
-        vshape = pobj->shape();
-    }
-    JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT);
+    if ((cs->format & JOF_SET) && obj->watched())
+        return JS_NO_PROP_CACHE_FILL;
 
     if (obj == pobj) {
         JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
     } else {
 #ifdef DEBUG
         if (scopeIndex == 0) {
             JS_ASSERT(protoIndex != 0);
             JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj));
         }
 #endif
 
         if (scopeIndex != 0 || protoIndex != 1) {
             /*
              * Make sure that a later shadowing assignment will enter
              * PurgeProtoChain and invalidate this entry, bug 479198.
-             *
-             * This is not thread-safe but we are about to make all objects
-             * except multi-threaded wrappers (bug 566951) single-threaded.
-             * And multi-threaded wrappers are non-native Proxy instances, so
-             * they won't use the property cache.
              */
             obj->setDelegate();
         }
     }
-    JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT);
 
-    entry = &table[hash(pc, kshape)];
+    entry = &table[hash(pc, obj->lastProperty())];
     PCMETER(entry->vword.isNull() || recycles++);
-    entry->assign(pc, kshape, vshape, scopeIndex, protoIndex, vword);
+    entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, scopeIndex, protoIndex);
 
     empty = false;
     PCMETER(fills++);
 
     /*
      * The modfills counter is not exact. It increases if a getter or setter
      * recurse into the interpreter.
      */
@@ -317,88 +169,84 @@ GetAtomFromBytecode(JSContext *cx, jsbyt
     return atom;
 }
 
 JS_REQUIRES_STACK JSAtom *
 PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
                         PropertyCacheEntry *entry)
 {
     JSObject *obj, *pobj, *tmp;
-    uint32 vcap;
-
     JSScript *script = cx->stack.currentScript();
 
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(uintN((cx->fp()->hasImacropc() ? cx->fp()->imacropc() : pc) - script->code)
               < script->length);
 
     JSOp op = js_GetOpcode(cx, script, pc);
     const JSCodeSpec &cs = js_CodeSpec[op];
 
     obj = *objp;
-    vcap = entry->vcap;
+    uint32 vindex = entry->vindex;
 
     if (entry->kpc != pc) {
         PCMETER(kpcmisses++);
 
         JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
 #ifdef DEBUG_notme
-        JSScript *script = cx->fp()->getScript();
         JSAutoByteString printable;
         fprintf(stderr,
                 "id miss for %s from %s:%u"
-                " (pc %u, kpc %u, kshape %u, shape %u)\n",
+                " (pc %u, kpc %u, kshape %p, shape %p)\n",
                 js_AtomToPrintableString(cx, atom, &printable),
                 script->filename,
                 js_PCToLineNumber(cx, script, pc),
                 pc - script->code,
                 entry->kpc - script->code,
                 entry->kshape,
-                obj->shape());
+                obj->lastProperty());
                 js_Disassemble1(cx, script, pc,
                                 pc - script->code,
                                 JS_FALSE, stderr);
 #endif
 
         return atom;
     }
 
-    if (entry->kshape != obj->shape()) {
+    if (entry->kshape != obj->lastProperty()) {
         PCMETER(kshapemisses++);
         return GetAtomFromBytecode(cx, pc, op, cs);
     }
 
     /*
      * PropertyCache::test handles only the direct and immediate-prototype hit
-     * cases. All others go here. We could embed the target object in the cache
-     * entry but then entry size would be 5 words. Instead we traverse chains.
+     * cases. All others go here.
      */
     pobj = obj;
 
     if (JOF_MODE(cs.format) == JOF_NAME) {
-        while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
+        while (vindex & (PCINDEX_SCOPEMASK << PCINDEX_PROTOBITS)) {
             tmp = pobj->getParent();
             if (!tmp || !tmp->isNative())
                 break;
             pobj = tmp;
-            vcap -= PCVCAP_PROTOSIZE;
+            vindex -= PCINDEX_PROTOSIZE;
         }
 
         *objp = pobj;
     }
 
-    while (vcap & PCVCAP_PROTOMASK) {
+    while (vindex & PCINDEX_PROTOMASK) {
         tmp = pobj->getProto();
         if (!tmp || !tmp->isNative())
             break;
         pobj = tmp;
-        --vcap;
+        --vindex;
     }
 
-    if (matchShape(cx, pobj, vcap >> PCVCAP_TAGBITS)) {
+    if (pobj->lastProperty() == entry->pshape) {
 #ifdef DEBUG
         JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
         jsid id = ATOM_TO_JSID(atom);
 
         id = js_CheckForStringIndex(id);
         JS_ASSERT(pobj->nativeContains(cx, id));
 #endif
         *pobjp = pobj;
@@ -412,32 +260,32 @@ PropertyCache::fullTest(JSContext *cx, j
 #ifdef DEBUG
 void
 PropertyCache::assertEmpty()
 {
     JS_ASSERT(empty);
     for (uintN i = 0; i < SIZE; i++) {
         JS_ASSERT(!table[i].kpc);
         JS_ASSERT(!table[i].kshape);
-        JS_ASSERT(!table[i].vcap);
-        JS_ASSERT(table[i].vword.isNull());
+        JS_ASSERT(!table[i].pshape);
+        JS_ASSERT(!table[i].prop);
+        JS_ASSERT(!table[i].vindex);
     }
 }
 #endif
 
 void
 PropertyCache::purge(JSContext *cx)
 {
     if (empty) {
         assertEmpty();
         return;
     }
 
     PodArrayZero(table);
-    JS_ASSERT(table[0].vword.isNull());
     empty = true;
 
 #ifdef JS_PROPERTY_CACHE_METERING
   { static FILE *fp;
     if (!fp)
         fp = fopen("/tmp/propcache.stats", "w");
     if (fp) {
         fputs("Property cache stats for ", fp);
@@ -488,32 +336,16 @@ PropertyCache::purge(JSContext *cx)
     }
   }
 #endif
 
     PCMETER(flushes++);
 }
 
 void
-PropertyCache::purgeForScript(JSContext *cx, JSScript *script)
-{
-    JS_ASSERT(!cx->runtime->gcRunning);
-
-    for (PropertyCacheEntry *entry = table; entry < table + SIZE; entry++) {
-        if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
-            entry->kpc = NULL;
-#ifdef DEBUG
-            entry->kshape = entry->vcap = 0;
-            entry->vword.setNull();
-#endif
-        }
-    }
-}
-
-void
 PropertyCache::restore(PropertyCacheEntry *entry)
 {
     PropertyCacheEntry *entry2;
 
     empty = false;
 
     entry2 = &table[hash(entry->kpc, entry->kshape)];
     *entry2 = *entry;
--- a/js/src/jspropertycache.h
+++ b/js/src/jspropertycache.h
@@ -48,107 +48,48 @@
 namespace js {
 
 /*
  * Property cache with structurally typed capabilities for invalidation, for
  * polymorphic callsite method/get/set speedups.  For details, see
  * <https://developer.mozilla.org/en/SpiderMonkey/Internals/Property_cache>.
  */
 
-/* Property cache value capabilities. */
+/* Indexing for property cache entry scope and prototype chain walking. */
 enum {
-    PCVCAP_PROTOBITS = 4,
-    PCVCAP_PROTOSIZE = JS_BIT(PCVCAP_PROTOBITS),
-    PCVCAP_PROTOMASK = JS_BITMASK(PCVCAP_PROTOBITS),
-
-    PCVCAP_SCOPEBITS = 4,
-    PCVCAP_SCOPESIZE = JS_BIT(PCVCAP_SCOPEBITS),
-    PCVCAP_SCOPEMASK = JS_BITMASK(PCVCAP_SCOPEBITS),
-
-    PCVCAP_TAGBITS = PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS,
-    PCVCAP_TAGMASK = JS_BITMASK(PCVCAP_TAGBITS)
-};
-
-const uint32 SHAPE_OVERFLOW_BIT = JS_BIT(32 - PCVCAP_TAGBITS);
+    PCINDEX_PROTOBITS = 4,
+    PCINDEX_PROTOSIZE = JS_BIT(PCINDEX_PROTOBITS),
+    PCINDEX_PROTOMASK = JS_BITMASK(PCINDEX_PROTOBITS),
 
-/*
- * Property cache value. This is simply a tagged union:
- *    PCVal = (JSObject * | uint32 | js::Shape *).
- * It is the type of PropertyCacheEntry::vword and combines with the tag bits
- * of PropertyCacheEntry::vcap to tell how to get or set the property, once a
- * property cache hit is validated.
- *
- * PropertyCache::purge depends on the bit-pattern of a null PCVal being 0.
- */
-class PCVal
-{
-  private:
-    enum {
-        OBJECT = 0,
-        SLOT = 1,
-        SHAPE = 2,
-        TAG = 3
-    };
-
-    jsuword v;
-
-  public:
-    bool isNull() const { return v == 0; }
-    void setNull() { v = 0; }
-
-    bool isFunObj() const { return (v & TAG) == OBJECT; }
-    JSObject &toFunObj() const {
-        JS_ASSERT(isFunObj());
-        return *reinterpret_cast<JSObject *>(v);
-    }
-    void setFunObj(JSObject &obj) {
-        v = reinterpret_cast<jsuword>(&obj);
-    }
-
-    bool isSlot() const { return v & SLOT; }
-    uint32 toSlot() const { JS_ASSERT(isSlot()); return uint32(v) >> 1; }
-    void setSlot(uint32 slot) { v = (jsuword(slot) << 1) | SLOT; }
-
-    bool isShape() const { return (v & TAG) == SHAPE; }
-    const js::Shape *toShape() const {
-        JS_ASSERT(isShape());
-        return reinterpret_cast<js::Shape *>(v & ~TAG);
-    }
-    void setShape(const js::Shape *shape) {
-        JS_ASSERT(shape);
-        v = reinterpret_cast<jsuword>(shape) | SHAPE;
-    }
+    PCINDEX_SCOPEBITS = 4,
+    PCINDEX_SCOPESIZE = JS_BIT(PCINDEX_SCOPEBITS),
+    PCINDEX_SCOPEMASK = JS_BITMASK(PCINDEX_SCOPEBITS)
 };
 
 struct PropertyCacheEntry
 {
     jsbytecode          *kpc;           /* pc of cache-testing bytecode */
-    jsuword             kshape;         /* shape of direct (key) object */
-    jsuword             vcap;           /* value capability, see above */
-    PCVal               vword;          /* value word, see PCVal above */
-
-    bool adding() const { return vcapTag() == 0 && kshape != vshape(); }
-    bool directHit() const { return vcapTag() == 0 && kshape == vshape(); }
+    const Shape         *kshape;        /* shape of direct (key) object */
+    const Shape         *pshape;        /* shape of owning object */
+    const Shape         *prop;          /* shape of accessed property */
+    uint16              vindex;         /* scope/proto chain indexing,
+                                         * see PCINDEX above */
 
-    jsuword vcapTag() const { return vcap & PCVCAP_TAGMASK; }
-    uint32 vshape() const { return uint32(vcap >> PCVCAP_TAGBITS); }
-    jsuword scopeIndex() const { return (vcap >> PCVCAP_PROTOBITS) & PCVCAP_SCOPEMASK; }
-    jsuword protoIndex() const { return vcap & PCVCAP_PROTOMASK; }
+    bool directHit() const { return vindex == 0; }
 
-    void assign(jsbytecode *kpc, jsuword kshape, jsuword vshape,
-                uintN scopeIndex, uintN protoIndex, PCVal vword) {
-        JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT);
-        JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT);
-        JS_ASSERT(scopeIndex <= PCVCAP_SCOPEMASK);
-        JS_ASSERT(protoIndex <= PCVCAP_PROTOMASK);
+    void assign(jsbytecode *kpc, const Shape *kshape, const Shape *pshape,
+                const Shape *prop, uintN scopeIndex, uintN protoIndex) {
+        JS_ASSERT(scopeIndex <= PCINDEX_SCOPEMASK);
+        JS_ASSERT(protoIndex <= PCINDEX_PROTOMASK);
 
         this->kpc = kpc;
         this->kshape = kshape;
-        this->vcap = (vshape << PCVCAP_TAGBITS) | (scopeIndex << PCVCAP_PROTOBITS) | protoIndex;
-        this->vword = vword;
+        this->pshape = pshape;
+        this->prop = prop;
+        this->vindex = (scopeIndex << PCINDEX_PROTOBITS) | protoIndex;
     }
 };
 
 /*
  * Special value for functions returning PropertyCacheEntry * to distinguish
  * between failure and no no-cache-fill cases.
  */
 #define JS_NO_PROP_CACHE_FILL ((js::PropertyCacheEntry *) NULL + 1)
@@ -207,25 +148,20 @@ class PropertyCache
 # define PCMETER(x)     ((void)0)
 #endif
 
     PropertyCache() {
         PodZero(this);
     }
     
   private:
-    /*
-     * Add kshape rather than xor it to avoid collisions between nearby bytecode
-     * that are evolving an object by setting successive properties, incrementing
-     * the object's shape on each set.
-     */
     static inline jsuword
-    hash(jsbytecode *pc, jsuword kshape)
+    hash(jsbytecode *pc, const Shape *kshape)
     {
-        return ((((jsuword(pc) >> SIZE_LOG2) ^ jsuword(pc)) + kshape) & MASK);
+        return (((jsuword(pc) >> SIZE_LOG2) ^ jsuword(pc) ^ ((jsuword)kshape >> 3)) & MASK);
     }
 
     static inline bool matchShape(JSContext *cx, JSObject *obj, uint32 shape);
 
     JS_REQUIRES_STACK JSAtom *fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
                                        JSObject **pobjp, PropertyCacheEntry *entry);
 
 #ifdef DEBUG
@@ -246,41 +182,27 @@ class PropertyCache
      *
      * On a miss, set *atomp to the name of the property being set and return false.
      */
     JS_ALWAYS_INLINE bool testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj,
                                      PropertyCacheEntry **entryp, JSObject **obj2p,
                                      JSAtom **atomp);
 
     /*
-     * Test for cached information about creating a new own data property on obj at pc.
-     *
-     * On a hit, set *shapep to an shape from the property tree describing the
-     * new property as well as all existing properties on obj and return
-     * true. Otherwise return false.
-     *
-     * Hit or miss, *entryp receives a pointer to the property cache entry.
-     */
-    JS_ALWAYS_INLINE bool testForInit(JSRuntime *rt, jsbytecode *pc, JSObject *obj,
-                                      const js::Shape **shapep, PropertyCacheEntry **entryp);
-
-    /*
      * Fill property cache entry for key cx->fp->pc, optimized value word
      * computed from obj and shape, and entry capability forged from 24-bit
      * obj->shape(), 4-bit scopeIndex, and 4-bit protoIndex.
      *
      * Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was
      * not possible.
      */
     JS_REQUIRES_STACK PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, uintN scopeIndex,
-                                               JSObject *pobj, const js::Shape *shape,
-                                               JSBool adding = false);
+                                               JSObject *pobj, const js::Shape *shape);
 
     void purge(JSContext *cx);
-    void purgeForScript(JSContext *cx, JSScript *script);
 
     /* Restore an entry that may have been purged during a GC. */
     void restore(PropertyCacheEntry *entry);
 };
 
 } /* namespace js */
 
 #endif /* jspropertycache_h___ */
--- a/js/src/jspropertycacheinlines.h
+++ b/js/src/jspropertycacheinlines.h
@@ -43,22 +43,16 @@
 #define jspropertycacheinlines_h___
 
 #include "jslock.h"
 #include "jspropertycache.h"
 #include "jsscope.h"
 
 using namespace js;
 
-/* static */ inline bool
-PropertyCache::matchShape(JSContext *cx, JSObject *obj, uint32 shape)
-{
-    return obj->shape() == shape;
-}
-
 /*
  * This method is designed to inline the fast path in js_Interpret, so it makes
  * "just-so" restrictions on parameters, e.g. pobj and obj should not be the
  * same variable, since for JOF_PROP-mode opcodes, obj must not be changed
  * because of a cache miss.
  *
  * On return, if atom is null then obj points to the scope chain element in
  * which the property was found, pobj is locked, and entry is valid. If atom is
@@ -70,89 +64,59 @@ PropertyCache::matchShape(JSContext *cx,
  * caches (on all threads) by re-generating JSObject::shape().
  */
 JS_ALWAYS_INLINE void
 PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj,
                     JSObject *&pobj, PropertyCacheEntry *&entry, JSAtom *&atom)
 {
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
 
-    uint32 kshape = obj->shape();
+    const Shape *kshape = obj->lastProperty();
     entry = &table[hash(pc, kshape)];
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     JS_ASSERT(&obj != &pobj);
-    JS_ASSERT(entry->kshape < SHAPE_OVERFLOW_BIT);
     if (entry->kpc == pc && entry->kshape == kshape) {
         JSObject *tmp;
         pobj = obj;
-        if (entry->vcapTag() == 1 &&
+        if (entry->vindex == 1 &&
             (tmp = pobj->getProto()) != NULL) {
             pobj = tmp;
         }
 
-        if (matchShape(cx, pobj, entry->vshape())) {
+        if (pobj->lastProperty() == entry->pshape) {
             PCMETER(pchits++);
-            PCMETER(!entry->vcapTag() || protopchits++);
+            PCMETER(!entry->vindex || protopchits++);
             atom = NULL;
             return;
         }
     }
     atom = fullTest(cx, pc, &obj, &pobj, entry);
     if (atom)
         PCMETER(misses++);
 }
 
 JS_ALWAYS_INLINE bool
 PropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj,
                           PropertyCacheEntry **entryp, JSObject **obj2p, JSAtom **atomp)
 {
-    uint32 shape = obj->shape();
-    PropertyCacheEntry *entry = &table[hash(pc, shape)];
+    JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
+
+    const Shape *kshape = obj->lastProperty();
+    PropertyCacheEntry *entry = &table[hash(pc, kshape)];
     *entryp = entry;
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     PCMETER(settests++);
-    JS_ASSERT(entry->kshape < SHAPE_OVERFLOW_BIT);
-    if (entry->kpc == pc && entry->kshape == shape)
+    if (entry->kpc == pc && entry->kshape == kshape)
         return true;
 
     JSAtom *atom = fullTest(cx, pc, &obj, obj2p, entry);
     JS_ASSERT(atom);
 
     PCMETER(misses++);
     PCMETER(setmisses++);
 
     *atomp = atom;
     return false;
 }
 
-JS_ALWAYS_INLINE bool
-PropertyCache::testForInit(JSRuntime *rt, jsbytecode *pc, JSObject *obj,
-                           const js::Shape **shapep, PropertyCacheEntry **entryp)
-{
-    JS_ASSERT(obj->slotSpan() >= JSSLOT_FREE(obj->getClass()));
-    uint32 kshape = obj->shape();
-    PropertyCacheEntry *entry = &table[hash(pc, kshape)];
-    *entryp = entry;
-    PCMETER(pctestentry = entry);
-    PCMETER(tests++);
-    PCMETER(initests++);
-    JS_ASSERT(entry->kshape < SHAPE_OVERFLOW_BIT);
-
-    if (entry->kpc == pc &&
-        entry->kshape == kshape &&
-        entry->vshape() == rt->protoHazardShape) {
-        // If obj is not extensible, we cannot have a cache hit. This happens
-        // for sharp-variable expressions like (#1={x: Object.seal(#1#)}).
-        JS_ASSERT(obj->isExtensible());
-
-        PCMETER(pchits++);
-        PCMETER(inipchits++);
-        JS_ASSERT(entry->vcapTag() == 0);
-        *shapep = entry->vword.toShape();
-        JS_ASSERT((*shapep)->writable());
-        return true;
-    }
-    return false;
-}
-
 #endif /* jspropertycacheinlines_h___ */
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -56,17 +56,17 @@ inline HashNumber
 ShapeHasher::hash(const Lookup l)
 {
     return l->hash();
 }
 
 inline bool
 ShapeHasher::match(const Key k, const Lookup l)
 {
-    return l->matches(k);
+    return k->matches(l);
 }
 
 Shape *
 PropertyTree::newShape(JSContext *cx)
 {
     Shape *shape = js_NewGCShape(cx);
     if (!shape) {
         JS_ReportOutOfMemory(cx);
@@ -90,18 +90,16 @@ HashChildren(Shape *kid1, Shape *kid2)
 }
 
 bool
 PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child)
 {
     JS_ASSERT(!parent->inDictionary());
     JS_ASSERT(!child->parent);
     JS_ASSERT(!child->inDictionary());
-    JS_ASSERT(!JSID_IS_VOID(parent->propid));
-    JS_ASSERT(!JSID_IS_VOID(child->propid));
     JS_ASSERT(cx->compartment == compartment);
     JS_ASSERT(child->compartment() == parent->compartment());
 
     KidsPointer *kidp = &parent->kids;
 
     if (kidp->isNull()) {
         child->setParent(parent);
         kidp->setShape(child);
@@ -131,17 +129,16 @@ PropertyTree::insertChild(JSContext *cx,
     child->setParent(parent);
     return true;
 }
 
 void
 Shape::removeChild(Shape *child)
 {
     JS_ASSERT(!child->inDictionary());
-    JS_ASSERT(!JSID_IS_VOID(propid));
 
     KidsPointer *kidp = &kids;
     if (kidp->isShape()) {
         JS_ASSERT(kidp->toShape() == child);
         kids.setNull();
         return;
     }
 
@@ -149,17 +146,16 @@ Shape::removeChild(Shape *child)
 }
 
 Shape *
 PropertyTree::getChild(JSContext *cx, Shape *parent, const Shape &child)
 {
     Shape *shape;
 
     JS_ASSERT(parent);
-    JS_ASSERT(!JSID_IS_VOID(parent->propid));
 
     /*
      * The property tree has extremely low fan-out below its root in
      * popular embeddings with real-world workloads. Patterns such as
      * defining closures that capture a constructor's environment as
      * getters or setters on the new object that is passed in as
      * |this| can significantly increase fan-out below the property
      * tree root -- see bug 335700 for details.
@@ -176,18 +172,22 @@ PropertyTree::getChild(JSContext *cx, Sh
     } else {
         /* If kidp->isNull(), we always insert. */
     }
 
     shape = newShape(cx);
     if (!shape)
         return NULL;
 
-    new (shape) Shape(child.propid, child.rawGetter, child.rawSetter, child.slot, child.attrs,
-                      child.flags, child.shortid, js_GenerateShape(cx));
+    BaseShape *base = child.base();
+    if (base->isOwned())
+        base = base->base;
+
+    new (shape) Shape(base, child.propid_, child.slot_, child.attrs,
+                      child.flags, child.shortid_);
 
     if (!insertChild(cx, parent, shape))
         return NULL;
 
     return shape;
 }
 
 void
@@ -195,18 +195,16 @@ Shape::finalize(JSContext *cx)
 {
     if (!inDictionary()) {
         if (parent && parent->isMarked())
             parent->removeChild(this);
 
         if (kids.isHash())
             cx->delete_(kids.toHash());
     }
-
-    freeTable(cx);
 }
 
 #ifdef DEBUG
 
 void
 KidsPointer::checkConsistency(const Shape *aKid) const
 {
     if (isShape()) {
@@ -217,16 +215,18 @@ KidsPointer::checkConsistency(const Shap
         KidsHash::Ptr ptr = hash->lookup(aKid);
         JS_ASSERT(*ptr == aKid);
     }
 }
 
 void
 Shape::dump(JSContext *cx, FILE *fp) const
 {
+    jsid propid = this->propid();
+
     JS_ASSERT(!JSID_IS_VOID(propid));
 
     if (JSID_IS_INT(propid)) {
         fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
     } else if (JSID_IS_DEFAULT_XML_NAMESPACE(propid)) {
         fprintf(fp, "<default XML namespace>");
     } else {
         JSLinearString *str;
@@ -239,20 +239,21 @@ Shape::dump(JSContext *cx, FILE *fp) con
             str = s ? s->ensureLinear(cx) : NULL;
         }
         if (!str)
             fputs("<error>", fp);
         else
             FileEscapedString(fp, str, '"');
     }
 
-    fprintf(fp, " g/s %p/%p slot %u attrs %x ",
-            JS_FUNC_TO_DATA_PTR(void *, rawGetter),
-            JS_FUNC_TO_DATA_PTR(void *, rawSetter),
-            slot, attrs);
+    fprintf(fp, " g/s %p/%p slot %d attrs %x ",
+            JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
+            JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
+            hasSlot() ? slot() : -1, attrs);
+
     if (attrs) {
         int first = 1;
         fputs("(", fp);
 #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(" " #display + first, fp), first = 0
         DUMP_ATTR(ENUMERATE, enumerate);
         DUMP_ATTR(READONLY, readonly);
         DUMP_ATTR(PERMANENT, permanent);
         DUMP_ATTR(GETTER, getter);
@@ -269,26 +270,26 @@ Shape::dump(JSContext *cx, FILE *fp) con
 #define DUMP_FLAG(name, display) if (flags & name) fputs(" " #display + first, fp), first = 0
         DUMP_FLAG(HAS_SHORTID, has_shortid);
         DUMP_FLAG(METHOD, method);
         DUMP_FLAG(IN_DICTIONARY, in_dictionary);
 #undef  DUMP_FLAG
         fputs(") ", fp);
     }
 
-    fprintf(fp, "shortid %d\n", shortid);
+    fprintf(fp, "shortid %d\n", shortid());
 }
 
 void
 Shape::dumpSubtree(JSContext *cx, int level, FILE *fp) const
 {
     if (!parent) {
         JS_ASSERT(level == 0);
-        JS_ASSERT(JSID_IS_EMPTY(propid));
-        fprintf(fp, "class %s emptyShape %u\n", clasp->name, shapeid);
+        JS_ASSERT(JSID_IS_EMPTY(propid_));
+        fprintf(fp, "class %s emptyShape\n", getClass()->name);
     } else {
         fprintf(fp, "%*sid ", level, "");
         dump(cx, fp);
     }
 
     if (!kids.isNull()) {
         ++level;
         if (kids.isShape()) {
@@ -327,18 +328,20 @@ js::PropertyTree::dumpShapes(JSContext *
     fprintf(dumpfp, "rt->gcNumber = %lu", (unsigned long)rt->gcNumber);
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
         if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != *c)
             continue;
 
         fprintf(dumpfp, "*** Compartment %p ***\n", (void *)*c);
 
+        /*
         typedef JSCompartment::EmptyShapeSet HS;
         HS &h = (*c)->emptyShapes;
         for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
             Shape *empty = r.front();
             empty->dumpSubtree(cx, 0, dumpfp);
             putc('\n', dumpfp);
         }
+        */
     }
 }
 #endif
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -182,16 +182,17 @@ template <typename K,
           typename V,
           size_t InlineElems>
 class InlineMap;
 
 class PropertyCache;
 struct PropertyCacheEntry;
 
 struct Shape;
+struct BaseShape;
 struct EmptyShape;
 class Bindings;
 
 class MultiDeclRange;
 class ParseMapPool;
 class DefnOrHeader;
 typedef InlineMap<JSAtom *, JSDefinition *, 24> AtomDefnMap;
 typedef InlineMap<JSAtom *, jsatomid, 24> AtomIndexMap;
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -196,16 +196,17 @@ typedef enum {
     /*
      * Trace kinds internal to the engine. The embedding can only them if it
      * implements JSTraceCallback.
      */
 #if JS_HAS_XML_SUPPORT
     JSTRACE_XML,
 #endif
     JSTRACE_SHAPE,
+    JSTRACE_BASE_SHAPE,
     JSTRACE_TYPE_OBJECT,
     JSTRACE_LAST = JSTRACE_TYPE_OBJECT
 } JSGCTraceKind;
 
 /* Struct typedefs. */
 typedef struct JSClass                      JSClass;
 typedef struct JSCompartment                JSCompartment;
 typedef struct JSConstDoubleSpec            JSConstDoubleSpec;
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -777,27 +777,27 @@ JSObject::initRegExp(JSContext *cx, js::
             *shapep = assignInitialRegExpShape(cx);
             if (!*shapep)
                 return false;
         }
         setLastProperty(*shapep);
         JS_ASSERT(!nativeEmpty());
     }
 
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom))->slot ==
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_LAST_INDEX);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom))->slot ==
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_SOURCE);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom))->slot ==
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_GLOBAL);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom))->slot ==
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_IGNORE_CASE);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom))->slot ==
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_MULTILINE);
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom))->slot ==
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom))->slot() ==
               JSObject::JSSLOT_REGEXP_STICKY);
 
     setPrivate(re);
     zeroRegExpLastIndex();
     setRegExpSource(re->getSource());
     setRegExpGlobal(re->global());
     setRegExpIgnoreCase(re->ignoreCase());
     setRegExpMultiline(re->multiline());
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -64,48 +64,16 @@
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
-uint32
-js_GenerateShape(JSRuntime *rt)
-{
-    uint32 shape;
-
-    shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
-    JS_ASSERT(shape != 0);
-    if (shape >= SHAPE_OVERFLOW_BIT) {
-        /*
-         * FIXME bug 440834: The shape id space has overflowed. Currently we
-         * cope badly with this and schedule the GC on the every call. But
-         * first we make sure that increments from other threads would not
-         * have a chance to wrap around shapeGen to zero.
-         */
-        rt->shapeGen = SHAPE_OVERFLOW_BIT;
-        shape = SHAPE_OVERFLOW_BIT;
-
-#ifdef JS_THREADSAFE
-        AutoLockGC lockIf(rt);
-#endif
-        GCREASON(SHAPE);
-        TriggerGC(rt);
-    }
-    return shape;
-}
-
-uint32
-js_GenerateShape(JSContext *cx)
-{
-    return js_GenerateShape(cx->runtime);
-}
-
 bool
 JSObject::ensureClassReservedSlotsForEmptyObject(JSContext *cx)
 {
     JS_ASSERT(nativeEmpty());
 
     /*
      * Subtle rule: objects that call JSObject::ensureInstanceReservedSlots
      * must either:
@@ -152,43 +120,91 @@ PropertyTable::init(JSRuntime *rt, Shape
      */
     entries = (Shape **) rt->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
     if (!entries)
         return false;
 
     hashShift = JS_DHASH_BITS - sizeLog2;
     for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
-        Shape **spp = search(shape.propid, true);
+        Shape **spp = search(shape.propid(), true);
 
         /*
          * Beware duplicate args and arg vs. var conflicts: the youngest shape
          * (nearest to lastProp) must win. See bug 600067.
          */
         if (!SHAPE_FETCH(spp))
             SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
     }
     return true;
 }
 
 bool
+Shape::makeOwnBaseShape(JSContext *cx)
+{
+    JS_ASSERT(!base()->isOwned());
+
+    BaseShape *nbase = js_NewGCBaseShape(cx);
+    if (!nbase)
+        return false;
+
+    new (nbase) BaseShape(*base());
+    nbase->setOwned(base());
+
+    this->base_ = nbase;
+
+    return true;
+}
+
+void
+Shape::handoffTable(Shape *shape)
+{
+    JS_ASSERT(inDictionary() && shape->inDictionary());
+
+    if (this == shape)
+        return;
+
+    JS_ASSERT(base()->isOwned() && !shape->base()->isOwned());
+
+    BaseShape *nbase = base();
+
+    /* Update the slot span when growing dictionaries. */
+    uint32 span = nbase->slotSpan;
+    if (shape->hasSlot())
+        span = JS_MAX(span, shape->slot() + 1);
+
+    PropertyTable *table = nbase->table();
+    JS_ASSERT(table);
+
+    this->base_ = nbase->base;
+
+    new (nbase) BaseShape(shape->base(), table, span);
+
+    shape->base_ = nbase;
+}
+
+bool
 Shape::hashify(JSContext *cx)
 {
     JS_ASSERT(!hasTable());
+
+    if (!ensureOwnBaseShape(cx))
+        return false;
+
     JSRuntime *rt = cx->runtime;
     PropertyTable *table = rt->new_<PropertyTable>(entryCount());
     if (!table)
         return false;
 
     if (!table->init(rt, this)) {
         rt->free_(table);
         return false;
     }
 
-    setTable(table);
+    base()->setTable(table);
     return true;
 }
 
 /*
  * Double hashing needs the second hash code to be relatively prime to table
  * size, so we simply make hash2 odd.
  */
 #define HASH1(hash0,shift)      ((hash0) >> (shift))
@@ -198,31 +214,31 @@ Shape **
 PropertyTable::search(jsid id, bool adding)
 {
     JSHashNumber hash0, hash1, hash2;
     int sizeLog2;
     Shape *stored, *shape, **spp, **firstRemoved;
     uint32 sizeMask;
 
     JS_ASSERT(entries);
-    JS_ASSERT(!JSID_IS_VOID(id));
+    JS_ASSERT(!JSID_IS_EMPTY(id));
 
     /* Compute the primary hash address. */
     hash0 = HashId(id);
     hash1 = HASH1(hash0, hashShift);
     spp = entries + hash1;
 
     /* Miss: return space for a new entry. */
     stored = *spp;
     if (SHAPE_IS_FREE(stored))
         return spp;
 
     /* Hit: return entry. */
     shape = SHAPE_CLEAR_COLLISION(stored);
-    if (shape && shape->propid == id)
+    if (shape && shape->propid() == id)
         return spp;
 
     /* Collision: double hash. */
     sizeLog2 = JS_DHASH_BITS - hashShift;
     hash2 = HASH2(hash0, sizeLog2, hashShift);
     sizeMask = JS_BITMASK(sizeLog2);
 
 #ifdef DEBUG
@@ -246,17 +262,17 @@ PropertyTable::search(jsid id, bool addi
         hash1 &= sizeMask;
         spp = entries + hash1;
 
         stored = *spp;
         if (SHAPE_IS_FREE(stored))
             return (adding && firstRemoved) ? firstRemoved : spp;
 
         shape = SHAPE_CLEAR_COLLISION(stored);
-        if (shape && shape->propid == id) {
+        if (shape && shape->propid() == id) {
             JS_ASSERT(collision_flag);
             return spp;
         }
 
         if (SHAPE_IS_REMOVED(stored)) {
             if (!firstRemoved)
                 firstRemoved = spp;
         } else {
@@ -293,17 +309,17 @@ PropertyTable::change(int log2Delta, JSC
     removedCount = 0;
     Shape **oldTable = entries;
     entries = newTable;
 
     /* Copy only live entries, leaving removed and free ones behind. */
     for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) {
         Shape *shape = SHAPE_FETCH(oldspp);
         if (shape) {
-            Shape **spp = search(shape->propid, true);
+            Shape **spp = search(shape->propid(), true);
             JS_ASSERT(SHAPE_IS_FREE(*spp));
             *spp = shape;
         }
         oldsize--;
     }
 
     /* Finally, free the old entries storage. */
     cx->free_(oldTable);
@@ -321,66 +337,61 @@ PropertyTable::grow(JSContext *cx)
     if (!change(delta, cx) && entryCount + removedCount == size - 1) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
     return true;
 }
 
 Shape *
-Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
+Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp, bool allowDictionary)
 {
-    JS_ASSERT(!JSID_IS_VOID(child.propid));
     JS_ASSERT(!child.inDictionary());
 
     if (inDictionary()) {
         Shape *oldShape = *listp;
-        PropertyTable *table = (oldShape && oldShape->hasTable()) ? oldShape->getTable() : NULL;
+        PropertyTable *table = oldShape->getTable();
 
         /*
          * Attempt to grow table if needed before extending *listp, rather than
-         * risking OOM under table->grow after newDictionaryShape succeeds, and
-         * then have to fix up *listp.
+         * risking OOM under table->grow after initDictionaryShape, and then
+         * have to fix up *listp.
          */
-        if (table && table->needsToGrow() && !table->grow(cx))
+        if (table->needsToGrow() && !table->grow(cx))
+            return NULL;
+
+        Shape *newShape = js_NewGCShape(cx);
+        if (!newShape)
             return NULL;
 
-        if (newDictionaryShape(cx, child, listp)) {
-            Shape *newShape = *listp;
+        newShape->initDictionaryShape(child, listp);
 
-            JS_ASSERT(oldShape == newShape->parent);
-            if (table) {
-                /* Add newShape to the property table. */
-                Shape **spp = table->search(newShape->propid, true);
+        JS_ASSERT(oldShape == newShape->parent);
+
+        /* Add newShape to the property table. */
+        Shape **spp = table->search(newShape->propid(), true);
 
-                /*
-                 * Beware duplicate formal parameters, allowed by ECMA-262 in
-                 * non-strict mode. Otherwise we know that Bindings::add (our
-                 * caller) won't pass an id already in the table to us. In the
-                 * case of duplicate formals, the last one wins, so while we
-                 * must not overcount entries, we must store newShape.
-                 */
-                if (!SHAPE_FETCH(spp))
-                    ++table->entryCount;
-                SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
+        /*
+         * Beware duplicate formal parameters, allowed by ECMA-262 in
+         * non-strict mode. Otherwise we know that Bindings::add (our caller)
+         * won't pass an id already in the table to us. In the case of
+         * duplicate formals, the last one wins, so while we must not overcount
+         * entries, we must store newShape.
+         */
+        if (!SHAPE_FETCH(spp))
+            ++table->entryCount;
+        SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
 
-                /* Hand the table off from oldShape to newShape. */
-                oldShape->setTable(NULL);
-                newShape->setTable(table);
-            } else {
-                if (!newShape->hasTable())
-                    newShape->hashify(cx);
-            }
-            return newShape;
-        }
+        /* Hand the table off from oldShape to newShape. */
+        oldShape->handoffTable(newShape);
 
-        return NULL;
+        return newShape;
     }
 
-    if ((*listp)->entryCount() >= PropertyTree::MAX_HEIGHT) {
+    if (allowDictionary && (*listp)->entryCount() >= PropertyTree::MAX_HEIGHT) {
         Shape *dprop = Shape::newDictionaryList(cx, listp);
         if (!dprop)
             return NULL;
         return dprop->getChild(cx, child, listp);
     }
 
     Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, child);
     if (shape) {
@@ -394,101 +405,81 @@ Shape::getChild(JSContext *cx, const js:
 /*
  * Get or create a property-tree or dictionary child property of parent, which
  * must be lastProp if inDictionaryMode(), else parent must be one of lastProp
  * or lastProp->parent.
  */
 Shape *
 JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child)
 {
-    JS_ASSERT(!JSID_IS_VOID(child.propid));
-    JS_ASSERT(!child.inDictionary());
-
     /*
-     * Shared properties have no slot. Unshared properties allocate a slot here
-     * but may lose it due to a JS_ClearScope call.
+     * Shared properties have no slot, but slot_ will reflect that of parent.
+     * Unshared properties allocate a slot here but may lose it due to a
+     * JS_ClearScope call.
      */
-    if (child.attrs & JSPROP_SHARED) {
-        child.slot = SHAPE_INVALID_SLOT;
+    if (!child.hasSlot()) {
+        child.slot_ = parent->maybeSlot();
     } else {
-        /*
-         * We may have set slot from a nearly-matching shape, above. If so,
-         * we're overwriting that nearly-matching shape, so we can reuse its
-         * slot -- we don't need to allocate a new one. Similarly, we use a
-         * specific slot if provided by the caller.
-         */
-        if (child.slot == SHAPE_INVALID_SLOT && !allocSlot(cx, &child.slot))
-            return NULL;
+        if (child.hasMissingSlot()) {
+            uint32 slot;
+            if (!allocSlot(cx, &slot))
+                return NULL;
+            child.slot_ = slot;
+        } else {
+            /* Slots can only be allocated out of order on objects in dictionary mode. */
+            JS_ASSERT(inDictionaryMode() ||
+                      lastProp->hasMissingSlot() ||
+                      child.slot() == lastProp->slot() + 1);
+        }
     }
 
     Shape *shape;
 
     if (inDictionaryMode()) {
         JS_ASSERT(parent == lastProp);
-        if (parent->frozen()) {
-            parent = Shape::newDictionaryList(cx, &lastProp);
-            if (!parent)
-                return NULL;
-            JS_ASSERT(!parent->frozen());
-        }
-        shape = Shape::newDictionaryShape(cx, child, &lastProp);
+        shape = js_NewGCShape(cx);
         if (!shape)
             return NULL;
+        shape->initDictionaryShape(child, &lastProp);
     } else {
         shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, child);
         if (!shape)
             return NULL;
         JS_ASSERT(shape->parent == parent);
         JS_ASSERT_IF(parent != lastProp, parent == lastProp->parent);
         setLastProperty(shape);
     }
 
     updateFlags(shape);
-    updateShape(cx);
     return shape;
 }
 
 Shape *
-Shape::newDictionaryShape(JSContext *cx, const Shape &child, Shape **listp)
-{
-    Shape *dprop = JS_PROPERTY_TREE(cx).newShape(cx);
-    if (!dprop)
-        return NULL;
-
-    new (dprop) Shape(child.propid, child.rawGetter, child.rawSetter, child.slot, child.attrs,
-                      (child.flags & ~FROZEN) | IN_DICTIONARY, child.shortid,
-                      js_GenerateShape(cx), child.slotSpan);
-
-    dprop->listp = NULL;
-    dprop->insertIntoDictionary(listp);
-    return dprop;
-}
-
-Shape *
 Shape::newDictionaryList(JSContext *cx, Shape **listp)
 {
     Shape *shape = *listp;
     Shape *list = shape;
 
     /*
      * We temporarily create the dictionary shapes using a root located on the
      * stack. This way, the GC doesn't see any intermediate state until we
      * switch listp at the end.
      */
     Shape *root = NULL;
     Shape **childp = &root;
 
     while (shape) {
-        JS_ASSERT_IF(!shape->frozen(), !shape->inDictionary());
+        JS_ASSERT(!shape->inDictionary());
 
-        Shape *dprop = Shape::newDictionaryShape(cx, *shape, childp);
+        Shape *dprop = js_NewGCShape(cx);
         if (!dprop) {
             *listp = list;
             return NULL;
         }
+        dprop->initDictionaryShape(*shape, childp);
 
         JS_ASSERT(!dprop->hasTable());
         childp = &dprop->parent;
         shape = shape->parent;
     }
 
     *listp = root;
     root->listp = listp;
@@ -500,20 +491,25 @@ Shape::newDictionaryList(JSContext *cx, 
 
 bool
 JSObject::toDictionaryMode(JSContext *cx)
 {
     JS_ASSERT(!inDictionaryMode());
 
     /* We allocate the shapes from cx->compartment, so make sure it's right. */
     JS_ASSERT(compartment() == cx->compartment);
+
+    uint32 span = slotSpan();
+
     if (!Shape::newDictionaryList(cx, &lastProp))
         return false;
 
-    clearOwnShape();
+    JS_ASSERT(lastProp->hasTable());
+    lastProp->base()->slotSpan = span;
+
     return true;
 }
 
 /*
  * Normalize stub getter and setter values for faster is-stub testing in the
  * SHAPE_CALL_[GS]ETTER macros.
  */
 static inline bool
@@ -522,20 +518,20 @@ NormalizeGetterAndSetter(JSContext *cx, 
                          PropertyOp &getter,
                          StrictPropertyOp &setter)
 {
     if (setter == JS_StrictPropertyStub) {
         JS_ASSERT(!(attrs & JSPROP_SETTER));
         setter = NULL;
     }
     if (flags & Shape::METHOD) {
-        /* Here, getter is the method, a function object reference. */
-        JS_ASSERT(getter);
+        JS_ASSERT_IF(getter, getter == JS_PropertyStub);
         JS_ASSERT(!setter);
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+        getter = NULL;
     } else {
         if (getter == JS_PropertyStub) {
             JS_ASSERT(!(attrs & JSPROP_GETTER));
             getter = NULL;
         }
     }
 
     return true;
@@ -553,111 +549,108 @@ JSObject::checkShapeConsistency()
             throttle = atoi(var);
         if (throttle < 0)
             throttle = 0;
     }
     if (throttle == 0)
         return;
 
     JS_ASSERT(isNative());
-    if (hasOwnShape())
-        JS_ASSERT(objShape != lastProp->shapeid);
-    else
-        JS_ASSERT(objShape == lastProp->shapeid);
 
     Shape *shape = lastProp;
     Shape *prev = NULL;
 
     if (inDictionaryMode()) {
-        if (shape->hasTable()) {
-            PropertyTable *table = shape->getTable();
-            for (uint32 fslot = table->freelist; fslot != SHAPE_INVALID_SLOT;
-                 fslot = getSlot(fslot).toPrivateUint32()) {
-                JS_ASSERT(fslot < shape->slotSpan);
-            }
+        JS_ASSERT(shape->hasTable());
 
-            for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
-                JS_ASSERT_IF(shape != lastProp, !shape->hasTable());
+        PropertyTable *table = shape->getTable();
+        for (uint32 fslot = table->freelist; fslot != SHAPE_INVALID_SLOT;
+             fslot = getSlot(fslot).toPrivateUint32()) {
+            JS_ASSERT(fslot < slotSpan());
+        }
 
-                Shape **spp = table->search(shape->propid, false);
-                JS_ASSERT(SHAPE_FETCH(spp) == shape);
-            }
-        } else {
-            shape = shape->parent;
-            for (int n = throttle; --n >= 0 && shape; shape = shape->parent)
-                JS_ASSERT(!shape->hasTable());
+        for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
+            JS_ASSERT_IF(shape != lastProp, !shape->hasTable());
+
+            Shape **spp = table->search(shape->propid(), false);
+            JS_ASSERT(SHAPE_FETCH(spp) == shape);
         }
 
         shape = lastProp;
         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
-            JS_ASSERT_IF(shape->slot != SHAPE_INVALID_SLOT, shape->slot < shape->slotSpan);
+            JS_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
             if (!prev) {
                 JS_ASSERT(shape == lastProp);
                 JS_ASSERT(shape->listp == &lastProp);
             } else {
                 JS_ASSERT(shape->listp == &prev->parent);
-                JS_ASSERT(prev->slotSpan >= shape->slotSpan);
             }
             prev = shape;
         }
     } else {
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
             if (shape->hasTable()) {
                 PropertyTable *table = shape->getTable();
                 JS_ASSERT(shape->parent);
                 for (Shape::Range r(shape); !r.empty(); r.popFront()) {
-                    Shape **spp = table->search(r.front().propid, false);
+                    Shape **spp = table->search(r.front().propid(), false);
                     JS_ASSERT(SHAPE_FETCH(spp) == &r.front());
                 }
             }
             if (prev) {
-                JS_ASSERT(prev->slotSpan >= shape->slotSpan);
+                JS_ASSERT(prev->maybeSlot() >= shape->maybeSlot());
                 shape->kids.checkConsistency(prev);
             }
             prev = shape;
         }
     }
 }
 #else
 # define CHECK_SHAPE_CONSISTENCY(obj) ((void)0)
 #endif
 
 const Shape *
 JSObject::addProperty(JSContext *cx, jsid id,
                       PropertyOp getter, StrictPropertyOp setter,
                       uint32 slot, uintN attrs,
-                      uintN flags, intN shortid)
+                      uintN flags, intN shortid, bool allowDictionary)
 {
     JS_ASSERT(!JSID_IS_VOID(id));
 
     if (!isExtensible()) {
         reportNotExtensible(cx);
         return NULL;
     }
 
     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
 
     /* Search for id with adding = true in order to claim its entry. */
     Shape **spp = nativeSearch(cx, id, true);
     JS_ASSERT(!SHAPE_FETCH(spp));
-    return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
+    return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, allowDictionary);
 }
 
 const Shape *
 JSObject::addPropertyInternal(JSContext *cx, jsid id,
                               PropertyOp getter, StrictPropertyOp setter,
                               uint32 slot, uintN attrs,
                               uintN flags, intN shortid,
-                              Shape **spp)
+                              Shape **spp, bool allowDictionary)
 {
-    JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
+    JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
 
     PropertyTable *table = NULL;
     if (!inDictionaryMode()) {
-        if (lastProp->entryCount() >= PropertyTree::MAX_HEIGHT) {
+        bool stableSlot =
+            (slot == SHAPE_INVALID_SLOT) ||
+            lastProp->hasMissingSlot() ||
+            (slot == lastProp->slot() + 1);
+        JS_ASSERT_IF(!allowDictionary, stableSlot);
+        if (allowDictionary &&
+            (!stableSlot || lastProp->entryCount() >= PropertyTree::MAX_HEIGHT)) {
             if (!toDictionaryMode(cx))
                 return NULL;
             spp = nativeSearch(cx, id, true);
             table = lastProp->getTable();
         }
     } else if (lastProp->hasTable()) {
         table = lastProp->getTable();
         if (table->needsToGrow()) {
@@ -665,34 +658,38 @@ JSObject::addPropertyInternal(JSContext 
                 return NULL;
 
             spp = table->search(id, true);
             JS_ASSERT(!SHAPE_FETCH(spp));
         }
     }
 
     /* Find or create a property tree node labeled by our arguments. */
-    const Shape *shape;
+    Shape *shape;
     {
-        Shape child(id, getter, setter, slot, attrs, flags, shortid);
+        BaseShape base(getClass(), attrs, getter, setter);
+        BaseShape *nbase = BaseShape::lookup(cx, base);
+        if (!nbase)
+            return NULL;
+
+        Shape child(nbase, id, slot, attrs, flags, shortid);
         shape = getChildProperty(cx, lastProp, child);
     }
 
     if (shape) {
         JS_ASSERT(shape == lastProp);
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
             SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
             ++table->entryCount;
 
             /* Pass the table along to the new lastProp, namely shape. */
             JS_ASSERT(shape->parent->getTable() == table);
-            shape->parent->setTable(NULL);
-            shape->setTable(table);
+            shape->parent->handoffTable(shape);
         }
 
         CHECK_SHAPE_CONSISTENCY(this);
         return shape;
     }
 
     CHECK_SHAPE_CONSISTENCY(this);
     return NULL;
@@ -710,159 +707,158 @@ CheckCanChangeAttrs(JSContext *cx, JSObj
         return true;
 
     /* A permanent property must stay permanent. */
     *attrsp |= JSPROP_PERMANENT;
 
     /* Reject attempts to remove a slot from the permanent data property. */
     if (shape->isDataDescriptor() && shape->hasSlot() &&
         (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
-        obj->reportNotConfigurable(cx, shape->propid);
+        obj->reportNotConfigurable(cx, shape->propid());
         return false;
     }
 
     return true;
 }
 
 const Shape *
 JSObject::putProperty(JSContext *cx, jsid id,
                       PropertyOp getter, StrictPropertyOp setter,
                       uint32 slot, uintN attrs,
                       uintN flags, intN shortid)
 {
     JS_ASSERT(!JSID_IS_VOID(id));
 
-    /*
-     * Horrid non-strict eval, debuggers, and |default xml namespace ...| may
-     * extend Call objects.
-     */
-    if (lastProp->frozen()) {
-        if (!Shape::newDictionaryList(cx, &lastProp))
-            return NULL;
-        JS_ASSERT(!lastProp->frozen());
-    }
-
     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
 
     /* Search for id in order to claim its entry if table has been allocated. */
     Shape **spp = nativeSearch(cx, id, true);
     Shape *shape = SHAPE_FETCH(spp);
     if (!shape) {
         /*
          * You can't add properties to a non-extensible object, but you can change
          * attributes of properties in such objects.
          */
         if (!isExtensible()) {
             reportNotExtensible(cx);
             return NULL;
         }
 
-        return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
+        return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
     }
 
     /* Property exists: search must have returned a valid *spp. */
     JS_ASSERT(!SHAPE_IS_REMOVED(*spp));
 
     if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
         return NULL;
     
     /*
      * If the caller wants to allocate a slot, but doesn't care which slot,
      * copy the existing shape's slot into slot so we can match shape, if all
      * other members match.
      */
     bool hadSlot = shape->hasSlot();
-    uint32 oldSlot = shape->slot;
+    uint32 oldSlot = shape->maybeSlot();
     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
         slot = oldSlot;
 
+    BaseShape *nbase;
+    {
+        BaseShape base(getClass(), attrs, getter, setter);
+        nbase = BaseShape::lookup(cx, base);
+        if (!nbase)
+            return NULL;
+    }
+
     /*
      * Now that we've possibly preserved slot, check whether all members match.
      * If so, this is a redundant "put" and we can return without more work.
      */
-    if (shape->matchesParamsAfterId(getter, setter, slot, attrs, flags, shortid))
+    if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
         return shape;
 
     /*
      * Overwriting a non-last property requires switching to dictionary mode.
      * The shape tree is shared immutable, and we can't removeProperty and then
      * addPropertyInternal because a failure under add would lose data.
      */
     if (shape != lastProp && !inDictionaryMode()) {
         if (!toDictionaryMode(cx))
             return NULL;
-        spp = nativeSearch(cx, shape->propid);
+        spp = nativeSearch(cx, shape->propid());
         shape = SHAPE_FETCH(spp);
     }
 
+    JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
+
     /*
      * Now that we have passed the lastProp->frozen() check at the top of this
      * method, and the non-last-property conditioning just above, we are ready
      * to overwrite.
      *
      * Optimize the case of a non-frozen dictionary-mode object based on the
      * property that dictionaries exclusively own their mutable shape structs,
-     * each of which has a unique shape number (not shared via a shape tree).
+     * each of which has a unique shape (not shared via a shape tree).
      *
      * This is more than an optimization: it is required to preserve for-in
      * enumeration order (see bug 601399).
      */
     if (inDictionaryMode()) {
+        bool updateLast = (shape == lastProp);
+        if (!generateOwnShape(cx))
+            return NULL;
+        if (updateLast)
+            shape = lastProp;
+
         /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
         if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
             if (!allocSlot(cx, &slot))
                 return NULL;
         }
 
-        shape->slot = slot;
-        if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
-            shape->slotSpan = slot + 1;
-
-            for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
-                if (temp->slotSpan <= slot)
-                    temp->slotSpan = slot + 1;
-            }
+        if (shape == lastProp) {
+            uint32 span = shape->base()->slotSpan;
+            PropertyTable *table = shape->base()->table();
+            new (shape->base()) BaseShape(nbase, table, span);
+        } else {
+            shape->base_ = nbase;
         }
 
-        shape->rawGetter = getter;
-        shape->rawSetter = setter;
+        shape->slot_ = slot;
         shape->attrs = uint8(attrs);
         shape->flags = flags | Shape::IN_DICTIONARY;
-        shape->shortid = int16(shortid);
+        shape->shortid_ = int16(shortid);
 
         /*
          * We are done updating shape and lastProp. Now we may need to update
          * flags and we will need to update objShape, which is no longer "own".
          * In the last non-dictionary property case in the else clause just
          * below, getChildProperty handles this for us. First update flags.
          */
         updateFlags(shape);
-
-        /*
-         * We have just mutated shape in place, but nothing caches it based on
-         * shape->shape unless shape is lastProp and !hasOwnShape()). Therefore
-         * we regenerate only lastProp->shape. We will clearOwnShape(), which
-         * sets objShape to lastProp->shape.
-         */
-        lastProp->shapeid = js_GenerateShape(cx);
-        clearOwnShape();
     } else {
         /*
          * Updating lastProp in a non-dictionary-mode object. Such objects
          * share their shapes via a tree rooted at a prototype emptyShape, or
          * perhaps a well-known compartment-wide singleton emptyShape.
          *
          * If any shape in the tree has a property hashtable, it is shared and
          * immutable too, therefore we must not update *spp.
          */
+        BaseShape base(getClass(), attrs, getter, setter);
+        BaseShape *nbase = BaseShape::lookup(cx, base);
+        if (!nbase)
+            return NULL;
+
         JS_ASSERT(shape == lastProp);
         removeLastProperty();
 
         /* Find or create a property tree node labeled by our arguments. */
-        Shape child(id, getter, setter, slot, attrs, flags, shortid);
+        Shape child(nbase, id, slot, attrs, flags, shortid);
 
         Shape *newShape = getChildProperty(cx, lastProp, child);
         if (!newShape) {
             setLastProperty(shape);
             CHECK_SHAPE_CONSISTENCY(this);
             return NULL;
         }
 
@@ -871,245 +867,161 @@ JSObject::putProperty(JSContext *cx, jsi
 
     /*
      * Can't fail now, so free the previous incarnation's slot if the new shape
      * has no slot. But we do not need to free oldSlot (and must not, as trying
      * to will botch an assertion in JSObject::freeSlot) if the new lastProp
      * (shape here) has a slotSpan that does not cover it.
      */
     if (hadSlot && !shape->hasSlot()) {
-        if (oldSlot < shape->slotSpan)
+        if (oldSlot < slotSpan())
             freeSlot(cx, oldSlot);
         else
             setSlot(oldSlot, UndefinedValue());
         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     }
 
     CHECK_SHAPE_CONSISTENCY(this);
 
     return shape;
 }
 
 const Shape *
 JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN mask,
                          PropertyOp getter, StrictPropertyOp setter)
 {
-    JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
-    JS_ASSERT(!JSID_IS_VOID(shape->propid));
     JS_ASSERT(nativeContains(cx, *shape));
 
     attrs |= shape->attrs & mask;
 
     /* Allow only shared (slotless) => unshared (slotful) transition. */
     JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
               !(attrs & JSPROP_SHARED));
 
-    /* Don't allow method properties to be changed to have a getter. */
-    JS_ASSERT_IF(getter != shape->rawGetter, !shape->isMethod());
+    /* Don't allow method properties to be changed to have a getter or setter. */
+    JS_ASSERT_IF(shape->isMethod(), !getter && !setter);
 
-    types::MarkTypePropertyConfigured(cx, this, shape->propid);
+    types::MarkTypePropertyConfigured(cx, this, shape->propid());
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
-        types::AddTypePropertyId(cx, this, shape->propid, types::Type::UnknownType());
+        types::AddTypePropertyId(cx, this, shape->propid(), types::Type::UnknownType());
 
     if (getter == JS_PropertyStub)
         getter = NULL;
     if (setter == JS_StrictPropertyStub)
         setter = NULL;
 
     if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
         return NULL;
     
     if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
         return shape;
 
-    const Shape *newShape;
-
     /*
-     * Dictionary-mode objects exclusively own their mutable shape structs, so
-     * we simply modify in place.
+     * Let JSObject::putProperty handle this |overwriting| case, including
+     * the conservation of shape->slot (if it's valid). We must not call
+     * removeProperty because it will free an allocated shape->slot, and
+     * putProperty won't re-allocate it.
      */
-    if (inDictionaryMode()) {
-        /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
-        uint32 slot = shape->slot;
-        if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
-            if (!allocSlot(cx, &slot))
-                return NULL;
-        }
-
-        Shape *mutableShape = const_cast<Shape *>(shape);
-        mutableShape->slot = slot;
-        if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
-            mutableShape->slotSpan = slot + 1;
-
-            for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
-                if (temp->slotSpan <= slot)
-                    temp->slotSpan = slot + 1;
-            }
-        }
-
-        mutableShape->rawGetter = getter;
-        mutableShape->rawSetter = setter;
-        mutableShape->attrs = uint8(attrs);
-
-        updateFlags(shape);
-
-        /* See the corresponding code in putProperty. */
-        lastProp->shapeid = js_GenerateShape(cx);
-        clearOwnShape();
-
-        newShape = mutableShape;
-    } else if (shape == lastProp) {
-        Shape child(shape->propid, getter, setter, shape->slot, attrs, shape->flags,
-                    shape->shortid);
-
-        newShape = getChildProperty(cx, shape->parent, child);
-#ifdef DEBUG
-        if (newShape) {
-            JS_ASSERT(newShape == lastProp);
-            if (newShape->hasTable()) {
-                Shape **spp = nativeSearch(cx, shape->propid);
-                JS_ASSERT(SHAPE_FETCH(spp) == newShape);
-            }
-        }
-#endif
-    } else {
-        /*
-         * Let JSObject::putProperty handle this |overwriting| case, including
-         * the conservation of shape->slot (if it's valid). We must not call
-         * removeProperty because it will free an allocated shape->slot, and
-         * putProperty won't re-allocate it.
-         */
-        Shape child(shape->propid, getter, setter, shape->slot, attrs, shape->flags,
-                    shape->shortid);
-        newShape = putProperty(cx, child.propid, child.rawGetter, child.rawSetter, child.slot,
-                               child.attrs, child.flags, child.shortid);
-    }
+    const Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
+                                        attrs, shape->flags, shape->maybeShortid());
 
     CHECK_SHAPE_CONSISTENCY(this);
     return newShape;
 }
 
 bool
 JSObject::removeProperty(JSContext *cx, jsid id)
 {
     Shape **spp = nativeSearch(cx, id);
     Shape *shape = SHAPE_FETCH(spp);
     if (!shape)
         return true;
 
-    /* First, if shape is unshared and not has a slot, free its slot number. */
-    bool addedToFreelist = false;
-    bool hadSlot = shape->hasSlot();
-    if (hadSlot) {
-        addedToFreelist = freeSlot(cx, shape->slot);
-        JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
-    }
-
     /* If shape is not the last property added, switch to dictionary mode. */
     if (shape != lastProp && !inDictionaryMode()) {
         if (!toDictionaryMode(cx))
             return false;
-        spp = nativeSearch(cx, shape->propid);
+        spp = nativeSearch(cx, shape->propid());
         shape = SHAPE_FETCH(spp);
     }
 
     /*
+     * If in dictionary mode, get a new shape for the last property after the
+     * removal. We need a fresh shape for all dictionary deletions, even of
+     * lastProp. Otherwise, a shape number could replay and caches might
+     * return deleted DictionaryShapes! See bug 595365. Do this before changing
+     * the object or table, so the remaining removal is infallible.
+     */
+    Shape *spare = NULL;
+    if (inDictionaryMode()) {
+        spare = js_NewGCShape(cx);
+        if (!spare)
+            return false;
+        PodZero(spare);
+    }
+
+    /* If shape has a slot, free its slot number. */
+    if (shape->hasSlot()) {
+        freeSlot(cx, shape->slot());
+        JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
+    }
+
+    /*
      * A dictionary-mode object owns mutable, unique shapes on a non-circular
-     * doubly linked list, optionally hashed by lastProp->table. So we can edit
-     * the list and hash in place.
+     * doubly linked list, hashed by lastProp->table. So we can edit the list
+     * and hash in place.
      */
     if (inDictionaryMode()) {
-        PropertyTable *table = lastProp->hasTable() ? lastProp->getTable() : NULL;
+        PropertyTable *table = lastProp->getTable();
 
         if (SHAPE_HAD_COLLISION(*spp)) {
-            JS_ASSERT(table);
             *spp = SHAPE_REMOVED;
             ++table->removedCount;
             --table->entryCount;
         } else {
-            if (table) {
-                *spp = NULL;
-                --table->entryCount;
+            *spp = NULL;
+            --table->entryCount;
 
 #ifdef DEBUG
-                /*
-                 * Check the consistency of the table but limit the number of
-                 * checks not to alter significantly the complexity of the
-                 * delete in debug builds, see bug 534493.
-                 */
-                const Shape *aprop = lastProp;
-                for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
-                    JS_ASSERT_IF(aprop != shape, nativeContains(cx, *aprop));
+            /*
+             * Check the consistency of the table but limit the number of
+             * checks not to alter significantly the complexity of the
+             * delete in debug builds, see bug 534493.
+             */
+            const Shape *aprop = lastProp;
+            for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
+                JS_ASSERT_IF(aprop != shape, nativeContains(cx, *aprop));
 #endif
-            }
         }
 
-        /*
-         * Remove shape from its non-circular doubly linked list, setting this
-         * object's OWN_SHAPE flag so the updateShape(cx) further below will
-         * generate a fresh shape id for this object, distinct from the id of
-         * any shape in the list. We need a fresh shape for all deletions, even
-         * of lastProp. Otherwise, a shape number could replay and caches might
-         * return get deleted DictionaryShapes! See bug 595365.
-         */
-        flags |= OWN_SHAPE;
-
+        /* Remove shape from its non-circular doubly linked list. */
         Shape *oldLastProp = lastProp;
         shape->removeFromDictionary(this);
-        if (table) {
-            if (shape == oldLastProp) {
-                JS_ASSERT(shape->getTable() == table);
-                JS_ASSERT(shape->parent == lastProp);
-                JS_ASSERT(shape->slotSpan >= lastProp->slotSpan);
-                JS_ASSERT_IF(hadSlot, shape->slot + 1 <= shape->slotSpan);
 
-                /*
-                 * Maintain slot freelist consistency. Slot numbers on the
-                 * freelist are less than lastProp->slotSpan; so if the
-                 * freelist is non-empty, then lastProp->slotSpan may not
-                 * decrease.
-                 */
-                if (table->freelist != SHAPE_INVALID_SLOT) {
-                    lastProp->slotSpan = shape->slotSpan;
+        /* Hand off table from old to new lastProp. */
+        oldLastProp->handoffTable(lastProp);
 
-                    /*
-                     * Add the slot to the freelist if it wasn't added in
-                     * freeSlot and it is not a reserved slot.
-                     */
-                    if (hadSlot && !addedToFreelist && JSSLOT_FREE(clasp) <= shape->slot) {
-                        setSlot(shape->slot, PrivateUint32Value(table->freelist));
-                        table->freelist = shape->slot;
-                    }
-                }
-            }
+        /* Generate a new shape for the object, infallibly. */
+        JS_ALWAYS_TRUE(generateOwnShape(cx, spare));
 
-            /* Hand off table from old to new lastProp. */
-            oldLastProp->setTable(NULL);
-            lastProp->setTable(table);
-        }
+        /* Consider shrinking table if its load factor is <= .25. */
+        uint32 size = table->capacity();
+        if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2)
+            (void) table->change(-1, cx);
     } else {
         /*
          * Non-dictionary-mode property tables are shared immutables, so all we
          * need do is retract lastProp and we'll either get or else lazily make
          * via a later hashify the exact table for the new property lineage.
          */
         JS_ASSERT(shape == lastProp);
         removeLastProperty();
     }
-    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)
-            (void) table->change(-1, cx);
-    }
-
-    /* Also, consider shrinking object slots if 25% or more are unused. */
+    /* Consider shrinking object slots if 25% or more are unused. */
     if (hasSlotsArray()) {
         JS_ASSERT(slotSpan() <= numSlots());
         if ((slotSpan() + (slotSpan() >> 2)) < numSlots())
             shrinkSlots(cx, slotSpan());
     }
 
     CHECK_SHAPE_CONSISTENCY(this);
     return true;
@@ -1125,138 +1037,259 @@ JSObject::clear(JSContext *cx)
         shape = shape->parent;
         JS_ASSERT(inDictionaryMode() == shape->inDictionary());
     }
     JS_ASSERT(shape->isEmptyShape());
 
     if (inDictionaryMode())
         shape->listp = &lastProp;
 
-    /*
-     * 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);
     CHECK_SHAPE_CONSISTENCY(this);
 }
 
 void
 JSObject::rollbackProperties(JSContext *cx, uint32 slotSpan)
 {
     /* Remove properties from this object until it has a matching slot span. */
     JS_ASSERT(!inDictionaryMode() && !hasSlotsArray() && slotSpan <= this->slotSpan());
     while (this->slotSpan() != slotSpan) {
-        JS_ASSERT(lastProp->hasSlot() && getSlot(lastProp->slot).isUndefined());
+        JS_ASSERT(lastProp->hasSlot() && getSlot(lastProp->slot()).isUndefined());
         removeLastProperty();
     }
-    updateShape(cx);
 }
 
-void
-JSObject::generateOwnShape(JSContext *cx)
+bool
+JSObject::generateOwnShape(JSContext *cx, Shape *newShape)
 {
 #ifdef JS_TRACER
     JS_ASSERT_IF(!parent && JS_ON_TRACE(cx), JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit);
     LeaveTraceIfGlobalObject(cx, this);
 
     /*
      * If we are recording, here is where we forget already-guarded shapes.
      * Any subsequent property operation upon object on the trace currently
      * being recorded will re-guard (and re-memoize).
      */
     if (TraceRecorder *tr = TRACE_RECORDER(cx))
         tr->forgetGuardedShapesForObject(this);
 #endif
 
-    setOwnShape(js_GenerateShape(cx));
-}
+    if (!inDictionaryMode() && !toDictionaryMode(cx))
+        return false;
+
+    if (!newShape) {
+        newShape = js_NewGCShape(cx);
+        if (!newShape)
+            return false;
+    }
+
+    PropertyTable *table = lastProp->getTable();
+    Shape **spp = lastProp->isEmptyShape() ? NULL : table->search(lastProp->maybePropid(), false);
 
-void
-JSObject::deletingShapeChange(JSContext *cx, const Shape &shape)
-{
-    JS_ASSERT(!JSID_IS_VOID(shape.propid));
-    generateOwnShape(cx);
+    Shape *oldShape = lastProp;
+    newShape->initDictionaryShape(*oldShape, &lastProp);
+
+    JS_ASSERT(newShape->parent == oldShape);
+    oldShape->removeFromDictionary(this);
+
+    oldShape->handoffTable(newShape);
+
+    if (spp) {
+        if (SHAPE_HAD_COLLISION(*spp))
+            SHAPE_FLAG_COLLISION(spp, newShape);
+        else
+            *spp = newShape;
+    }
+    return true;
 }
 
 const Shape *
 JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
 {
     const Shape *result = &shape;
 
-    JS_ASSERT(!JSID_IS_VOID(shape.propid));
+    if (!inDictionaryMode() && !toDictionaryMode(cx))
+        return NULL;
+
+    Shape *spare = js_NewGCShape(cx);
+    if (!spare)
+        return NULL;
+
     if (shape.isMethod()) {
 #ifdef DEBUG
-        const Value &prev = nativeGetSlot(shape.slot);
-        JS_ASSERT(shape.methodObject() == prev.toObject());
         JS_ASSERT(canHaveMethodBarrier());
-        JS_ASSERT(hasMethodBarrier());
-        JS_ASSERT(!shape.rawSetter);
+        JS_ASSERT(!shape.setter());
+        JS_ASSERT(!shape.hasShortID());
 #endif
 
         /*
          * Pass null to make a stub getter, but pass along shape.rawSetter to
          * preserve watchpoints. Clear Shape::METHOD from flags as we are
          * despecializing from a method memoized in the property tree to a
          * plain old function-valued property.
          */
-        result = putProperty(cx, shape.propid, NULL, shape.rawSetter, shape.slot,
+        result = putProperty(cx, shape.propid(), NULL, NULL, shape.slot(),
                              shape.attrs,
                              shape.getFlags() & ~Shape::METHOD,
-                             shape.shortid);
+                             0);
         if (!result)
             return NULL;
     }
 
-    if (branded()) {
-        uintN thrashCount = getMethodThrashCount();
-        if (thrashCount < JSObject::METHOD_THRASH_COUNT_MAX) {
-            ++thrashCount;
-            setMethodThrashCount(thrashCount);
-            if (thrashCount == JSObject::METHOD_THRASH_COUNT_MAX) {
-                unbrand(cx);
-                return result;
-            }
-        }
-    }
-
-    generateOwnShape(cx);
+    JS_ALWAYS_TRUE(generateOwnShape(cx, spare));
     return result;
 }
 
 bool
-JSObject::methodShapeChange(JSContext *cx, uint32 slot)
+JSObject::protoShapeChange(JSContext *cx)
+{
+    return generateOwnShape(cx);
+}
+
+bool
+JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
+{
+    return generateOwnShape(cx);
+}
+
+/* static */ inline HashNumber
+JSCompartment::BaseShapeEntry::hash(const js::BaseShape *base)
+{
+    JS_ASSERT(!base->isOwned() && !base->table());
+
+    JSDHashNumber hash = base->flags;
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->clasp);
+    if (base->rawGetter)
+        hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->rawGetter);
+    if (base->rawSetter)
+        hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(base->rawSetter);
+    return hash;
+}
+
+/* static */ inline bool
+JSCompartment::BaseShapeEntry::match(const BaseShapeEntry &entry, const BaseShape *lookup)
+{
+    BaseShape *key = entry.base;
+    JS_ASSERT(!key->isOwned() && !lookup->isOwned());
+
+    return key->flags == lookup->flags
+        && key->clasp == lookup->clasp
+        && key->getterObj == lookup->getterObj
+        && key->setterObj == lookup->setterObj;
+}
+
+static inline JSCompartment::BaseShapeEntry *
+LookupBaseShape(JSContext *cx, const BaseShape &base)
 {
-    if (!hasMethodBarrier()) {
-        generateOwnShape(cx);
-    } else {
-        for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
-            const Shape &shape = r.front();
-            JS_ASSERT(!JSID_IS_VOID(shape.propid));
-            if (shape.slot == slot)
-                return methodShapeChange(cx, shape) != NULL;
-        }
-    }
-    return true;
+    JSCompartment::BaseShapeSet &table = cx->compartment->baseShapes;
+
+    if (!table.initialized() && !table.init())
+        return false;
+
+    JSCompartment::BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
+    if (p)
+        return &const_cast<JSCompartment::BaseShapeEntry &>(*p);
+
+    BaseShape *nbase = js_NewGCBaseShape(cx);
+    if (!nbase)
+        return false;
+    new (nbase) BaseShape(base);
+
+    JSCompartment::BaseShapeEntry entry;
+    entry.base = nbase;
+    entry.empty = NULL;
+
+    p = table.lookupForAdd(&base);
+    if (!table.add(p, entry))
+        return NULL;
+
+    return &const_cast<JSCompartment::BaseShapeEntry &>(*p);
+}
+
+/* static */ BaseShape *
+BaseShape::lookup(JSContext *cx, const BaseShape &base)
+{
+    JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, base);
+    return entry ? entry->base : NULL;
+}
+
+/* static */ EmptyShape *
+BaseShape::lookupEmpty(JSContext *cx, Class *clasp)
+{
+    js::BaseShape base(clasp);
+    JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, base);
+    if (!entry)
+        return NULL;
+    if (entry->empty)
+        return entry->empty;
+
+    entry->empty = (EmptyShape *) JS_PROPERTY_TREE(cx).newShape(cx);
+    if (!entry->empty)
+        return NULL;
+    return new (entry->empty) EmptyShape(entry->base);
 }
 
 void
-JSObject::protoShapeChange(JSContext *cx)
+JSCompartment::sweepBaseShapeTable(JSContext *cx)
 {
-    generateOwnShape(cx);
+    if (baseShapes.initialized()) {
+        for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
+            JSCompartment::BaseShapeEntry &entry =
+                const_cast<JSCompartment::BaseShapeEntry &>(e.front());
+            if (!entry.base->isMarked())
+                e.removeFront();
+            else if (entry.empty && !entry.empty->isMarked())
+                entry.empty = NULL;
+        }
+    }
 }
 
 void
-JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
+BaseShape::finalize(JSContext *cx)
+{
+    if (table_) {
+        cx->delete_(table_);
+        table_ = NULL;
+    }
+}
+
+/* static */ bool
+Shape::setExtensibleParents(JSContext *cx, Shape **pshape)
 {
-    JS_ASSERT(!JSID_IS_VOID(shape.propid));
-    generateOwnShape(cx);
+    Shape *shape = *pshape;
+    JS_ASSERT(!shape->inDictionary());
+
+    BaseShape base(shape->getClass(), shape->attrs, shape->getter(), shape->setter());
+    base.flags |= BaseShape::EXTENSIBLE_PARENTS;
+    BaseShape *nbase = BaseShape::lookup(cx, base);
+    if (!nbase)
+        return false;
+
+    Shape child(nbase, shape->maybePropid(), shape->maybeSlot(),
+                shape->attrs, shape->flags, shape->maybeShortid());
+    Shape *newShape;
+    if (shape->parent) {
+        newShape = JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, child);
+        if (!newShape)
+            return false;
+    } else {
+        newShape = js_NewGCShape(cx);
+        if (!newShape)
+            return false;
+        new (newShape) Shape(child);
+    }
+
+    *pshape = newShape;
+
+    return true;
 }
 
 bool
-JSObject::globalObjectOwnShapeChange(JSContext *cx)
+Bindings::setExtensibleParents(JSContext *cx)
 {
-    generateOwnShape(cx);
-    return !js_IsPropertyCacheDisabled(cx);
+    if (!ensureShape(cx))
+        return false;
+    return Shape::setExtensibleParents(cx, &lastBinding);
 }
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -123,22 +123,16 @@
  * skipping nodes that lack entries.
  *
  * What if we add Y again?  X->Y->Z->Y is wrong and we'll enumerate Y twice.
  * Therefore we must fork in such a case if not earlier, or do something else.
  * We used to fork on the theory that set after delete is rare, but the Web is
  * a harsh mistress, and we now convert the scope to a "dictionary" on first
  * delete, to avoid O(n^2) growth in the property tree.
  *
- * What about thread safety?  If the property tree operations done by requests
- * are find-node and insert-node, then the only hazard is duplicate insertion.
- * This is harmless except for minor bloat.  When all requests have ended or
- * been suspended, the GC is free to sweep the tree after marking all nodes
- * reachable from scopes, performing remove-node operations as needed.
- *
  * Is the property tree worth it compared to property storage in each table's
  * entries?  To decide, we must find the relation <> between the words used
  * with a property tree and the words required without a tree.
  *
  * Model all scopes as one super-scope of capacity T entries (T a power of 2).
  * Let alpha be the load factor of this double hash-table.  With the property
  * tree, each entry in the table is a word-sized pointer to a node that can be
  * shared by many scopes.  But all such pointers are overhead compared to the
@@ -175,18 +169,17 @@
  *
  * We ensure that alpha <= .75, so the property tree wins if beta < .83_.  An
  * average beta from recent Mozilla browser startups was around .6.
  *
  * Can we reduce L?  Observe that the property tree degenerates into a list of
  * lists if at most one property Y follows X in all scopes.  In or near such a
  * case, we waste a word on the right-sibling link outside of the root ply of
  * the tree.  Note also that the root ply tends to be large, so O(n^2) growth
- * searching it is likely, indicating the need for hashing (but with increased
- * thread safety costs).
+ * searching it is likely, indicating the need for hashing.
  *
  * If only K out of N nodes in the property tree have more than one child, we
  * could eliminate the sibling link and overlay a children list or hash-table
  * pointer on the leftmost-child link (which would then be either null or an
  * only-child link; the overlay could be tagged in the low bit of the pointer,
  * or flagged elsewhere in the property tree node, although such a flag must
  * not be considered when comparing node labels during tree search).
  *
@@ -203,17 +196,17 @@
  * scopes are looked up fewer than three times;  in these cases, initializing
  * scope->table isn't worth it.  So instead of always allocating scope->table,
  * we leave it null while initializing all the other scope members as if it
  * were non-null and minimal-length.  Until a scope is searched
  * MAX_LINEAR_SEARCHES times, we use linear search from obj->lastProp to find a
  * given id, and save on the time and space overhead of creating a hash table.
  */
 
-#define SHAPE_INVALID_SLOT              0xffffffff
+#define SHAPE_INVALID_SLOT                  0xffffffff
 
 namespace js {
 
 /*
  * Shapes use multiplicative hashing, _a la_ jsdhash.[ch], but specialized to
  * minimize footprint.  But if a Shape lineage has been searched fewer than
  * MAX_LINEAR_SEARCHES times, we use linear search and avoid allocating
  * scope->table.
@@ -286,201 +279,249 @@ struct PropertyTable {
 } /* namespace js */
 
 struct JSObject;
 
 namespace js {
 
 class PropertyTree;
 
-static inline PropertyOp
-CastAsPropertyOp(js::Class *clasp)
-{
-    return JS_DATA_TO_FUNC_PTR(PropertyOp, clasp);
-}
-
 /*
  * Reuse the API-only JSPROP_INDEX attribute to mean shadowability.
  */
 #define JSPROP_SHADOWABLE       JSPROP_INDEX
 
+/*
+ * Shapes encode information about both a property lineage *and* a particular
+ * property. This information is split across the Shape and the BaseShape
+ * at shape->base(). Both Shape and BaseShape can be either owned or unowned
+ * by, respectively, the Object or Shape referring to them.
+ *
+ * Owned Shapes are used in dictionary objects, and form a doubly linked list
+ * whose entries are all owned by that dictionary. Unowned Shapes are all in
+ * the property tree.
+ *
+ * Owned BaseShapes are used for shapes which have property tables, including
+ * the last properties in all dictionaries. Unowned BaseShapes compactly store
+ * information common to many shapes.
+ *
+ * All combinations of owned/unowned Shapes/BaseShapes are possible:
+ *
+ * Owned Shape, Owned BaseShape:
+ *
+ *     Last property in a dictionary object. The BaseShape is transferred from
+ *     property to property as the object's last property changes.
+ *
+ * Owned Shape, Unowned BaseShape:
+ *
+ *     Property in a dictionary object other than the last one.
+ *
+ * Unowned Shape, Owned BaseShape:
+ *
+ *     Property in the property tree which has a property table.
+ *
+ * Unowned Shape, Unowned BaseShape:
+ *
+ *     Property in the property tree which does not have a property table.
+ */
+
+struct BaseShape : public js::gc::Cell
+{
+#if JS_BITS_PER_WORD == 32
+    void *padding;
+#endif
+
+    enum {
+        /* Owned by the referring shape. */
+        OWNED_SHAPE       = 0x1,
+
+        /* getterObj/setterObj are active in unions below. */
+        HAS_GETTER_OBJECT = 0x2,
+        HAS_SETTER_OBJECT = 0x4,
+
+        /*
+         * For the last property in static Block objects and Bindings,
+         * indicates that cloned Block or Call objects need unique shapes.
+         * See Shape::extensibleParents.
+         */
+        EXTENSIBLE_PARENTS = 0x8
+    };
+
+    uint32              flags;          /* Vector of above flags. */
+    uint32              slotSpan;       /* Object slot span for BaseShapes at
+                                         * dictionary last properties. */
+    Class               *clasp;         /* Class of referring shape/object. */
+
+    union {
+        js::PropertyOp  rawGetter;      /* getter hook for shape */
+        JSObject        *getterObj;     /* user-defined callable "get" object or
+                                           null if shape->hasGetterValue() */
+    };
+
+    union {
+        js::StrictPropertyOp rawSetter; /* setter hook for shape */
+        JSObject        *setterObj;     /* user-defined callable "set" object or
+                                           null if shape->hasSetterValue() */
+    };
+
+    BaseShape           *base;          /* For owned BaseShapes, the identical
+                                         * unowned BaseShape. */
+
+  private:
+    PropertyTable       *table_;        /* For owned BaseShapes, the shape's
+                                         * property table. */
+
+  public:
+    void finalize(JSContext *cx);
+
+    BaseShape(Class *clasp) {
+        PodZero(this);
+        this->clasp = clasp;
+    }
+
+    BaseShape(Class *clasp, uint8 attrs, js::PropertyOp rawGetter, js::StrictPropertyOp rawSetter) {
+        PodZero(this);
+        this->clasp = clasp;
+        this->rawGetter = rawGetter;
+        this->rawSetter = rawSetter;
+        if ((attrs & JSPROP_GETTER) && rawGetter)
+            flags |= HAS_GETTER_OBJECT;
+        if ((attrs & JSPROP_SETTER) && rawSetter)
+            flags |= HAS_SETTER_OBJECT;
+    }
+
+    BaseShape(BaseShape *base, PropertyTable *table, uint32 slotSpan) {
+        *this = *base;
+        setOwned(base);
+        setTable(table);
+        this->slotSpan = slotSpan;
+    }
+
+    bool isOwned() const { return !!(flags & OWNED_SHAPE); }
+    void setOwned(BaseShape *base) { flags |= OWNED_SHAPE; this->base = base; }
+
+    PropertyTable *table() const { JS_ASSERT_IF(table_, isOwned()); return table_; }
+    void setTable(PropertyTable *table) { JS_ASSERT(isOwned()); table_ = table; }
+
+    /* Lookup base shapes from the compartment's baseShapes table. */
+    static BaseShape *lookup(JSContext *cx, const BaseShape &base);
+    static EmptyShape *lookupEmpty(JSContext *cx, Class *clasp);
+};
+
 struct Shape : public js::gc::Cell
 {
     friend struct ::JSObject;
     friend struct ::JSFunction;
     friend class js::PropertyTree;
     friend class js::Bindings;
     friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
 
-    mutable uint32      shapeid;        /* shape identifier */
-    uint32              slotSpan;       /* one more than maximum live slot number */
+  protected:
+    BaseShape           *base_;
+    jsid                propid_;
 
     /* 
      * numLinearSearches starts at zero and is incremented initially on each
      * search() call.  Once numLinearSearches reaches MAX_LINEAR_SEARCHES
      * (which is a small integer), the table is created on the next search()
-     * call, and the table pointer will be easily distinguishable from a small
-     * integer.  The table can also be created when hashifying for dictionary
+     * call.  The table can also be created when hashifying for dictionary
      * mode.
      */
-    union {
-        mutable size_t numLinearSearches;
-        mutable js::PropertyTable *table;
-    };
+    uint8               numLinearSearches:3;
 
-    inline void freeTable(JSContext *cx);
+    uint32              slot_:29;       /* abstract index in object slots */
 
-    jsid                propid;
+    static const size_t SLOT_BITS = 29;
 
-  protected:
-    union {
-        PropertyOp      rawGetter;      /* getter and setter hooks or objects */
-        JSObject        *getterObj;     /* user-defined callable "get" object or
-                                           null if shape->hasGetterValue(); or
-                                           joined function object if METHOD flag
-                                           is set. */
-        js::Class       *clasp;         /* prototype class for empty scope */
-    };
+    uint8               attrs;          /* attributes, see jsapi.h JSPROP_* */
+    uint8               flags;          /* flags, see below for defines */
+    int16               shortid_;       /* tinyid, or local arg/var index */
 
-    union {
-        StrictPropertyOp rawSetter;     /* getter is JSObject* and setter is 0
-                                           if shape->isMethod() */
-        JSObject        *setterObj;     /* user-defined callable "set" object or
-                                           null if shape->hasSetterValue() */
-    };
-
-  public:
-    uint32              slot;           /* abstract index in object slots */
-  private:
-    uint8               attrs;          /* attributes, see jsapi.h JSPROP_* */
-    mutable uint8       flags;          /* flags, see below for defines */
-  public:
-    int16               shortid;        /* tinyid, or local arg/var index */
-
-  protected:
-    mutable js::Shape   *parent;        /* parent node, reverse for..in order */
+    js::Shape   *parent;        /* parent node, reverse for..in order */
     /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
     union {
-        mutable js::KidsPointer kids;   /* null, single child, or a tagged ptr
-                                           to many-kids data structure */
-        mutable js::Shape **listp;      /* dictionary list starting at lastProp
-                                           has a double-indirect back pointer,
-                                           either to shape->parent if not last,
-                                           else to obj->lastProp */
+        js::KidsPointer kids;   /* null, single child, or a tagged ptr
+                                   to many-kids data structure */
+        js::Shape **listp;      /* dictionary list starting at lastProp
+                                   has a double-indirect back pointer,
+                                   either to shape->parent if not last,
+                                   else to obj->lastProp */
     };
 
     static inline js::Shape **search(JSContext *cx, js::Shape **startp, jsid id,
                                      bool adding = false);
-    static js::Shape *newDictionaryShape(JSContext *cx, const js::Shape &child, js::Shape **listp);
     static js::Shape *newDictionaryList(JSContext *cx, js::Shape **listp);
 
-    inline void removeFromDictionary(JSObject *obj) const;
+    inline void removeFromDictionary(JSObject *obj);
     inline void insertIntoDictionary(js::Shape **dictp);
 
-    js::Shape *getChild(JSContext *cx, const js::Shape &child, js::Shape **listp);
+    inline void initDictionaryShape(const js::Shape &child, js::Shape **dictp);
+
+    js::Shape *getChild(JSContext *cx, const js::Shape &child, js::Shape **listp,
+                        bool allowDictionary = true);
 
     bool hashify(JSContext *cx);
-
-    void setTable(js::PropertyTable *t) const {
-        JS_ASSERT_IF(t && t->freelist != SHAPE_INVALID_SLOT, t->freelist < slotSpan);
-        table = t;
-    }
+    void handoffTable(Shape *newShape);
 
-    /*
-     * Setter for parent. The challenge is to maintain JSObjectMap::slotSpan in
-     * the face of arbitrary slot order.
-     *
-     * By induction, an empty shape has a slotSpan member correctly computed as
-     * JSCLASS_FREE(clasp) -- see EmptyShape's constructor in jsscopeinlines.h.
-     * This is the basis case, where p is null.
-     *
-     * Any child shape, whether in a shape tree or in a dictionary list, must
-     * have a slotSpan either one greater than its slot value (if the child's
-     * slot is SHAPE_INVALID_SLOT, this will yield 0; the static assertion
-     * below enforces this), or equal to its parent p's slotSpan, whichever is
-     * greater. This is the inductive step.
-     *
-     * If we maintained shape paths such that parent slot was always one less
-     * than child slot, possibly with an exception for SHAPE_INVALID_SLOT slot
-     * values where we would use another way of computing slotSpan based on the
-     * PropertyTable (as JSC does), then we would not need to store slotSpan in
-     * Shape (to be precise, in its base struct, JSobjectMap).
-     *
-     * But we currently scramble slots along shape paths due to resolve-based
-     * creation of shapes mapping reserved slots, and we do not have the needed
-     * PropertyTable machinery to use as an alternative when parent slot is not
-     * one less than child slot. This machinery is neither simple nor free, as
-     * it must involve creating a table for any slot-less transition and then
-     * pinning the table to its shape.
-     *
-     * Use of 'delete' can scramble slots along the shape lineage too, although
-     * it always switches the target object to dictionary mode, so the cost of
-     * a pinned table is less onerous.
-     *
-     * Note that allocating a uint32 slotSpan member in JSObjectMap takes no
-     * net extra space on 64-bit targets (it packs with shape). And on 32-bit
-     * targets, adding slotSpan to JSObjectMap takes no gross extra space,
-     * because Shape rounds up to an even number of 32-bit words (required for
-     * GC-thing and js::Value allocation in any event) on 32-bit targets.
-     *
-     * So in terms of space, we can afford to maintain both slotSpan and slot,
-     * but it might be better if we eliminated slotSpan using slot combined
-     * with an auxiliary mechanism based on table.
-     */
     void setParent(js::Shape *p) {
-        JS_STATIC_ASSERT(uint32(SHAPE_INVALID_SLOT) == ~uint32(0));
-        if (p)
-            slotSpan = JS_MAX(p->slotSpan, slot + 1);
-        JS_ASSERT(slotSpan < JSObject::NSLOTS_LIMIT);
+        JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(), p->slot_ <= slot_);
+        JS_ASSERT_IF(p && !inDictionary(), hasSlot() == (p->slot_ != slot_));
         parent = p;
     }
 
-  public:
-    static JS_FRIEND_DATA(Shape) sharedNonNative;
+    bool ensureOwnBaseShape(JSContext *cx) {
+        if (base()->isOwned())
+            return true;
+        return makeOwnBaseShape(cx);
+    }
 
+    bool makeOwnBaseShape(JSContext *cx);
+
+  public:
     bool hasTable() const {
-        /* A valid pointer should be much bigger than MAX_LINEAR_SEARCHES. */
-        return numLinearSearches > PropertyTable::MAX_LINEAR_SEARCHES;
+        return base()->table() != NULL;
     }
 
     js::PropertyTable *getTable() const {
         JS_ASSERT(hasTable());
-        return table;
+        return base()->table();
     }
 
     size_t sizeOfPropertyTable(JSUsableSizeFun usf) const {
         return hasTable() ? getTable()->sizeOf(usf) : 0;
     }
 
     size_t sizeOfKids(JSUsableSizeFun usf) const {
         /* Nb: |countMe| is true because the kids HashTable is on the heap. */
         return (!inDictionary() && kids.isHash())
              ? kids.toHash()->sizeOf(usf, /* countMe */true)
              : 0;
     }
 
-    bool isNative() const { return this != &sharedNonNative; }
+    bool isNative() const {
+        JS_ASSERT(!(flags & NON_NATIVE) == getClass()->isNative());
+        return !(flags & NON_NATIVE);
+    }
 
     const js::Shape *previous() const {
         return parent;
     }
 
     class Range {
       protected:
         friend struct Shape;
 
         const Shape *cursor;
         const Shape *end;
 
       public:
         Range(const Shape *shape) : cursor(shape) { }
 
         bool empty() const {
-            JS_ASSERT_IF(!cursor->parent, JSID_IS_EMPTY(cursor->propid));
-            return !cursor->parent;
+            return cursor->isEmptyShape();
         }
 
         const Shape &front() const {
             JS_ASSERT(!empty());
             return *cursor;
         }
 
         void popFront() {
@@ -488,118 +529,163 @@ struct Shape : public js::gc::Cell
             cursor = cursor->parent;
         }
     };
 
     Range all() const {
         return Range(this);
     }
 
+    Class *getClass() const { return base()->clasp; }
+
   protected:
     /*
      * Implementation-private bits stored in shape->flags. See public: enum {}
      * flags further below, which were allocated FCFS over time, so interleave
      * with these bits.
      */
     enum {
-        SHARED_EMPTY    = 0x01,
+        /* Property is placeholder for a non-native class. */
+        NON_NATIVE      = 0x01,
 
         /* Property stored in per-object dictionary, not shared property tree. */
         IN_DICTIONARY   = 0x02,
 
-        /* Prevent unwanted mutation of shared Bindings::lastBinding nodes. */
-        FROZEN          = 0x04,
-
-        UNUSED_BITS     = 0x38
+        UNUSED_BITS     = 0x3C
     };
 
-    Shape(jsid id, PropertyOp getter, StrictPropertyOp setter, uint32 slot, uintN attrs,
-          uintN flags, intN shortid, uint32 shape = INVALID_SHAPE, uint32 slotSpan = 0);
+    Shape(BaseShape *base, jsid id, uint32 slot, uintN attrs, uintN flags, intN shortid);
 
     /* Used by EmptyShape (see jsscopeinlines.h). */
-    Shape(JSCompartment *comp, Class *aclasp);
-
-    /* Used by sharedNonNative. */
-    explicit Shape(uint32 shape);
+    Shape(BaseShape *base);
 
-    bool inDictionary() const   { return (flags & IN_DICTIONARY) != 0; }
-    bool frozen() const         { return (flags & FROZEN) != 0; }
-    void setFrozen()            { flags |= FROZEN; }
-
-    bool isEmptyShape() const   { JS_ASSERT_IF(!parent, JSID_IS_EMPTY(propid)); return !parent; }
+    bool inDictionary() const { return (flags & IN_DICTIONARY) != 0; }
 
   public:
     /* Public bits stored in shape->flags. */
     enum {
         HAS_SHORTID     = 0x40,
         METHOD          = 0x80,
         PUBLIC_FLAGS    = HAS_SHORTID | METHOD
     };
 
     uintN getFlags() const  { return flags & PUBLIC_FLAGS; }
     bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
-    bool isMethod() const   { return (flags & METHOD) != 0; }
-
-    JSObject &methodObject() const { JS_ASSERT(isMethod()); return *getterObj; }
 
-    PropertyOp getter() const { return rawGetter; }
-    bool hasDefaultGetter() const  { return !rawGetter; }
-    PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return rawGetter; }
-    JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return getterObj; }
+    /*
+     * A scope has a method barrier when some compiler-created "null closure"
+     * function objects (functions that do not use lexical bindings above their
+     * scope, only free variable names) that have a correct JSSLOT_PARENT value
+     * thanks to the COMPILE_N_GO optimization are stored in objects without
+     * cloning.
+     *
+     * The de-facto standard JS language requires each evaluation of such a
+     * closure to result in a unique (according to === and observable effects)
+     * function object. When storing a function to a property, we use method
+     * shapes to speculate that these effects will never be observed: the
+     * property will only be used in calls, and f.callee will not be used
+     * to get a handle on the object.
+     *
+     * If either a non-call use or callee access occurs, then the function is
+     * cloned and the object is reshaped with a non-method property.
+     *
+     * Note that method shapes do not imply the object has a particular
+     * uncloned function, just that the object has *some* uncloned function
+     * in the shape's slot.
+     */
+    bool isMethod() const {
+        JS_ASSERT_IF(flags & METHOD, !base()->rawGetter);
+        return (flags & METHOD) != 0;
+    }
+
+    PropertyOp getter() const { return base()->rawGetter; }
+    bool hasDefaultGetterOrIsMethod() const { return !base()->rawGetter; }
+    bool hasDefaultGetter() const  { return !base()->rawGetter && !isMethod(); }
+    PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; }
+    JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; }
 
     // Per ES5, decode null getterObj as the undefined value, which encodes as null.
     Value getterValue() const {
         JS_ASSERT(hasGetterValue());
-        return getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue();
+        return base()->getterObj ? js::ObjectValue(*base()->getterObj) : js::UndefinedValue();
     }
 
     Value getterOrUndefined() const {
-        return hasGetterValue() && getterObj ? js::ObjectValue(*getterObj) : js::UndefinedValue();
+        return (hasGetterValue() && base()->getterObj)
+            ? ObjectValue(*base()->getterObj)
+            : UndefinedValue();
     }
 
-    StrictPropertyOp setter() const { return rawSetter; }
-    bool hasDefaultSetter() const  { return !rawSetter; }
-    StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return rawSetter; }
-    JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return setterObj; }
+    StrictPropertyOp setter() const { return base()->rawSetter; }
+    bool hasDefaultSetter() const  { return !base()->rawSetter; }
+    StrictPropertyOp setterOp() const { JS_ASSERT(!hasSetterValue()); return base()->rawSetter; }
+    JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return base()->setterObj; }
 
     // Per ES5, decode null setterObj as the undefined value, which encodes as null.
     Value setterValue() const {
         JS_ASSERT(hasSetterValue());
-        return setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue();
+        return base()->setterObj ? js::ObjectValue(*base()->setterObj) : js::UndefinedValue();
     }
 
     Value setterOrUndefined() const {
-        return hasSetterValue() && setterObj ? js::ObjectValue(*setterObj) : js::UndefinedValue();
+        return (hasSetterValue() && base()->setterObj)
+            ? ObjectValue(*base()->setterObj)
+            : UndefinedValue();
     }
 
     inline JSDHashNumber hash() const;
     inline bool matches(const js::Shape *p) const;
-    inline bool matchesParamsAfterId(PropertyOp agetter, StrictPropertyOp asetter,
+    inline bool matchesParamsAfterId(BaseShape *base,
                                      uint32 aslot, uintN aattrs, uintN aflags,
                                      intN ashortid) const;
 
     bool get(JSContext* cx, JSObject *receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const;
     bool set(JSContext* cx, JSObject *obj, bool strict, js::Value* vp) const;
 
+    BaseShape *base() const { return base_; }
+
+    /*
+     * For properties which haven't been fully initialized, empty shapes and
+     * slot-less properties under empty shapes.
+     */
+    bool hasMissingSlot() const { return slot_ == (SHAPE_INVALID_SLOT >> (32 - SLOT_BITS)); }
+
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
+    uint32 slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
+    uint32 maybeSlot() const { return slot_; }
+
+    bool isEmptyShape() const {
+        JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
+        return JSID_IS_EMPTY(propid_);
+    }
+
+    jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); }
+    jsid maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
+
+    int16 shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
+    int16 maybeShortid() const { return shortid_; }
+
+    /*
+     * If SHORTID is set in shape->flags, we use shape->shortid rather
+     * than id when calling shape's getter or setter.
+     */
+    jsid getUserId() const {
+        return hasShortID() ? INT_TO_JSID(shortid()) : propid();
+    }
 
     uint8 attributes() const { return attrs; }
     bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
     bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
     bool writable() const {
         // JS_ASSERT(isDataDescriptor());
         return (attrs & JSPROP_READONLY) == 0;
     }
     bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
     bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
 
-    bool hasDefaultGetterOrIsMethod() const {
-        return hasDefaultGetter() || isMethod();
-    }
-
     bool isDataDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
     }
     bool isAccessorDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
     }
 
     /*
@@ -608,16 +694,62 @@ struct Shape : public js::gc::Cell
      * the directly referenced object will have the same getter and setter as
      * the prototype property. See bug 552432.
      */
     bool shadowable() const {
         JS_ASSERT_IF(isDataDescriptor(), writable());
         return hasSlot() || (attrs & JSPROP_SHADOWABLE);
     }
 
+    /*
+     * Sometimes call objects and run-time block objects need unique shapes, but
+     * sometimes they don't.
+     *
+     * Property cache entries only record the shapes of the first and last
+     * objects along the search path, so if the search traverses more than those
+     * two objects, then those first and last shapes must determine the shapes
+     * of everything else along the path. The js_PurgeScopeChain stuff takes
+     * care of making this work, but that suffices only because we require that
+     * start points with the same shape have the same successor object in the
+     * search path --- a cache hit means the starting shapes were equal, which
+     * means the seach path tail (everything but the first object in the path)
+     * was shared, which in turn means the effects of a purge will be seen by
+     * all affected starting search points.
+     *
+     * For call and run-time block objects, the "successor object" is the scope
+     * chain parent. Unlike prototype objects (of which there are usually few),
+     * scope chain parents are created frequently (possibly on every call), so
+     * following the shape-implies-parent rule blindly would lead one to give
+     * every call and block its own shape.
+     *
+     * In many cases, however, it's not actually necessary to give call and
+     * block objects their own shapes, and we can do better. If the code will
+     * always be used with the same global object, and none of the enclosing
+     * call objects could have bindings added to them at runtime (by direct eval
+     * calls or function statements), then we can use a fixed set of shapes for
+     * those objects. You could think of the shapes in the functions' bindings
+     * and compile-time blocks as uniquely identifying the global object(s) at
+     * the end of the scope chain.
+     *
+     * (In fact, some JSScripts we do use against multiple global objects (see
+     * bug 618497), and using the fixed shapes isn't sound there.)
+     *
+     * In deciding whether a call or block has any extensible parents, we
+     * actually only need to consider enclosing calls; blocks are never
+     * extensible, and the other sorts of objects that appear in the scope
+     * chains ('with' blocks, say) are not CacheableNonGlobalScopes.
+     *
+     * If the hasExtensibleParents flag is set for the last property in a
+     * script's bindings or a compiler-generated Block object, then created
+     * Call or Block objects need unique shapes. If the flag is clear, then we
+     * can use lastBinding's shape.
+     */
+    static bool setExtensibleParents(JSContext *cx, Shape **pshape);
+    bool extensibleParents() const { return !!(base()->flags & BaseShape::EXTENSIBLE_PARENTS); }
+
     uint32 entryCount() const {
         if (hasTable())
             return getTable()->entryCount;
 
         const js::Shape *shape = this;
         uint32 count = 0;
         for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront())
             ++count;
@@ -631,25 +763,28 @@ struct Shape : public js::gc::Cell
 
     void finalize(JSContext *cx);
     void removeChild(js::Shape *child);
     void removeChildSlowly(js::Shape *child);
 };
 
 struct EmptyShape : public js::Shape
 {
-    EmptyShape(JSCompartment *comp, js::Class *aclasp);
-
-    js::Class *getClass() const { return clasp; };
+    EmptyShape(BaseShape *base);
 
     static EmptyShape *create(JSContext *cx, js::Class *clasp) {
+        BaseShape lookup(clasp);
+        BaseShape *base = BaseShape::lookup(cx, lookup);
+        if (!base)
+            return NULL;
+
         js::Shape *eprop = JS_PROPERTY_TREE(cx).newShape(cx);
         if (!eprop)
             return NULL;
-        return new (eprop) EmptyShape(cx->compartment, clasp);
+        return new (eprop) EmptyShape(base);
     }
 
     static EmptyShape *ensure(JSContext *cx, js::Class *clasp, EmptyShape **shapep) {
         EmptyShape *shape = *shapep;
         if (!shape) {
             if (!(shape = create(cx, clasp)))
                 return NULL;
             return *shapep = shape;
@@ -682,30 +817,16 @@ struct EmptyShape : public js::Shape
 #define SHAPE_FETCH(spp)                SHAPE_CLEAR_COLLISION(*(spp))
 
 #define SHAPE_CLEAR_COLLISION(shape)                                          \
     ((js::Shape *) (jsuword(shape) & ~SHAPE_COLLISION))
 
 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape)                          \
     (*(spp) = (js::Shape *) (jsuword(shape) | SHAPE_HAD_COLLISION(*(spp))))
 
-/*
- * If SHORTID is set in shape->flags, we use shape->shortid rather
- * than id when calling shape's getter or setter.
- */
-#define SHAPE_USERID(shape)                                                   \
-    ((shape)->hasShortID() ? INT_TO_JSID((shape)->shortid)                    \
-                           : (shape)->propid)
-
-extern uint32
-js_GenerateShape(JSRuntime *rt);
-
-extern uint32
-js_GenerateShape(JSContext *cx);
-
 namespace js {
 
 JS_ALWAYS_INLINE js::Shape **
 Shape::search(JSContext *cx, js::Shape **startp, jsid id, bool adding)
 {
     js::Shape *start = *startp;
     if (start->hasTable())
         return start->getTable()->search(id, adding);
@@ -725,17 +846,17 @@ Shape::search(JSContext *cx, js::Shape *
      * from *startp.
      *
      * We don't use a Range here, or stop at null parent (the empty shape
      * at the end), to avoid an extra load per iteration just to save a
      * load and id test at the end (when missing).
      */
     js::Shape **spp;
     for (spp = startp; js::Shape *shape = *spp; spp = &shape->parent) {
-        if (shape->propid == id)
+        if (shape->maybePropid() == id)
             return spp;
     }
     return spp;
 }
 
 } // namespace js
 
 #ifdef _MSC_VER
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -52,25 +52,16 @@
 
 #include "vm/ArgumentsObject.h"
 #include "vm/StringObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
-inline void
-js::Shape::freeTable(JSContext *cx)
-{
-    if (hasTable()) {
-        cx->delete_(getTable());
-        setTable(NULL);
-    }
-}
-
 inline js::EmptyShape *
 js::types::TypeObject::getEmptyShape(JSContext *cx, js::Class *aclasp,
                                      gc::AllocKind kind)
 {
     JS_ASSERT(!singleton);
 
     /*
      * Empty shapes can only be on the default 'new' type for a prototype.
@@ -113,43 +104,28 @@ js::types::TypeObject::getEmptyShape(JSC
 
 inline bool
 js::types::TypeObject::canProvideEmptyShape(js::Class *aclasp)
 {
     return proto && !singleton && (!emptyShapes || emptyShapes[0]->getClass() == aclasp);
 }
 
 inline void
-JSObject::updateShape(JSContext *cx)
-{
-    JS_ASSERT(isNative());
-    js::LeaveTraceIfGlobalObject(cx, this);
-    if (hasOwnShape())
-        setOwnShape(js_GenerateShape(cx));
-    else
-        objShape = lastProp->shapeid;
-}
-
-inline void
 JSObject::updateFlags(const js::Shape *shape, bool isDefinitelyAtom)
 {
     jsuint index;
-    if (!isDefinitelyAtom && js_IdIsIndex(shape->propid, &index))
+    if (!isDefinitelyAtom && js_IdIsIndex(shape->propid(), &index))
         setIndexed();
-
-    if (shape->isMethod())
-        setMethodBarrier();
 }
 
 inline void
 JSObject::extend(JSContext *cx, const js::Shape *shape, bool isDefinitelyAtom)
 {
     setLastProperty(shape);
     updateFlags(shape, isDefinitelyAtom);
-    updateShape(cx);
 }
 
 namespace js {
 
 inline bool
 StringObject::init(JSContext *cx, JSString *str)
 {
     JS_ASSERT(nativeEmpty());
@@ -159,143 +135,118 @@ StringObject::init(JSContext *cx, JSStri
         setLastProperty(*shapep);
     } else {
         *shapep = assignInitialShape(cx);
         if (!*shapep)
             return false;
     }
     JS_ASSERT(*shapep == lastProperty());
     JS_ASSERT(!nativeEmpty());
-    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot == LENGTH_SLOT);
+    JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot() == LENGTH_SLOT);
 
     setStringThis(str);
     return true;
 }
 
 inline
-Shape::Shape(jsid propid, PropertyOp getter, StrictPropertyOp setter, uint32 slot,
-             uintN attrs, uintN flags, intN shortid, uint32 shapeid, uint32 slotSpan)
-  : shapeid(shapeid),
-    slotSpan(slotSpan),
+Shape::Shape(BaseShape *base, jsid propid, uint32 slot,
+             uintN attrs, uintN flags, intN shortid)
+  : base_(base),
+    propid_(propid),
     numLinearSearches(0),
-    propid(propid),
-    rawGetter(getter),
-    rawSetter(setter),
-    slot(slot),
+    slot_(slot),
     attrs(uint8(attrs)),
     flags(uint8(flags)),
-    shortid(int16(shortid)),
+    shortid_(int16(shortid)),
     parent(NULL)
 {
-    JS_ASSERT_IF(slotSpan != SHAPE_INVALID_SLOT, slotSpan < JSObject::NSLOTS_LIMIT);
-    JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable());
-    JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER), setterObj->isCallable());
+    JS_ASSERT(base);
+    JS_ASSERT(!JSID_IS_VOID(propid));
+    JS_ASSERT_IF(isMethod(), !base->rawGetter);
     kids.setNull();
 }
 
 inline
-Shape::Shape(JSCompartment *comp, Class *aclasp)
-  : shapeid(js_GenerateShape(comp->rt)),
-    slotSpan(JSSLOT_FREE(aclasp)),
+Shape::Shape(BaseShape *base)
+  : base_(base),
+    propid_(JSID_EMPTY),
     numLinearSearches(0),
-    propid(JSID_EMPTY),
-    clasp(aclasp),
-    rawSetter(NULL),
-    slot(SHAPE_INVALID_SLOT),
-    attrs(0),
-    flags(SHARED_EMPTY),
-    shortid(0),
+    slot_(-1),
+    attrs(JSPROP_SHARED),
+    flags(0),
+    shortid_(0),
     parent(NULL)
 {
-    kids.setNull();
-}
-
-inline
-Shape::Shape(uint32 shapeid)
-  : shapeid(shapeid),
-    slotSpan(0),
-    numLinearSearches(0),
-    propid(JSID_EMPTY),
-    clasp(NULL),
-    rawSetter(NULL),
-    slot(SHAPE_INVALID_SLOT),
-    attrs(0),
-    flags(SHARED_EMPTY),
-    shortid(0),
-    parent(NULL)
-{
+    JS_ASSERT(base);
     kids.setNull();
 }
 
 inline JSDHashNumber
 Shape::hash() const
 {
-    JSDHashNumber hash = 0;
+    JSDHashNumber hash = jsuword(base_->isOwned() ? base_->base : base_);
 
     /* Accumulate from least to most random so the low bits are most random. */
-    JS_ASSERT_IF(isMethod(), !rawSetter);
-    if (rawGetter)
-        hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(rawGetter);
-    if (rawSetter)
-        hash = JS_ROTATE_LEFT32(hash, 4) ^ jsuword(rawSetter);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & PUBLIC_FLAGS);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs;
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid;
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ slot;
-    hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid);
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid_;
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ slot_;
+    hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid_);
     return hash;
 }
 
 inline bool
 Shape::matches(const js::Shape *other) const
 {
-    JS_ASSERT(!JSID_IS_VOID(propid));
-    JS_ASSERT(!JSID_IS_VOID(other->propid));
-    return propid == other->propid &&
-           matchesParamsAfterId(other->rawGetter, other->rawSetter, other->slot, other->attrs,
-                                other->flags, other->shortid);
+    return propid_ == other->propid_ &&
+           matchesParamsAfterId(other->base(), other->slot_, other->attrs,
+                                other->flags, other->shortid_);
 }
 
 inline bool
-Shape::matchesParamsAfterId(PropertyOp agetter, StrictPropertyOp asetter, uint32 aslot,
+Shape::matchesParamsAfterId(BaseShape *base, uint32 aslot,
                             uintN aattrs, uintN aflags, intN ashortid) const
 {
-    JS_ASSERT(!JSID_IS_VOID(propid));
-    return rawGetter == agetter &&
-           rawSetter == asetter &&
-           slot == aslot &&
+    if (base->isOwned())
+        base = base->base;
+
+    BaseShape *mbase = base_;
+    if (mbase->isOwned())
+        mbase = mbase->base;
+
+    return base == mbase &&
+           slot_ == aslot &&
            attrs == aattrs &&
            ((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
-           shortid == ashortid;
+           shortid_ == ashortid;
 }
 
 inline bool
 Shape::get(JSContext* cx, JSObject *receiver, JSObject* obj, JSObject *pobj, js::Value* vp) const
 {
-    JS_ASSERT(!JSID_IS_VOID(propid));
     JS_ASSERT(!hasDefaultGetter());
 
     if (hasGetterValue()) {
         JS_ASSERT(!isMethod());
         js::Value fval = getterValue();
         return js::InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp);
     }
 
     if (isMethod()) {
-        vp->setObject(methodObject());
+        vp->setObject(*pobj->nativeGetMethod(this));
         return pobj->methodReadBarrier(cx, *this, vp);
     }
 
     /*
      * |with (it) color;| ends up here, as do XML filter-expressions.
      * Avoid exposing the With object to native getters.
      */
     if (obj->isWith())
         obj = js_UnwrapWithObject(cx, obj);
-    return js::CallJSPropertyOp(cx, getterOp(), receiver, SHAPE_USERID(this), vp);
+    return js::CallJSPropertyOp(cx, getterOp(), receiver, getUserId(), vp);
 }
 
 inline bool
 Shape::set(JSContext* cx, JSObject* obj, bool strict, js::Value* vp) const
 {
     JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
 
     if (attrs & JSPROP_SETTER) {
@@ -304,67 +255,78 @@ Shape::set(JSContext* cx, JSObject* obj,
     }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     /* See the comment in js::Shape::get as to why we check for With. */
     if (obj->isWith())
         obj = js_UnwrapWithObject(cx, obj);
-    return js::CallJSPropertyOpSetter(cx, setterOp(), obj, SHAPE_USERID(this), strict, vp);
+    return js::CallJSPropertyOpSetter(cx, setterOp(), obj, getUserId(), strict, vp);
 }
 
 inline void
-Shape::removeFromDictionary(JSObject *obj) const
+Shape::removeFromDictionary(JSObject *obj)
 {
-    JS_ASSERT(!frozen());
     JS_ASSERT(inDictionary());
     JS_ASSERT(obj->inDictionaryMode());
     JS_ASSERT(listp);
-    JS_ASSERT(!JSID_IS_VOID(propid));
 
     JS_ASSERT(obj->lastProp->inDictionary());
     JS_ASSERT(obj->lastProp->listp == &obj->lastProp);
-    JS_ASSERT_IF(obj->lastProp != this, !JSID_IS_VOID(obj->lastProp->propid));
-    JS_ASSERT_IF(obj->lastProp->parent, !JSID_IS_VOID(obj->lastProp->parent->propid));
 
     if (parent)
         parent->listp = listp;
     *listp = parent;
     listp = NULL;
 }
 
 inline void
 Shape::insertIntoDictionary(js::Shape **dictp)
 {
     /*
      * Don't assert inDictionaryMode() here because we may be called from
      * JSObject::toDictionaryMode via JSObject::newDictionaryShape.
      */
     JS_ASSERT(inDictionary());
     JS_ASSERT(!listp);
-    JS_ASSERT(!JSID_IS_VOID(propid));
 
-    JS_ASSERT_IF(*dictp, !(*dictp)->frozen());
     JS_ASSERT_IF(*dictp, (*dictp)->inDictionary());
     JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp);
-    JS_ASSERT_IF(*dictp, !JSID_IS_VOID((*dictp)->propid));
     JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment());
 
     setParent(*dictp);
     if (parent)
         parent->listp = &parent;
     listp = dictp;
     *dictp = this;
 }
 
+void
+Shape::initDictionaryShape(const Shape &child, Shape **dictp)
+{
+    BaseShape *base = child.base();
+    if (base->isOwned())
+        base = base->base;
+
+    new (this) Shape(base, child.maybePropid(), child.maybeSlot(), child.attrs,
+                     child.flags | IN_DICTIONARY, child.maybeShortid());
+
+    this->listp = NULL;
+    insertIntoDictionary(dictp);
+}
+
 inline
-EmptyShape::EmptyShape(JSCompartment *comp, js::Class *aclasp)
-  : js::Shape(comp, aclasp)
-{}
+EmptyShape::EmptyShape(BaseShape *base)
+  : js::Shape(base)
+{
+    /* Only empty shapes can be NON_NATIVE. */
+    if (!getClass()->isNative())
+        flags |= NON_NATIVE;
+}
 
 /* static */ inline EmptyShape *
 EmptyShape::getEmptyArgumentsShape(JSContext *cx)
 {
     return ensure(cx, &NormalArgumentsObjectClass, &cx->compartment->emptyArgumentsShape);
 }
 
 /* static */ inline EmptyShape *
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -89,17 +89,17 @@ Bindings::lookup(JSContext *cx, JSAtom *
 
     Shape *shape =
         SHAPE_FETCH(Shape::search(cx, const_cast<Shape **>(&lastBinding),
                     ATOM_TO_JSID(name)));
     if (!shape)
         return NONE;
 
     if (indexp)
-        *indexp = shape->shortid;
+        *indexp = shape->shortid();
 
     if (shape->getter() == GetCallArg)
         return ARGUMENT;
     if (shape->getter() == GetCallUpvar)
         return UPVAR;
 
     return shape->writable() ? VARIABLE : CONSTANT;
 }
@@ -110,17 +110,17 @@ Bindings::add(JSContext *cx, JSAtom *nam
     if (!ensureShape(cx))
         return false;
 
     /*
      * We still follow 10.2.3 of ES3 and make argument and variable properties
      * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
      * avoid objects as activations, something we should do too.
      */
-    uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
+    uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
     uint16 *indexp;
     PropertyOp getter;
     StrictPropertyOp setter;
     uint32 slot = CallObject::RESERVED_SLOTS;
 
     if (kind == ARGUMENT) {
         JS_ASSERT(nvars == 0);
@@ -128,17 +128,18 @@ Bindings::add(JSContext *cx, JSAtom *nam
         indexp = &nargs;
         getter = GetCallArg;
         setter = SetCallArg;
         slot += nargs;
     } else if (kind == UPVAR) {
         indexp = &nupvars;
         getter = GetCallUpvar;
         setter = SetCallUpvar;
-        slot = SHAPE_INVALID_SLOT;
+        slot = lastBinding->maybeSlot();
+        attrs |= JSPROP_SHARED;
     } else {
         JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
         JS_ASSERT(nupvars == 0);
 
         indexp = &nvars;
         getter = GetCallVar;
         setter = SetCallVar;
         if (kind == CONSTANT)
@@ -157,19 +158,25 @@ Bindings::add(JSContext *cx, JSAtom *nam
     jsid id;
     if (!name) {
         JS_ASSERT(kind == ARGUMENT); /* destructuring */
         id = INT_TO_JSID(nargs);
     } else {
         id = ATOM_TO_JSID(name);
     }
 
-    Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp);
+    BaseShape base(&CallClass, attrs, getter, setter);
+    BaseShape *nbase = BaseShape::lookup(cx, base);
+    if (!nbase)
+        return NULL;
 
-    Shape *shape = lastBinding->getChild(cx, child, &lastBinding);
+    Shape child(nbase, id, slot, attrs, Shape::HAS_SHORTID, *indexp);
+
+    /* Shapes in bindings cannot be dictionaries. */
+    Shape *shape = lastBinding->getChild(cx, child, &lastBinding, false);
     if (!shape)
         return false;
 
     JS_ASSERT(lastBinding == shape);
     ++*indexp;
     return true;
 }
 
@@ -189,32 +196,32 @@ Bindings::getLocalNameArray(JSContext *c
 #ifdef DEBUG
     JSAtom * const POISON = reinterpret_cast<JSAtom *>(0xdeadbeef);
     for (uintN i = 0; i < n; i++)
         names[i] = POISON;
 #endif
 
     for (Shape::Range r = lastBinding; !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
-        uintN index = uint16(shape.shortid);
+        uintN index = uint16(shape.shortid());
 
         if (shape.getter() == GetCallArg) {
             JS_ASSERT(index < nargs);
         } else if (shape.getter() == GetCallUpvar) {
             JS_ASSERT(index < nupvars);
             index += nargs + nvars;
         } else {
             JS_ASSERT(index < nvars);
             index += nargs;
         }
 
-        if (JSID_IS_ATOM(shape.propid)) {
-            names[index] = JSID_TO_ATOM(shape.propid);
+        if (JSID_IS_ATOM(shape.propid())) {
+            names[index] = JSID_TO_ATOM(shape.propid());
         } else {
-            JS_ASSERT(JSID_IS_INT(shape.propid));
+            JS_ASSERT(JSID_IS_INT(shape.propid()));
             JS_ASSERT(shape.getter() == GetCallArg);
             names[index] = NULL;
         }
     }
 
 #ifdef DEBUG
     for (uintN i = 0; i < n; i++)
         JS_ASSERT(names[i] != POISON);
@@ -270,23 +277,17 @@ Bindings::sharpSlotBase(JSContext *cx)
 #endif
     return -1;
 }
 
 void
 Bindings::makeImmutable()
 {
     JS_ASSERT(lastBinding);
-    Shape *shape = lastBinding;
-    if (shape->inDictionary()) {
-        do {
-            JS_ASSERT(!shape->frozen());
-            shape->setFrozen();
-        } while ((shape = shape->parent) != NULL);
-    }
+    JS_ASSERT(!lastBinding->inDictionary());
 }
 
 void
 Bindings::trace(JSTracer *trc)
 {
     if (lastBinding)
         MarkShape(trc, lastBinding, "shape");
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -169,17 +169,16 @@ enum BindingKind { NONE, ARGUMENT, VARIA
  * both function and top-level scripts (the latter is needed to track names in
  * strict mode eval code, to give such code its own lexical environment).
  */
 class Bindings {
     js::Shape *lastBinding;
     uint16 nargs;
     uint16 nvars;
     uint16 nupvars;
-    bool hasExtensibleParents;
 
   public:
     inline Bindings(JSContext *cx);
 
     /*
      * Transfers ownership of bindings data from bindings into this fresh
      * Bindings instance. Once such a transfer occurs, the old bindings must
      * not be used again.
@@ -205,16 +204,20 @@ class Bindings {
     bool hasLocalNames() const { return countLocalNames() > 0; }
 
     /* Ensure these bindings have a shape lineage. */
     inline bool ensureShape(JSContext *cx);
 
     /* Returns the shape lineage generated for these bindings. */
     inline js::Shape *lastShape() const;
 
+    /* See Scope::extensibleParents */
+    inline bool extensibleParents();
+    bool setExtensibleParents(JSContext *cx);
+
     enum {
         /*
          * A script may have no more than this many arguments, variables, or
          * upvars.
          */
         BINDING_COUNT_LIMIT = 0xFFFF
     };
 
@@ -289,64 +292,16 @@ class Bindings {
     /*
      * Protect stored bindings from mutation.  Subsequent attempts to add
      * bindings will copy the existing bindings before adding to them, allowing
      * the original bindings to be safely shared.
      */
     void makeImmutable();
 
     /*
-     * Sometimes call objects and run-time block objects need unique shapes, but
-     * sometimes they don't.
-     *
-     * Property cache entries only record the shapes of the first and last
-     * objects along the search path, so if the search traverses more than those
-     * two objects, then those first and last shapes must determine the shapes
-     * of everything else along the path. The js_PurgeScopeChain stuff takes
-     * care of making this work, but that suffices only because we require that
-     * start points with the same shape have the same successor object in the
-     * search path --- a cache hit means the starting shapes were equal, which
-     * means the seach path tail (everything but the first object in the path)
-     * was shared, which in turn means the effects of a purge will be seen by
-     * all affected starting search points.
-     *
-     * For call and run-time block objects, the "successor object" is the scope
-     * chain parent. Unlike prototype objects (of which there are usually few),
-     * scope chain parents are created frequently (possibly on every call), so
-     * following the shape-implies-parent rule blindly would lead one to give
-     * every call and block its own shape.
-     *
-     * In many cases, however, it's not actually necessary to give call and
-     * block objects their own shapes, and we can do better. If the code will
-     * always be used with the same global object, and none of the enclosing
-     * call objects could have bindings added to them at runtime (by direct eval
-     * calls or function statements), then we can use a fixed set of shapes for
-     * those objects. You could think of the shapes in the functions' bindings
-     * and compile-time blocks as uniquely identifying the global object(s) at
-     * the end of the scope chain.
-     *
-     * (In fact, some JSScripts we do use against multiple global objects (see
-     * bug 618497), and using the fixed shapes isn't sound there.)
-     *
-     * In deciding whether a call or block has any extensible parents, we
-     * actually only need to consider enclosing calls; blocks are never
-     * extensible, and the other sorts of objects that appear in the scope
-     * chains ('with' blocks, say) are not CacheableNonGlobalScopes.
-     *
-     * If the hasExtensibleParents flag is set, then Call objects created for
-     * the function this Bindings describes need unique shapes. If the flag is
-     * clear, then we can use lastBinding's shape.
-     *
-     * For blocks, we set the the OWN_SHAPE flag on the compiler-generated
-     * blocksto indicate that their clones need unique shapes.
-     */
-    void setExtensibleParents() { hasExtensibleParents = true; }
-    bool extensibleParents() const { return hasExtensibleParents; }
-
-    /*
      * These methods provide direct access to the shape path normally
      * encapsulated by js::Bindings. These methods may be used to make a
      * Shape::Range for iterating over the relevant shapes from youngest to
      * oldest (i.e., last or right-most to first or left-most in source order).
      *
      * Sometimes iteration order must be from oldest to youngest, however. For
      * such cases, use js::Bindings::getLocalNameArray.
      */
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -51,71 +51,66 @@
 #include "vm/GlobalObject.h"
 
 #include "jsscopeinlines.h"
 
 namespace js {
 
 inline
 Bindings::Bindings(JSContext *cx)
-  : lastBinding(NULL), nargs(0), nvars(0), nupvars(0),
-    hasExtensibleParents(false)
+  : lastBinding(NULL), nargs(0), nvars(0), nupvars(0)
 {
 }
 
 inline void
 Bindings::transfer(JSContext *cx, Bindings *bindings)
 {
     JS_ASSERT(!lastBinding);
+    JS_ASSERT(!bindings->lastBinding || !bindings->lastBinding->inDictionary());
 
     *this = *bindings;
 #ifdef DEBUG
     bindings->lastBinding = NULL;
 #endif
-
-    /* Preserve back-pointer invariants across the lastBinding transfer. */
-    if (lastBinding && lastBinding->inDictionary())
-        lastBinding->listp = &this->lastBinding;
 }
 
 inline void
 Bindings::clone(JSContext *cx, Bindings *bindings)
 {
     JS_ASSERT(!lastBinding);
-
-    /*
-     * Non-dictionary bindings are fine to share, as are dictionary bindings if
-     * they're copy-on-modification.
-     */
-    JS_ASSERT(!bindings->lastBinding ||
-              !bindings->lastBinding->inDictionary() ||
-              bindings->lastBinding->frozen());
+    JS_ASSERT(!bindings->lastBinding || !bindings->lastBinding->inDictionary());
 
     *this = *bindings;
 }
 
 Shape *
 Bindings::lastShape() const
 {
     JS_ASSERT(lastBinding);
-    JS_ASSERT_IF(lastBinding->inDictionary(), lastBinding->frozen());
+    JS_ASSERT(!lastBinding->inDictionary());
     return lastBinding;
 }
 
 bool
 Bindings::ensureShape(JSContext *cx)
 {
     if (!lastBinding) {
         lastBinding = EmptyShape::getEmptyCallShape(cx);
         if (!lastBinding)
             return false;
     }
     return true;
 }
 
+bool
+Bindings::extensibleParents()
+{
+    return lastBinding && lastBinding->extensibleParents();
+}
+
 extern const char *
 CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop);
 
 inline const char *
 CurrentScriptFileAndLine(JSContext *cx, uintN *linenop, LineOption opt)
 {
     if (opt == CALLED_FROM_JSOP_EVAL) {
         JS_ASSERT(js_GetOpcode(cx, cx->fp()->script(), cx->regs().pc) == JSOP_EVAL);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1722,18 +1722,18 @@ FindReplaceLength(JSContext *cx, RegExpS
         JSObject *holder;
         JSProperty *prop = NULL;
         if (!LookupPropertyWithFlags(cx, base, id, JSRESOLVE_QUALIFIED, &holder, &prop))
             return false;
 
         /* Only handle the case where the property exists and is on this object. */
         if (prop && holder == base) {
             Shape *shape = (Shape *) prop;
-            if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) {
-                Value value = base->getSlot(shape->slot);
+            if (shape->hasSlot() && shape->hasDefaultGetter()) {
+                Value value = base->getSlot(shape->slot());
                 if (value.isString()) {
                     rdata.repstr = value.toString()->ensureLinear(cx);
                     if (!rdata.repstr)
                         return false;
                     *sizep = rdata.repstr->length();
                     return true;
                 }
             }
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1283,26 +1283,30 @@ StackSlotHash(JSContext* cx, unsigned sl
     HashAccum(h, uintptr_t(pc), ORACLE_MASK);
     HashAccum(h, uintptr_t(slot), ORACLE_MASK);
     return int(h);
 }
 
 static JS_REQUIRES_STACK inline int
 GlobalSlotHash(JSContext* cx, unsigned slot)
 {
+    JS_NOT_REACHED("FIXME");
+    return 0;
+#if 0
     uintptr_t h = HASH_SEED;
     StackFrame* fp = cx->fp();
 
     while (fp->prev())
         fp = fp->prev();
 
     HashAccum(h, uintptr_t(fp->maybeScript()), ORACLE_MASK);
     HashAccum(h, uintptr_t(fp->scopeChain().getGlobal()->shape()), ORACLE_MASK);
     HashAccum(h, uintptr_t(slot), ORACLE_MASK);
     return int(h);
+#endif
 }
 
 static inline int
 PCHash(jsbytecode* pc)
 {
     return int(uintptr_t(pc) & ORACLE_MASK);
 }
 
@@ -1711,16 +1715,19 @@ AssertTreeIsUnique(TraceMonitor* tm, Tre
     }
 }
 #endif
 
 static void
 AttemptCompilation(TraceMonitor *tm, JSObject* globalObj,
                    JSScript* script, jsbytecode* pc, uint32 argc)
 {
+    JS_NOT_REACHED("FIXME");
+
+#if 0
     /* If we already permanently blacklisted the location, undo that. */
     Unblacklist(script, pc);
     ResetRecordingAttempts(tm, pc);
 
     /* Breathe new life into all peer fragments at the designated loop header. */
     TreeFragment* f = LookupLoop(tm, pc, globalObj, globalObj->shape(), argc);
     if (!f) {
         /*
@@ -1734,16 +1741,17 @@ AttemptCompilation(TraceMonitor *tm, JSO
     JS_ASSERT(f->root == f);
     f = f->first;
     while (f) {
         JS_ASSERT(f->root == f);
         --f->recordAttempts;
         f->hits() = HOTLOOP;
         f = f->peer;
     }
+#endif
 }
 
 static const CallInfo *
 fcallinfo(LIns *ins)
 {
     return ins->isop(LIR_calld) ? ins->callInfo() : NULL;
 }
 
@@ -2294,17 +2302,17 @@ TraceRecorder::TraceRecorder(JSContext* 
     pendingGuardCondition(NULL),
     pendingGlobalSlotsToSet(cx),
     pendingLoop(true),
     generatedSpecializedNative(),
     tempTypeMap(cx),
     w(&tempAlloc(), lirbuf)
 {
     JS_ASSERT(globalObj == cx->fp()->scopeChain().getGlobal());
-    JS_ASSERT(globalObj->hasOwnShape());
+    JS_ASSERT(globalObj->inDictionaryMode());
     JS_ASSERT(cx->regs().pc == (jsbytecode*)fragment->ip);
 
 #ifdef JS_METHODJIT
     if (TRACE_PROFILER(cx))
         AbortProfiling(cx);
 #endif
 
     JS_ASSERT(JS_THREAD_DATA(cx)->onTraceCompartment == NULL);
@@ -2341,18 +2349,19 @@ TraceRecorder::TraceRecorder(JSContext* 
         OUT_OF_MEMORY_ABORT("TraceRecorder::TraceRecorder: out of memory");
 
 #ifdef JS_JIT_SPEW
     debug_only_print0(LC_TMMinimal, "\n");
     debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u (FragID=%06u)\n",
                       tree->treeFileName, tree->treeLineNumber, tree->treePCOffset,
                       fragment->profFragID);
 
-    debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n",
-                      (void*)this->globalObj, this->globalObj->shape());
+    JS_NOT_REACHED("FIXME");
+    //debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n",
+    //                  (void*)this->globalObj, this->globalObj->shape());
     debug_only_printf(LC_TMTreeVis, "TREEVIS RECORD FRAG=%p ANCHOR=%p\n", (void*)fragment,
                       (void*)anchor);
 #endif
 
     /* This creates the LIR writer pipeline. */
     w.init(&LogController, &NJConfig);
 
     w.start();
@@ -3826,32 +3835,32 @@ TraceRecorder::import(TreeFragment* tree
     importTypeMap.set(importStackSlots = stackSlots,
                       importGlobalSlots = ngslots,
                       typeMap, globalTypeMap);
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::isValidSlot(JSObject *obj, const Shape* shape)
 {
-    uint32 setflags = (js_CodeSpec[*cx->regs().pc].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
+    uint32 setflags = (js_CodeSpec[*cx->regs().pc].format & JOF_SET);
 
     if (setflags) {
         if (!shape->hasDefaultSetter())
             RETURN_VALUE("non-stub setter", false);
         if (!shape->writable())
             RETURN_VALUE("writing to a read-only property", false);
     }
 
     /* This check applies even when setflags == 0. */
     if (setflags != JOF_SET && !shape->hasDefaultGetter()) {
         JS_ASSERT(!shape->isMethod());
         RETURN_VALUE("non-stub getter", false);
     }
 
-    if (!obj->containsSlot(shape->slot))
+    if (!obj->containsSlot(shape->slot()))
         RETURN_VALUE("invalid-slot obj property", false);
 
     return true;
 }
 
 /* Lazily import a global slot if we don't already have it in the tracker. */
 JS_REQUIRES_STACK void
 TraceRecorder::importGlobalSlot(unsigned slot)
@@ -5626,16 +5635,20 @@ TraceRecorder::checkTraceEnd(jsbytecode 
  * Check whether the shape of the global object has changed. The return value
  * indicates whether the recorder is still active.  If 'false', any active
  * recording has been aborted and the JIT may have been reset.
  */
 static JS_REQUIRES_STACK bool
 CheckGlobalObjectShape(JSContext* cx, TraceMonitor* tm, JSObject* globalObj,
                        uint32 *shape = NULL, SlotList** slots = NULL)
 {
+    JS_NOT_REACHED("FIXME");
+    return false;
+
+#if 0
     if (tm->needFlush) {
         ResetJIT(cx, tm, FR_DEEP_BAIL);
         return false;
     }
 
     if (globalObj->numSlots() > MAX_GLOBAL_SLOTS) {
         if (tm->recorder)
             AbortRecording(cx, "too many slots in global object");
@@ -5700,16 +5713,17 @@ CheckGlobalObjectShape(JSContext* cx, Tr
 
     /* No currently-tracked-global found and no room to allocate, abort. */
     AUDIT(globalShapeMismatchAtEntry);
     debug_only_printf(LC_TMTracer,
                       "No global slotlist for global shape %u, flushing cache.\n",
                       globalShape);
     ResetJIT(cx, tm, FR_GLOBALS_FULL);
     return false;
+#endif
 }
 
 /*
  * Return whether or not the recorder could be started. If 'false', the JIT has
  * been reset in response to an OOM.
  */
 bool JS_REQUIRES_STACK
 TraceRecorder::startRecorder(JSContext* cx, TraceMonitor *tm, VMSideExit* anchor, VMFragment* f,
@@ -6665,16 +6679,20 @@ enum LEAVE_TREE_STATUS {
 static LEAVE_TREE_STATUS
 LeaveTree(TraceMonitor *tm, TracerState&, VMSideExit *lr);
 
 /* Return false if the interpreter should goto error. */
 static JS_REQUIRES_STACK bool
 ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
             VMSideExit** innermostNestedGuardp, VMSideExit **lrp)
 {
+    JS_NOT_REACHED("FIXME");
+    return false;
+#if 0
+
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_EXECUTE);
 #endif
     JS_ASSERT(f->root == f && f->code());
 
     if (!ScopeChainCheck(cx, f) ||
         !cx->stack.space().ensureEnoughSpaceToEnterTrace(cx)) {
         *lrp = NULL;
@@ -6757,16 +6775,17 @@ ExecuteTree(JSContext* cx, TraceMonitor*
         {
             debug_only_printf(LC_TMMinimal, "  Blacklisting at line %u (executed only %d iters)\n",
                               f->treeLineNumber, f->iters);
             Blacklist((jsbytecode *)f->ip);
         }
     }
 #endif
     return ok;
+#endif
 }
 
 class Guardian {
     bool *flagp;
 public:
     Guardian(bool *flagp) {
         this->flagp = flagp;
         JS_ASSERT(!*flagp);
@@ -8226,19 +8245,19 @@ TraceRecorder::scopeChainProp(JSObject* 
         CHECK_STATUS_A(traverseScopeChain(chainHead, head_ins, obj, obj_ins));
 
         if (obj2 != obj)
             RETURN_STOP_A("prototype property");
 
         Shape* shape = (Shape*) prop;
         if (!isValidSlot(obj, shape))
             return ARECORD_STOP;
-        if (!lazilyImportGlobalSlot(shape->slot))
+        if (!lazilyImportGlobalSlot(shape->slot()))
             RETURN_STOP_A("lazy import of global slot failed");
-        vp = &obj->getSlot(shape->slot);
+        vp = &obj->getSlot(shape->slot());
         ins = get(vp);
         nr.tracked = true;
         return ARECORD_CONTINUE;
     }
 
     if (obj == obj2 && obj->isCall()) {
         AbortableRecordingStatus status =
             InjectStatus(callProp(obj, prop, ATOM_TO_JSID(atom), vp, ins, nr));
@@ -8253,21 +8272,21 @@ TraceRecorder::scopeChainProp(JSObject* 
  */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, const Value*& vp,
                         LIns*& ins, NameResult& nr)
 {
     Shape *shape = (Shape*) prop;
 
     JSOp op = JSOp(*cx->regs().pc);
-    uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
+    uint32 setflags = (js_CodeSpec[op].format & JOF_SET);
     if (setflags && !shape->writable())
         RETURN_STOP("writing to a read-only property");
 
-    uintN slot = uint16(shape->shortid);
+    uintN slot = uint16(shape->shortid());
 
     vp = NULL;
     CallObject &callobj = obj->asCall();
     StackFrame* cfp = callobj.maybeStackFrame();
     if (cfp) {
         if (shape->getterOp() == GetCallArg) {
             JS_ASSERT(slot < cfp->numFormalArgs());
             vp = &cfp->formalArg(slot);
@@ -8290,17 +8309,17 @@ TraceRecorder::callProp(JSObject* obj, J
             nr.tracked = true;
             return RECORD_CONTINUE;
         }
     } else {
         // Call objects do not yet have shape->isMethod() properties, but they
         // should. See bug 514046, for which this code is future-proof. Remove
         // this comment when that bug is fixed (so, FIXME: 514046).
         DebugOnly<JSBool> rv =
-            js_GetPropertyHelper(cx, obj, shape->propid,
+            js_GetPropertyHelper(cx, obj, shape->propid(),
                                  (op == JSOP_CALLNAME)
                                  ? JSGET_NO_METHOD_BARRIER
                                  : JSGET_METHOD_BARRIER,
                                  &nr.v);
         JS_ASSERT(rv);
     }
 
     LIns* obj_ins;
@@ -9595,16 +9614,20 @@ TraceRecorder::forgetGuardedShapes()
     dumpGuardedShapes("forget-all");
 #endif
     guardedShapeTable.clear();
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, PCVal& pcval)
 {
+    JS_NOT_REACHED("FIXME");
+    return ARECORD_CONTINUE;
+
+#if 0
     jsbytecode* pc = cx->regs().pc;
     JS_ASSERT(*pc != JSOP_INITPROP && *pc != JSOP_INITMETHOD &&
               *pc != JSOP_SETNAME && *pc != JSOP_SETPROP && *pc != JSOP_SETMETHOD);
 
     // Mimic the interpreter's special case for dense arrays by skipping up one
     // hop along the proto chain when accessing a named (not indexed) property,
     // typically to find Array.prototype methods.
     JSObject* aobj = obj;
@@ -9683,25 +9706,30 @@ TraceRecorder::test_property_cache(JSObj
     // There's a potential race in any JS_THREADSAFE embedding that's nuts
     // enough to share mutable objects on the scope or proto chain, but we
     // don't care about such insane embeddings. Anyway, the (scope, proto)
     // entry->vcap coordinates must reach obj2 from aobj at this point.
     JS_ASSERT(cx->thread()->data.requestDepth);
 #endif
 
     return InjectStatus(guardPropertyCacheHit(obj_ins, aobj, obj2, entry, pcval));
+#endif
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::guardPropertyCacheHit(LIns* obj_ins,
                                      JSObject* aobj,
                                      JSObject* obj2,
                                      PropertyCacheEntry* entry,
                                      PCVal& pcval)
 {
+    JS_NOT_REACHED("FIXME");
+    return RECORD_CONTINUE;
+
+#if 0
     VMSideExit* exit = snapshot(BRANCH_EXIT);
 
     uint32 vshape = entry->vshape();
 
     // Special case for the global object, which may be aliased to get a property value.
     // To catch cross-global property accesses we must check against globalObj identity.
     // But a JOF_NAME mode opcode needs no guard, as we ensure the global object's shape
     // never changes, and name ops can't reach across a global object ('with' aborts).
@@ -9743,16 +9771,17 @@ TraceRecorder::guardPropertyCacheHit(LIn
         } else {
             obj2_ins = w.immpObjGC(obj2);
         }
         CHECK_STATUS(guardShape(obj2_ins, obj2, vshape, "guard_vshape", exit));
     }
 
     pcval = entry->vword;
     return RECORD_CONTINUE;
+#endif
 }
 
 void
 TraceRecorder::stobj_set_fslot(LIns *obj_ins, unsigned slot, const Value &v, LIns* v_ins)
 {
     box_value_into(v, v_ins, FSlotsAddress(obj_ins, slot));
 }
 
@@ -10226,16 +10255,20 @@ TraceRecorder::guardHasPrototype(JSObjec
     bool cond = *pobj == NULL;
     guard(cond, w.name(w.eqp0(*pobj_ins), "guard(proto-not-null)"), exit);
     return !cond;
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::guardPrototypeHasNoIndexedProperties(JSObject* obj, LIns* obj_ins, VMSideExit *exit)
 {
+    JS_NOT_REACHED("FIXME");
+    return RECORD_CONTINUE;
+
+#if 0
     /*
      * Guard that no object along the prototype chain has any indexed
      * properties which might become visible through holes in the array.
      */
     if (js_PrototypeHasIndexedProperties(cx, obj))
         return RECORD_STOP;
 
     JS_ASSERT(obj->isDenseArray());
@@ -10259,25 +10292,30 @@ TraceRecorder::guardPrototypeHasNoIndexe
      */
     do {
         CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard(shape)", exit));
         obj = obj->getProto();
         obj_ins = w.ldpObjProto(obj_ins);
     } while (obj);
 
     return RECORD_CONTINUE;
+#endif
 }
 
 /*
  * Guard that the object stored in v has the ECMA standard [[DefaultValue]]
  * method. Several imacros require this.
  */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::guardNativeConversion(Value& v)
 {
+    JS_NOT_REACHED("FIXME");
+    return RECORD_CONTINUE;
+
+#if 0
     JSObject* obj = &v.toObject();
     LIns* obj_ins = get(&v);
 
     JSConvertOp convert = obj->getClass()->convert;
     if (convert != JS_ConvertStub)
         RETURN_STOP("operand has convert hook");
 
     VMSideExit* exit = snapshot(BRANCH_EXIT);
@@ -10288,16 +10326,17 @@ TraceRecorder::guardNativeConversion(Val
         CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(),
                                 "guardNativeConversion", exit));
     } else {
         // We could specialize to guard on just JSClass.convert, but a mere
         // class guard is simpler and slightly faster.
         guardClass(obj_ins, obj->getClass(), snapshot(MISMATCH_EXIT), LOAD_NORMAL);
     }
     return RECORD_CONTINUE;
+#endif
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::clearReturningFrameFromNativeTracker()
 {
     /*
      * Clear all tracker entries associated with the frame for the same reason
      * described in record_EnterFrame. Reuse the generic visitor to avoid
@@ -11210,17 +11249,17 @@ TraceRecorder::emitNativePropertyOp(cons
 
     enterDeepBailCall();
 
     w.stStateField(addr_boxed_val_ins, nativeVp);
     w.stStateField(w.immi(1), nativeVpLen);
 
     CallInfo* ci = new (traceAlloc()) CallInfo();
     /* Setters and getters have their initial arguments in common. */
-    LIns* possibleArgs[] = { NULL, NULL, w.immpIdGC(SHAPE_USERID(shape)), obj_ins, cx_ins };
+    LIns* possibleArgs[] = { NULL, NULL, w.immpIdGC(shape->getUserId()), obj_ins, cx_ins };
     LIns** args;
     if (setflag) {
         ci->_address = uintptr_t(shape->setterOp());
         ci->_typesig = CallInfo::typeSig5(ARGTYPE_I, ARGTYPE_P, ARGTYPE_P, ARGTYPE_P, ARGTYPE_B,
                                           ARGTYPE_P);
         possibleArgs[0] = addr_boxed_val_ins;
         possibleArgs[1] = strictModeCode_ins;
         args = possibleArgs;
@@ -12074,16 +12113,19 @@ SafeLookup(JSContext *cx, JSObject* obj,
 /*
  * Lookup the property for the SETPROP/SETNAME/SETMETHOD instruction at pc.
  * Emit guards to ensure that the result at run time is the same.
  */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::lookupForSetPropertyOp(JSObject* obj, LIns* obj_ins, jsid id,
                                       bool* safep, JSObject** pobjp, const Shape** shapep)
 {
+    JS_NOT_REACHED("FIXME");
+
+#if 0
     // We could consult the property cache here, but the contract for
     // PropertyCache::testForSet is intricate enough that it's a lot less code
     // to do a SafeLookup.
     *safep = SafeLookup(cx, obj, id, pobjp, shapep);
     if (!*safep)
         return RECORD_CONTINUE;
 
     VMSideExit *exit = snapshot(BRANCH_EXIT);
@@ -12098,39 +12140,45 @@ TraceRecorder::lookupForSetPropertyOp(JS
             if (obj != globalObj)
                 CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_proto_chain", exit));
             obj = obj->getProto();
             if (!obj)
                 break;
             obj_ins = w.immpObjGC(obj);
         }
     }
+#endif
     return RECORD_CONTINUE;
 }
 
 static JSBool FASTCALL
 MethodWriteBarrier(JSContext* cx, JSObject* obj, uint32 slot, const Value* v)
 {
+    JS_NOT_REACHED("FIXME");
+    return true;
+#if 0
+
 #ifdef DEBUG
     TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
 #endif
 
     bool ok = obj->methodWriteBarrier(cx, slot, *v);
     JS_ASSERT(WasBuiltinSuccessful(tm));
     return ok;
+#endif
 }
 JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, UINT32, CVALUEPTR,
                      0, ACCSET_STORE_ANY)
 
 /* Emit a specialized, inlined copy of js_NativeSet. */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, const Shape* shape,
                          const Value &v, LIns* v_ins)
 {
-    uint32 slot = shape->slot;
+    uint32 slot = shape->slot();
     JS_ASSERT((slot != SHAPE_INVALID_SLOT) == shape->hasSlot());
     JS_ASSERT_IF(shape->hasSlot(), obj->nativeContains(cx, *shape));
 
     /*
      * We do not trace assignment to properties that have both a non-default
      * setter and a slot, for several reasons.
      *
      * First, that would require sampling rt->propertyRemovals before and after
@@ -12162,17 +12210,19 @@ TraceRecorder::nativeSet(JSObject* obj, 
     // Call the setter, if any.
     if (!shape->hasDefaultSetter()) {
         if (shape->hasSetterValue())
             RETURN_STOP("can't trace JavaScript function setter yet");
         emitNativePropertyOp(shape, obj_ins, true, box_value_into_alloc(v, v_ins));
     }
 
     if (slot != SHAPE_INVALID_SLOT) {
-        if (obj->brandedOrHasMethodBarrier()) {
+        JS_NOT_REACHED("FIXME");
+#if 0
+        if (obj->hasMethodBarrier()) {
             if (obj == globalObj) {
                 // Because the trace is type-specialized to the global object's
                 // slots, no run-time check is needed. Avoid recording a global
                 // shape change, though.
                 JS_ASSERT(obj->nativeContains(cx, *shape));
                 if (IsFunctionObject(obj->getSlot(slot)))
                     RETURN_STOP("can't trace set of function-valued global property");
             } else {
@@ -12181,16 +12231,17 @@ TraceRecorder::nativeSet(JSObject* obj, 
                 // property is not function-valued now, it might be on trace.
                 enterDeepBailCall();
                 LIns* args[] = {box_value_into_alloc(v, v_ins), w.immi(slot), obj_ins, cx_ins};
                 LIns* ok_ins = w.call(&MethodWriteBarrier_ci, args);
                 guard(false, w.eqi0(ok_ins), OOM_EXIT);
                 leaveDeepBailCall();
             }
         }
+#endif
 
         // Store the value.
         if (obj == globalObj) {
             if (!lazilyImportGlobalSlot(slot))
                 RETURN_STOP("lazy import of global slot failed");
             set(&obj->getSlotRef(slot), v_ins);
         } else {
             LIns* slots_ins = NULL;
@@ -12298,38 +12349,38 @@ TraceRecorder::setCallProp(JSObject *obj
 {
     CallObject &callobj = obj->asCall();
 
     // Set variables in on-trace-stack call objects by updating the tracker.
     StackFrame *fp = frameIfInRange(&callobj);
     if (fp) {
         if (shape->setterOp() == SetCallArg) {
             JS_ASSERT(shape->hasShortID());
-            uintN slot = uint16(shape->shortid);
+            uintN slot = uint16(shape->shortid());
             Value *vp2 = &fp->formalArg(slot);
             CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins));
             return RECORD_CONTINUE;
         }
         if (shape->setterOp() == SetCallVar) {
             JS_ASSERT(shape->hasShortID());
-            uintN slot = uint16(shape->shortid);
+            uintN slot = uint16(shape->shortid());
             Value *vp2 = &fp->slots()[slot];
             CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins));
             return RECORD_CONTINUE;
         }
         RETURN_STOP("can't trace special CallClass setter");
     }
 
     if (!callobj.maybeStackFrame()) {
         // Because the parent guard in guardCallee ensures this Call object
         // will be the same object now and on trace, and because once a Call
         // object loses its frame it never regains one, on trace we will also
         // have a null private in the Call object. So all we need to do is
         // write the value to the Call object's slot.
-        intN slot = uint16(shape->shortid);
+        intN slot = uint16(shape->shortid());
         if (shape->setterOp() == SetCallArg) {
             JS_ASSERT(slot < ArgClosureTraits::slot_count(&callobj));
             slot += ArgClosureTraits::slot_offset(obj);
         } else if (shape->setterOp() == SetCallVar) {
             JS_ASSERT(slot < VarClosureTraits::slot_count(&callobj));
             slot += VarClosureTraits::slot_offset(obj);
         } else {
             RETURN_STOP("can't trace special CallClass setter");
@@ -12362,17 +12413,17 @@ TraceRecorder::setCallProp(JSObject *obj
     // inner trace such that the target variable is defined in the outer trace
     // entry frame. For simplicity, we just fall off trace.
     guard(false,
           w.eqp(entryFrameIns(), w.ldpObjPrivate(callobj_ins)),
           MISMATCH_EXIT);
 
     LIns* args[] = {
         box_value_for_native_call(v, v_ins),
-        w.nameImmw(JSID_BITS(SHAPE_USERID(shape))),
+        w.nameImmw(JSID_BITS(shape->getUserId())),
         callobj_ins,
         cx_ins
     };
     LIns* call_ins = w.call(ci, args);
     guard(false, w.name(w.eqi0(call_ins), "guard(set upvar)"), STATUS_EXIT);
 
     return RECORD_CONTINUE;
 }
@@ -12422,18 +12473,19 @@ TraceRecorder::setProperty(JSObject* obj
             RETURN_STOP("setting accessor property with no setter");
     } else if (!shape->writable()) {
         RETURN_STOP("setting readonly data property");
     }
 
     // Handle setting an existing own property.
     if (pobj == obj) {
         if (*cx->regs().pc == JSOP_SETMETHOD) {
-            if (shape->isMethod() && shape->methodObject() == v.toObject())
-                return RECORD_CONTINUE;
+            JS_NOT_REACHED("FIXME");
+            //if (shape->isMethod() && shape->methodObject() == v.toObject())
+            //    return RECORD_CONTINUE;
             RETURN_STOP("setmethod: property exists");
         }
         return nativeSet(obj, obj_ins, shape, v, v_ins);
     }
 
     // If shape is an inherited non-SHARED property, we will add a new,
     // shadowing data property.
     if (shape->hasSlot()) {
@@ -12763,40 +12815,40 @@ GetPropertyWithNativeGetter(JSContext* c
 {
     TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
 
     LeaveTraceIfGlobalObject(cx, obj);
 
 #ifdef DEBUG
     JSProperty* prop;
     JSObject* pobj;
-    JS_ASSERT(obj->lookupProperty(cx, shape->propid, &pobj, &prop));
+    JS_ASSERT(obj->lookupProperty(cx, shape->propid(), &pobj, &prop));
     JS_ASSERT(prop == (JSProperty*) shape);
 #endif
 
     // Shape::get contains a special case for With objects. We can elide it
     // here because With objects are, we claim, never on the operand stack
     // while recording.
     JS_ASSERT(obj->getClass() != &WithClass);
 
     vp->setUndefined();
-    if (!shape->getterOp()(cx, obj, SHAPE_USERID(shape), vp)) {
+    if (!shape->getterOp()(cx, obj, shape->getUserId(), vp)) {
         SetBuiltinError(tm);
         return JS_FALSE;
     }
     return WasBuiltinSuccessful(tm);
 }
 JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyWithNativeGetter,
                      CONTEXT, OBJECT, SHAPE, VALUEPTR, 0, ACCSET_STORE_ANY)
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::getPropertyWithNativeGetter(LIns* obj_ins, const Shape* shape, Value* outp)
 {
     JS_ASSERT(!shape->hasGetterValue());
-    JS_ASSERT(shape->slot == SHAPE_INVALID_SLOT);
+    JS_ASSERT(shape->slot() == SHAPE_INVALID_SLOT);
     JS_ASSERT(!shape->hasDefaultGetterOrIsMethod());
 
     // Call GetPropertyWithNativeGetter. See note in getPropertyByName about vp.
     // FIXME - We should call the getter directly. Using a builtin function for
     // now because it buys some extra asserts. See bug 508310.
     enterDeepBailCall();
     LIns* vp_ins = w.name(w.allocp(sizeof(Value)), "vp");
     LIns* args[] = {vp_ins, w.nameImmpNonGC(shape), obj_ins, cx_ins};
@@ -13381,27 +13433,30 @@ TraceRecorder::record_JSOP_CALLNAME()
         if (!nr.tracked)
             vp = &nr.v;
         if (!vp->isObject())
             RETURN_STOP_A("callee is not an object");
         funobj = &vp->toObject();
         if (!funobj->isFunction())
             RETURN_STOP_A("callee is not a function");
     } else {
+        JS_NOT_REACHED("FIXME");
+#if 0
         LIns* obj_ins = w.immpObjGC(globalObj);
         JSObject* obj2;
         PCVal pcval;
 
         CHECK_STATUS_A(test_property_cache(scopeObj, obj_ins, obj2, pcval));
 
         if (pcval.isNull() || !pcval.isFunObj())
             RETURN_STOP_A("callee is not a function");
 
         funobj = &pcval.toFunObj();
         funobj_ins = w.immpObjGC(funobj);
+#endif
     }
 
     // Detect crossed globals early. The interpreter could decide to compute
     // a non-Undefined |this| value, and we want to make sure that we'll (1)
     // abort in this case, and (2) bail out early if a callee will need special
     // |this| computation. Note that if (scopeObj != globalObj),
     // scopeChainProp() guarantees that scopeObj is a cacheable scope.
     if (scopeObj == globalObj) {
@@ -13646,23 +13701,24 @@ TraceRecorder::createThis(JSObject& ctor
 
     // At run time ctor might be a different instance of the same function. Its
     // .prototype property might not be resolved yet. Guard on the function
     // object's shape to make sure .prototype is there.
     //
     // However, if ctor_ins is constant, which is usual, we don't need to
     // guard: .prototype is non-configurable, and an object's non-configurable
     // data properties always stay in the same slot for the life of the object.
-    if (!ctor_ins->isImmP())
-        guardShape(ctor_ins, &ctor, ctor.shape(), "ctor_shape", snapshot(MISMATCH_EXIT));
+    JS_NOT_REACHED("FIXME");
+    //if (!ctor_ins->isImmP())
+    //    guardShape(ctor_ins, &ctor, ctor.shape(), "ctor_shape", snapshot(MISMATCH_EXIT));
 
     // Pass the slot of ctor.prototype to js_CreateThisFromTrace. We can only
     // bake the slot into the trace, not the value, since .prototype is
     // writable.
-    uintN protoSlot = shape->slot;
+    uintN protoSlot = shape->slot();
     LIns* args[] = { w.nameImmw(protoSlot), ctor_ins, cx_ins };
     *thisobj_insp = w.call(&js_CreateThisFromTrace_ci, args);
     guard(false, w.eqp0(*thisobj_insp), OOM_EXIT);
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, bool constructing)
@@ -13957,16 +14013,19 @@ TraceRecorder::name(const Value*& vp, LI
 {
     JSObject* obj = &cx->fp()->scopeChain();
     JSOp op = JSOp(*cx->regs().pc);
     if (js_CodeSpec[op].format & JOF_GNAME)
         obj = obj->getGlobal();
     if (obj != globalObj)
         return scopeChainProp(obj, vp, ins, nr);
 
+    JS_NOT_REACHED("FIXME");
+#if 0
+
     /* Can't use prop here, because we don't want unboxing from global slots. */
     LIns* obj_ins = w.immpObjGC(globalObj);
     uint32 slot;
 
     JSObject* obj2;
     PCVal pcval;
 
     /*
@@ -13983,29 +14042,31 @@ TraceRecorder::name(const Value*& vp, LI
     if (obj2 != obj)
         RETURN_STOP_A("name() hit prototype chain");
 
     /* Don't trace getter or setter calls, our caller wants a direct slot. */
     if (pcval.isShape()) {
         const Shape* shape = pcval.toShape();
         if (!isValidSlot(obj, shape))
             RETURN_STOP_A("name() not accessing a valid slot");
-        slot = shape->slot;
+        slot = shape->slot();
     } else {
         if (!pcval.isSlot())
             RETURN_STOP_A("PCE is not a slot");
         slot = pcval.toSlot();
     }
 
     if (!lazilyImportGlobalSlot(slot))
         RETURN_STOP_A("lazy import of global slot failed");
 
     vp = &obj->getSlotRef(slot);
     ins = get(vp);
     nr.tracked = true;
+#endif
+
     return ARECORD_CONTINUE;
 }
 
 static JSObject* FASTCALL
 MethodReadBarrier(JSContext* cx, JSObject* obj, Shape* shape, JSObject* funobj)
 {
     Value v = ObjectValue(*funobj);
     AutoValueRooter tvr(cx, v);
@@ -14035,16 +14096,21 @@ TraceRecorder::prop(JSObject* obj, LIns*
      * object not having the same op must have a different class, and therefore
      * must differ in its shape (or not be the global object).
      */
     if (!obj->isDenseArray() && obj->getOps()->getProperty)
         RETURN_STOP_A("non-dense-array, non-native js::ObjectOps::getProperty");
 
     JS_ASSERT((slotp && v_insp && !outp) || (!slotp && !v_insp && outp));
 
+    JS_NOT_REACHED("FIXME");
+    return ARECORD_CONTINUE;
+
+#if 0
+
     /*
      * Property cache ensures that we are dealing with an existing property,
      * and guards the shape for us.
      */
     JSObject* obj2;
     PCVal pcval;
     CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval));
 
@@ -14067,79 +14133,78 @@ TraceRecorder::prop(JSObject* obj, LIns*
          * This trace will be valid as long as neither the object nor any object
          * on its prototype chain changes shape.
          *
          * FIXME: This loop can become a single shape guard once bug 497789 has
          * been fixed.
          */
         VMSideExit* exit = snapshot(BRANCH_EXIT);
         do {
+            JS_NOT_REACHED("FIXME");
+            /*
             if (obj->isNative()) {
                 CHECK_STATUS_A(guardShape(obj_ins, obj, obj->shape(), "guard(shape)", exit));
             } else if (obj->isDenseArray()) {
                 guardDenseArray(obj_ins, exit);
             } else {
                 RETURN_STOP_A("non-native object involved in undefined property access");
             }
+            */
         } while (guardHasPrototype(obj, obj_ins, &obj, &obj_ins, exit));
 
         set(outp, w.immiUndefined());
         return ARECORD_CONTINUE;
     }
 
     return InjectStatus(propTail(obj, obj_ins, obj2, pcval, slotp, v_insp, outp));
+#endif
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::propTail(JSObject* obj, LIns* obj_ins, JSObject* obj2, PCVal pcval,
                         uint32 *slotp, LIns** v_insp, Value *outp)
 {
     const JSCodeSpec& cs = js_CodeSpec[*cx->regs().pc];
-    uint32 setflags = (cs.format & (JOF_INCDEC | JOF_FOR));
     JS_ASSERT(!(cs.format & JOF_SET));
 
+    JS_NOT_REACHED("FIXME");
+    return RECORD_CONTINUE;
+#if 0
     const Shape* shape;
     uint32 slot;
     bool isMethod;
 
     if (pcval.isShape()) {
         shape = pcval.toShape();
         JS_ASSERT(obj2->nativeContains(cx, *shape));
 
-        if (setflags && !shape->hasDefaultSetter())
-            RETURN_STOP("non-stub setter");
-        if (setflags && !shape->writable())
-            RETURN_STOP("writing to a readonly property");
         if (!shape->hasDefaultGetterOrIsMethod()) {
             if (slotp)
                 RETURN_STOP("can't trace non-stub getter for this opcode");
             if (shape->hasGetterValue())
                 return getPropertyWithScriptGetter(obj, obj_ins, shape);
-            if (shape->slot == SHAPE_INVALID_SLOT)
+            if (shape->slot() == SHAPE_INVALID_SLOT)
                 return getPropertyWithNativeGetter(obj_ins, shape, outp);
             return getPropertyById(obj_ins, outp);
         }
-        if (!obj2->containsSlot(shape->slot))
+        if (!obj2->containsSlot(shape->slot()))
             RETURN_STOP("no valid slot");
-        slot = shape->slot;
+        slot = shape->slot();
         isMethod = shape->isMethod();
         JS_ASSERT_IF(isMethod, obj2->hasMethodBarrier());
     } else {
         if (!pcval.isSlot())
             RETURN_STOP("PCE is not a slot");
         slot = pcval.toSlot();
         shape = NULL;
         isMethod = false;
     }
 
     /* We have a slot. Check whether it is direct or in a prototype. */
     if (obj2 != obj) {
-        if (setflags)
-            RETURN_STOP("JOF_INCDEC|JOF_FOR opcode hit prototype chain");
-
         /*
          * We're getting a prototype property. Two cases:
          *
          * 1. If obj2 is obj's immediate prototype we must walk up from obj,
          * since direct and immediate-prototype cache hits key on obj's shape,
          * not its identity.
          *
          * 2. Otherwise obj2 is higher up the prototype chain and we've keyed
@@ -14183,16 +14248,17 @@ TraceRecorder::propTail(JSObject* obj, L
 
     if (slotp) {
         *slotp = slot;
         *v_insp = v_ins;
     }
     if (outp)
         set(outp, v_ins);
     return RECORD_CONTINUE;
+#endif
 }
 
 /*
  * When we end up with a hole, read it as undefined, and make sure to set
  * addr_ins to null.
  */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::denseArrayElement(Value& oval, Value& ival, const Value*& vp, LIns*& v_ins,
@@ -14999,19 +15065,20 @@ TraceRecorder::traverseScopeChain(JSObje
                 RETURN_STOP("scope chain lookup crosses non-cacheable object");
 
             // We must guard on the shape of all call objects for heavyweight functions
             // that we traverse on the scope chain: if the shape changes, a variable with
             // the same name may have been inserted in the scope chain.
             if (IsFindableCallObj(obj)) {
                 if (!exit)
                     exit = snapshot(BRANCH_EXIT);
-                guard(true,
-                      w.name(w.eqiN(w.ldiObjShape(obj_ins), obj->shape()), "guard_shape"),
-                      exit);
+                JS_NOT_REACHED("FIXME");
+                //guard(true,
+                //      w.name(w.eqiN(w.ldiObjShape(obj_ins), obj->shape()), "guard_shape"),
+                //      exit);
             }
         }
 
         JS_ASSERT(!obj->isBlock());
 
         if (obj == targetObj)
             break;
 
@@ -15970,16 +16037,20 @@ TraceRecorder::record_JSOP_CALLPROP()
         if (!js_GetClassPrototype(cx, NULL, protoKey, &obj))
             RETURN_ERROR_A("GetClassPrototype failed!");
 
         obj_ins = w.immpObjGC(obj);
         debug_only_stmt(obj_ins = w.name(obj_ins, protoname);)
         this_ins = get(&l); // use primitive as |this|
     }
 
+    JS_NOT_REACHED("FIXME");
+    return ARECORD_CONTINUE;
+#if 0
+
     JSObject* obj2;
     PCVal pcval;
     CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval));
 
     if (pcval.isNull())
         RETURN_STOP_A("callprop of missing method");
 
     if (pcval.isFunObj()) {
@@ -15992,16 +16063,17 @@ TraceRecorder::record_JSOP_CALLPROP()
     } else {
         if (l.isPrimitive())
             RETURN_STOP_A("callprop of primitive method");
         JS_ASSERT_IF(pcval.isShape(), !pcval.toShape()->isMethod());
         CHECK_STATUS_A(propTail(obj, obj_ins, obj2, pcval, NULL, NULL, &l));
     }
     stack(0, this_ins);
     return ARECORD_CONTINUE;
+#endif
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_DELDESC()
 {
     return ARECORD_STOP;
 }
 
@@ -16313,52 +16385,16 @@ TraceRecorder::record_JSOP_TRACE()
 }
 
 AbortableRecordingStatus
 TraceRecorder::record_JSOP_NOTRACE()
 {
     return ARECORD_CONTINUE;
 }
 
-JSBool FASTCALL
-js_Unbrand(JSContext *cx, JSObject *obj)
-{
-    return obj->unbrand(cx);
-}
-
-JS_DEFINE_CALLINFO_2(extern, BOOL, js_Unbrand, CONTEXT, OBJECT, 0, ACCSET_STORE_ANY)
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_UNBRAND()
-{
-    LIns* args_ins[] = { stack(-1), cx_ins };
-    LIns* call_ins = w.call(&js_Unbrand_ci, args_ins);
-    guard(false, w.eqi0(call_ins), OOM_EXIT);
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_UNBRANDTHIS()
-{
-    /* In case of primitive this, do nothing. */
-    StackFrame *fp = cx->fp();
-    if (fp->fun()->inStrictMode() && !fp->thisValue().isObject())
-        return ARECORD_CONTINUE;
-
-    LIns* this_ins;
-    RecordingStatus status = getThis(this_ins);
-    if (status != RECORD_CONTINUE)
-        return InjectStatus(status);
-
-    LIns* args_ins[] = { this_ins, cx_ins };
-    LIns* call_ins = w.call(&js_Unbrand_ci, args_ins);
-    guard(false, w.eqi0(call_ins), OOM_EXIT);
-    return ARECORD_CONTINUE;
-}
-
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_SHARPINIT()
 {
     return ARECORD_STOP;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_UNUSED0() {
@@ -16981,26 +17017,29 @@ LoopProfile::profileOperation(JSContext*
             increment(OP_ARRAY_READ);
     }
 
     if (op == JSOP_GETPROP || op == JSOP_CALLPROP) {
         /* Try to see if it's a scripted getter, which is faster in the tracer. */
         Value v = cx->regs().sp[-1];
 
         if (v.isObject()) {
+            JS_NOT_REACHED("FIXME");
+#if 0
             JSObject *aobj = js_GetProtoIfDenseArray(&v.toObject());
             PropertyCacheEntry *entry;
             JSObject *obj2;
             JSAtom *atom;
             JS_PROPERTY_CACHE(cx).test(cx, pc, aobj, obj2, entry, atom);
             if (!atom && entry->vword.isShape()) {
                 const Shape *shape = entry->vword.toShape();
                 if (shape->hasGetterValue())
                     increment(OP_SCRIPTED_GETTER);
             }
+#endif
         }
     }
 
     if (op == JSOP_CALL) {
         increment(OP_CALL);
 
         uintN argc = GET_ARGC(cx->regs().pc);
         Value &v = cx->regs().sp[-((int)argc + 2)];
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1331,16 +1331,18 @@ class TraceRecorder
                                                  uint32 shape, const char* name, VMSideExit* exit);
 
 #if defined DEBUG_notme && defined XP_UNIX
     void dumpGuardedShapes(const char* prefix);
 #endif
 
     void forgetGuardedShapes();
 
+    typedef jsuword PCVal;
+
     JS_REQUIRES_STACK AbortableRecordingStatus test_property_cache(JSObject* obj, nanojit::LIns* obj_ins,
                                                                      JSObject*& obj2, PCVal& pcval);
     JS_REQUIRES_STACK RecordingStatus guardPropertyCacheHit(nanojit::LIns* obj_ins,
                                                             JSObject* aobj,
                                                             JSObject* obj2,
                                                             PropertyCacheEntry* entry,
                                                             PCVal& pcval);
 
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -207,17 +207,19 @@ ArrayBuffer::create(JSContext *cx, int32
          * as an integer value; if someone actually ever complains (validly), then we
          * can fix.
          */
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
         return NULL;
     }
 
     JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
-    obj->setSharedNonNativeMap();
+
+    if (!InitNonNativeObject(cx, obj, &ArrayBufferClass))
+        return NULL;
     obj->setClass(&ArrayBufferClass);
 
     /*
      * The first 8 bytes hold the length.
      * The rest of it is a flat data store for the array buffer.