Bug 816776 - Add debug-mode exact rooting assertions for Shape; r=sfink
authorTerrence Cole <terrence@mozilla.com>
Thu, 29 Nov 2012 10:22:10 -0800
changeset 115971 a41d57f0102000c6007738736ffbe3fb13ce86e7
parent 115970 4cc6db74ba94ad273bb44ad4488b585a0c69e1d9
child 115972 2fc7800b9847e6ace0f0b6f10a99aa99cda59d0a
push id24034
push useremorley@mozilla.com
push dateFri, 14 Dec 2012 15:28:57 +0000
treeherderautoland@50d8f411d305 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs816776
milestone20.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
Bug 816776 - Add debug-mode exact rooting assertions for Shape; r=sfink
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext-inl.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/Root.h
js/src/ion/CompilerRoot.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonCaches.cpp
js/src/ion/IonCaches.h
js/src/ion/MIR.h
js/src/ion/VMFunctions.cpp
js/src/ion/shared/Assembler-shared.h
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsfriendapi.cpp
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsinterpinlines.h
js/src/jsiter.cpp
js/src/jsmemorymetrics.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jspropertycacheinlines.h
js/src/jspropertytree.cpp
js/src/jspropertytree.h
js/src/jsprvtd.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jswatchpoint.cpp
js/src/jsxml.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/FastOps.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.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/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/ObjectImpl-inl.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/String.h
js/src/vm/StringObject-inl.h
js/src/vm/StringObject.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2070,17 +2070,17 @@ BindLet(JSContext *cx, BindData *data, H
     }
 
     /*
      * Define the let binding's property before storing pn in the the binding's
      * slot indexed by blockCount off the class-reserved slot base.
      */
     bool redeclared;
     RootedId id(cx, NameToId(name));
-    Shape *shape = StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared);
+    RootedShape shape(cx, StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared));
     if (!shape) {
         if (redeclared)
             ReportRedeclaration(cx, parser, pn, false, name);
         return false;
     }
 
     /* Store pn in the static block object. */
     blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
--- a/js/src/frontend/SharedContext-inl.h
+++ b/js/src/frontend/SharedContext-inl.h
@@ -118,17 +118,17 @@ frontend::LexicalLookup(ContextT *ct, Ha
         if (stmt->type == STMT_WITH)
             break;
 
         // Skip statements that do not introduce a new scope
         if (!stmt->isBlockScope)
             continue;
 
         StaticBlockObject &blockObj = *stmt->blockObj;
-        Shape *shape = blockObj.nativeLookup(ct->sc->context, AtomToId(atom));
+        UnrootedShape shape = blockObj.nativeLookup(ct->sc->context, AtomToId(atom));
         if (shape) {
             JS_ASSERT(shape->hasShortID());
 
             if (slotp)
                 *slotp = blockObj.stackDepth() + shape->shortid();
             return stmt;
         }
     }
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -66,30 +66,30 @@ PushMarkStack(GCMarker *gcmarker, JSObje
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSScript *thing);
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, Shape *thing);
+PushMarkStack(GCMarker *gcmarker, UnrootedShape thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 namespace js {
 namespace gc {
 
 static void MarkChildren(JSTracer *trc, JSString *str);
 static void MarkChildren(JSTracer *trc, JSScript *script);
-static void MarkChildren(JSTracer *trc, Shape *shape);
+static void MarkChildren(JSTracer *trc, UnrootedShape shape);
 static void MarkChildren(JSTracer *trc, UnrootedBaseShape base);
 static void MarkChildren(JSTracer *trc, types::TypeObject *type);
 static void MarkChildren(JSTracer *trc, ion::IonCode *code);
 #if JS_HAS_XML_SUPPORT
 static void MarkChildren(JSTracer *trc, JSXML *xml);
 #endif
 
 } /* namespace gc */
@@ -287,22 +287,24 @@ Is##base##Marked(type **thingp)         
 }                                                                                                 \
                                                                                                   \
 bool                                                                                              \
 Is##base##Marked(EncapsulatedPtr<type> *thingp)                                                   \
 {                                                                                                 \
     return IsMarked<type>(thingp->unsafeGet());                                                   \
 }                                                                                                 \
                                                                                                   \
-bool Is##base##AboutToBeFinalized(type **thingp)                                                  \
+bool                                                                                              \
+Is##base##AboutToBeFinalized(type **thingp)                                                       \
 {                                                                                                 \
     return IsAboutToBeFinalized<type>(thingp);                                                    \
 }                                                                                                 \
                                                                                                   \
-bool Is##base##AboutToBeFinalized(EncapsulatedPtr<type> *thingp)                                  \
+bool                                                                                              \
+Is##base##AboutToBeFinalized(EncapsulatedPtr<type> *thingp)                                       \
 {                                                                                                 \
     return IsAboutToBeFinalized<type>(thingp->unsafeGet());                                       \
 }
 
 DeclMarkerImpl(BaseShape, BaseShape)
 DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(IonCode, ion::IonCode)
 DeclMarkerImpl(Object, ArgumentsObject)
@@ -726,20 +728,20 @@ PushMarkStack(GCMarker *gcmarker, JSScri
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
-ScanShape(GCMarker *gcmarker, Shape *shape);
+ScanShape(GCMarker *gcmarker, UnrootedShape shape);
 
 static void
-PushMarkStack(GCMarker *gcmarker, Shape *thing)
+PushMarkStack(GCMarker *gcmarker, UnrootedShape thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanShape(gcmarker, thing);
 }
 
@@ -761,17 +763,17 @@ PushMarkStack(GCMarker *gcmarker, Unroot
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /* We mark base shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanBaseShape(gcmarker, thing);
 }
 
 static void
-ScanShape(GCMarker *gcmarker, Shape *shape)
+ScanShape(GCMarker *gcmarker, UnrootedShape shape)
 {
   restart:
     PushMarkStack(gcmarker, shape->base());
 
     const EncapsulatedId &id = shape->propidRef();
     if (JSID_IS_STRING(id))
         PushMarkStack(gcmarker, JSID_TO_STRING(id));
     else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
@@ -925,17 +927,17 @@ gc::MarkChildren(JSTracer *trc, JSString
 
 static void
 gc::MarkChildren(JSTracer *trc, JSScript *script)
 {
     script->markChildren(trc);
 }
 
 static void
-gc::MarkChildren(JSTracer *trc, Shape *shape)
+gc::MarkChildren(JSTracer *trc, UnrootedShape shape)
 {
     shape->markChildren(trc);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, UnrootedBaseShape base)
 {
     base->markChildren(trc);
@@ -985,17 +987,17 @@ MarkCycleCollectorChildren(JSTracer *trc
  * This function is used by the cycle collector to trace through a
  * shape. The cycle collector does not care about shapes or base
  * shapes, so those are not marked. Instead, any shapes or base shapes
  * that are encountered have their children marked. Stack space is
  * bounded. If two shapes in a row have the same parent pointer, the
  * parent pointer will only be marked once.
  */
 void
-gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
+gc::MarkCycleCollectorChildren(JSTracer *trc, UnrootedShape shape)
 {
     JSObject *prevParent = NULL;
     do {
         MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
         MarkId(trc, &shape->propidRef(), "propid");
         shape = shape->previous();
     } while (shape);
 }
@@ -1016,17 +1018,17 @@ ScanTypeObject(GCMarker *gcmarker, types
     if (TaggedProto(type->proto).isObject())
         PushMarkStack(gcmarker, type->proto);
 
     if (type->singleton && !type->lazy())
         PushMarkStack(gcmarker, type->singleton);
 
     if (type->newScript) {
         PushMarkStack(gcmarker, type->newScript->fun);
-        PushMarkStack(gcmarker, type->newScript->shape);
+        PushMarkStack(gcmarker, type->newScript->shape.get());
     }
 
     if (type->interpretedFunction)
         PushMarkStack(gcmarker, type->interpretedFunction);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, types::TypeObject *type)
@@ -1351,17 +1353,17 @@ GCMarker::processMarkStackTop(SliceBudge
         if (budget.isOverBudget()) {
             pushObject(obj);
             return;
         }
 
         types::TypeObject *type = obj->typeFromGC();
         PushMarkStack(this, type);
 
-        Shape *shape = obj->lastProperty();
+        UnrootedShape shape = obj->lastProperty();
         PushMarkStack(this, shape);
 
         /* Call the trace hook if necessary. */
         Class *clasp = shape->getObjectClass();
         if (clasp->trace) {
             if (clasp == &ArrayClass) {
                 JS_ASSERT(!shape->isNative());
                 vp = obj->getDenseArrayElements();
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -231,17 +231,17 @@ void
 MarkChildren(JSTracer *trc, JSObject *obj);
 
 /*
  * Trace through the shape and any shapes it contains to mark
  * non-shape children. This is exposed to the JS API as
  * JS_TraceShapeCycleCollectorChildren.
  */
 void
-MarkCycleCollectorChildren(JSTracer *trc, Shape *shape);
+MarkCycleCollectorChildren(JSTracer *trc, UnrootedShape shape);
 
 void
 PushArena(GCMarker *gcmarker, ArenaHeader *aheader);
 
 /*** Generic ***/
 
 /*
  * The Mark() functions interface should only be used by code that must be
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -274,25 +274,27 @@ class MutableHandle : public js::Mutable
     template <typename S>
     MutableHandle(MutableHandle<S> handle,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
     {
         this->ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     template <typename S>
-    inline
-    MutableHandle(js::Rooted<S> *root,
-                  typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
+    inline MutableHandle(js::Rooted<S> *root,
+                         typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
     void set(T v) {
         JS_ASSERT(!js::RootMethods<T>::poisoned(v));
         *ptr = v;
     }
 
+    template <typename S>
+    inline void set(const js::Unrooted<S> &v);
+
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
      *
      * Create a MutableHandle from a raw location of a T.
      */
     static MutableHandle fromMarkedLocation(T *p) {
@@ -473,16 +475,23 @@ class Unrooted
     /* See notes for Unrooted::Unrooted(const T &) */
     Unrooted &operator=(T other) {
         JS_ASSERT(other != UninitializedTag());
         if (ptr_ == UninitializedTag())
             EnterAssertNoGCScope();
         ptr_ = other;
         return *this;
     }
+    Unrooted &operator=(Unrooted other) {
+        JS_ASSERT(other.ptr_ != UninitializedTag());
+        if (ptr_ == UninitializedTag())
+            EnterAssertNoGCScope();
+        ptr_ = other.ptr_;
+        return *this;
+    }
 
     operator T() const { return (ptr_ == UninitializedTag()) ? NULL : ptr_; }
     T *operator&() { return &ptr_; }
     const T operator->() const { JS_ASSERT(ptr_ != UninitializedTag()); return ptr_; }
     bool operator==(const T &other) { return ptr_ == other; }
     bool operator!=(const T &other) { return ptr_ != other; }
 
   private:
@@ -854,16 +863,23 @@ Handle<T>::Handle(MutableHandle<S> &root
 template <typename T> template <typename S>
 inline
 MutableHandle<T>::MutableHandle(js::Rooted<S> *root,
                                 typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
 {
     ptr = root->address();
 }
 
+template <typename T> template <typename S>
+inline void MutableHandle<T>::set(const js::Unrooted<S> &v)
+{
+    JS_ASSERT(!js::RootMethods<T>::poisoned(v));
+    *ptr = static_cast<S>(v);
+}
+
 /*
  * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is
  * attempted while the guard object is live.  If you have a GC-unsafe operation
  * to perform, use this guard object to protect your operation.
  */
 class AutoAssertNoGC
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/ion/CompilerRoot.h
+++ b/js/src/ion/CompilerRoot.h
@@ -36,26 +36,29 @@ class CompilerRoot : public CompilerRoot
         JS_ASSERT(!ptr);
         ptr = root;
         next = rootList;
         rootList = this;
     }
 
   public:
     operator T () const { return static_cast<T>(ptr); }
+    operator Unrooted<T> () const { return static_cast<T>(ptr); }
     T operator ->() const { return static_cast<T>(ptr); }
 
   private:
     CompilerRoot() MOZ_DELETE;
     CompilerRoot(const CompilerRoot<T> &) MOZ_DELETE;
     CompilerRoot<T> &operator =(const CompilerRoot<T> &) MOZ_DELETE;
 };
 
-typedef CompilerRoot<JSObject*>   CompilerRootObject;
+typedef CompilerRoot<JSObject*> CompilerRootObject;
 typedef CompilerRoot<JSFunction*> CompilerRootFunction;
+typedef CompilerRoot<JSScript*> CompilerRootScript;
 typedef CompilerRoot<PropertyName*> CompilerRootPropertyName;
+typedef CompilerRoot<Shape*> CompilerRootShape;
 typedef CompilerRoot<Value> CompilerRootValue;
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_gc_h__
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -4849,16 +4849,18 @@ TestSingletonPropertyTypes(JSContext *cx
 //     value at the top of the stack.
 // (4) If a type barrier is in place, and has a single type, an unbox
 //     instruction replaces the top of the stack.
 // (5) Lastly, a type barrier instruction replaces the top of the stack.
 bool
 IonBuilder::pushTypeBarrier(MInstruction *ins, types::StackTypeSet *actual,
                             types::StackTypeSet *observed)
 {
+    AutoAssertNoGC nogc;
+
     // If the instruction has no side effects, we'll resume the entire operation.
     // The actual type barrier will occur in the interpreter. If the
     // instruction is effectful, even if it has a singleton type, there
     // must be a resume point capturing the original def, and resuming
     // to that point will explicitly monitor the new type.
 
     if (!actual) {
         JS_ASSERT(!observed);
@@ -4966,17 +4968,17 @@ IonBuilder::jsop_getgname(HandleProperty
 
     RootedObject globalObj(cx, &script()->global());
     JS_ASSERT(globalObj->isNative());
 
     RootedId id(cx, NameToId(name));
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
-    const js::Shape *shape = globalObj->nativeLookup(cx, id);
+    RootedShape shape(cx, globalObj->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return jsop_getname(name);
 
     types::HeapTypeSet *propertyTypes = oracle->globalPropertyTypeSet(script(), pc, id);
     if (propertyTypes && propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_getname(name);
@@ -5043,17 +5045,17 @@ IonBuilder::jsop_setgname(HandleProperty
     types::HeapTypeSet *propertyTypes = oracle->globalPropertyWrite(script(), pc, id, &canSpecialize);
 
     // This should only happen for a few names like __proto__.
     if (!canSpecialize || globalObj->watched())
         return jsop_setprop(name);
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
-    const js::Shape *shape = globalObj->nativeLookup(cx, id);
+    RootedShape shape(cx, globalObj->nativeLookup(cx, id));
     if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot())
         return jsop_setprop(name);
 
     if (propertyTypes && propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
@@ -6140,17 +6142,17 @@ IonBuilder::invalidatedIdempotentCache()
             return true;
         builder = builder->callerBuilder_;
     } while (builder);
 
     return false;
 }
 
 bool
-IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType)
+IonBuilder::loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType)
 {
     JS_ASSERT(shape->hasDefaultGetter());
     JS_ASSERT(shape->hasSlot());
 
     RootedScript scriptRoot(cx, script());
     types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
     types::StackTypeSet *types = oracle->propertyRead(script(), pc);
 
@@ -6170,17 +6172,17 @@ IonBuilder::loadSlot(MDefinition *obj, S
     current->add(load);
     current->push(load);
 
     load->setResultType(rvalType);
     return pushTypeBarrier(load, types, barrier);
 }
 
 bool
-IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier)
+IonBuilder::storeSlot(MDefinition *obj, UnrootedShape shape, MDefinition *value, bool needsBarrier)
 {
     JS_ASSERT(shape->hasDefaultSetter());
     JS_ASSERT(shape->writable());
     JS_ASSERT(shape->hasSlot());
 
     if (shape->slot() < shape->numFixedSlots()) {
         MStoreFixedSlot *store = MStoreFixedSlot::New(obj, shape->slot(), value);
         current->add(store);
@@ -6395,38 +6397,39 @@ IonBuilder::getPropTryCommonGetter(bool 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getPropTryMonomorphic(bool *emitted, HandleId id, types::StackTypeSet *barrier,
                                   TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes)
 {
+    AssertCanGC();
     JS_ASSERT(*emitted == false);
     bool accessGetter = oracle->propertyReadAccessGetter(script(), pc);
 
     if (unary.ival != MIRType_Object)
         return true;
 
-    Shape *objShape = mjit::GetPICSingleShape(cx, script(), pc, info().constructing());
+    RootedShape objShape(cx, mjit::GetPICSingleShape(cx, script(), pc, info().constructing()));
     if (!objShape || objShape->inDictionary()) {
         spew("GETPROP not monomorphic");
         return true;
     }
 
     MDefinition *obj = current->pop();
 
     // The JM IC was monomorphic, so we inline the property access as long as
     // the shape is not in dictionary made. We cannot be sure that the shape is
     // still a lastProperty, and calling Shape::search() on dictionary mode
     // shapes that aren't lastProperty is invalid.
     obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
 
     spew("Inlining monomorphic GETPROP");
-    Shape *shape = objShape->search(cx, id);
+    RootedShape shape(cx, objShape->search(cx, id));
     JS_ASSERT(shape);
 
     MIRType rvalType = unary.rval;
     if (barrier || IsNullOrUndefined(unary.rval) || accessGetter)
         rvalType = MIRType_Value;
 
     if (!loadSlot(obj, shape, rvalType))
         return false;
@@ -6556,35 +6559,35 @@ IonBuilder::jsop_setprop(HandlePropertyN
     }
 
     oracle->binaryOp(script(), pc);
 
     MSetPropertyInstruction *ins;
     if (monitored) {
         ins = MCallSetProperty::New(obj, value, name, script()->strict);
     } else {
-        Shape *objShape;
+        UnrootedShape objShape;
         if ((objShape = mjit::GetPICSingleShape(cx, script(), pc, info().constructing())) &&
             !objShape->inDictionary())
         {
             // The JM IC was monomorphic, so we inline the property access as
             // long as the shape is not in dictionary mode. We cannot be sure
             // that the shape is still a lastProperty, and calling Shape::search
             // on dictionary mode shapes that aren't lastProperty is invalid.
             obj = addShapeGuard(obj, objShape, Bailout_CachedShapeGuard);
 
-            Shape *shape = objShape->search(cx, NameToId(name));
+            UnrootedShape shape = DropUnrooted(objShape)->search(cx, NameToId(name));
             JS_ASSERT(shape);
 
             spew("Inlining monomorphic SETPROP");
 
             jsid typeId = types::MakeTypeId(cx, id);
             bool needsBarrier = oracle->propertyWriteNeedsBarrier(script(), pc, typeId);
 
-            return storeSlot(obj, shape, value, needsBarrier);
+            return storeSlot(obj, DropUnrooted(shape), value, needsBarrier);
         }
 
         spew("SETPROP not monomorphic");
 
         ins = MSetPropertyCache::New(obj, value, name, script()->strict);
 
         if (!binaryTypes.lhsTypes || binaryTypes.lhsTypes->propertyNeedsBarrier(cx, id))
             ins->setNeedsBarrier();
@@ -6957,17 +6960,17 @@ IonBuilder::addBoundsCheck(MDefinition *
     // If a bounds check failed in the past, don't optimize bounds checks.
     if (failedBoundsCheck_)
         check->setNotMovable();
 
     return check;
 }
 
 MInstruction *
-IonBuilder::addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind)
+IonBuilder::addShapeGuard(MDefinition *obj, const UnrootedShape shape, BailoutKind bailoutKind)
 {
     MGuardShape *guard = MGuardShape::New(obj, shape, bailoutKind);
     current->add(guard);
 
     // If a shape guard failed in the past, don't optimize shape guard.
     if (failedShapeGuard_)
         guard->setNotMovable();
 
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -299,24 +299,24 @@ class IonBuilder : public MIRGenerator
     MInstruction *createDeclEnvObject(MDefinition *callee, MDefinition *scopeObj);
     MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj);
 
     bool makeCall(HandleFunction target, uint32_t argc, bool constructing);
 
     MDefinition *walkScopeChain(unsigned hops);
 
     MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length);
-    MInstruction *addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind);
+    MInstruction *addShapeGuard(MDefinition *obj, const UnrootedShape shape, BailoutKind bailoutKind);
 
     JSObject *getNewArrayTemplateObject(uint32_t count);
 
     bool invalidatedIdempotentCache();
 
-    bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType);
-    bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier);
+    bool loadSlot(MDefinition *obj, HandleShape shape, MIRType rvalType);
+    bool storeSlot(MDefinition *obj, UnrootedShape shape, MDefinition *value, bool needsBarrier);
 
     // jsop_getprop() helpers.
     bool getPropTryArgumentsLength(bool *emitted);
     bool getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *barrier,
                             types::StackTypeSet *types, TypeOracle::UnaryTypes unaryTypes);
     bool getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name,
                             types::StackTypeSet *barrier, types::StackTypeSet *types,
                             TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -144,29 +144,29 @@ IsCacheableProtoChain(JSObject *obj, JSO
         if (!proto || !proto->isNative())
             return false;
         obj = proto;
     }
     return true;
 }
 
 static bool
-IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, const Shape *shape)
+IsCacheableGetPropReadSlot(JSObject *obj, JSObject *holder, UnrootedShape shape)
 {
     if (!shape || !IsCacheableProtoChain(obj, holder))
         return false;
 
     if (!shape->hasSlot() || !shape->hasDefaultGetter())
         return false;
 
     return true;
 }
 
 static bool
-IsCacheableNoProperty(JSObject *obj, JSObject *holder, const Shape *shape, jsbytecode *pc,
+IsCacheableNoProperty(JSObject *obj, JSObject *holder, UnrootedShape shape, jsbytecode *pc,
                       const TypedOrValueRegister &output)
 {
     if (shape)
         return false;
 
     JS_ASSERT(!holder);
 
     // Just because we didn't find the property on the object doesn't mean it
@@ -207,30 +207,30 @@ IsCacheableNoProperty(JSObject *obj, JSO
     // monitor and invalidate the script.
     if (!output.hasValue())
         return false;
 
     return true;
 }
 
 static bool
-IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, const Shape *shape)
+IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, UnrootedShape shape)
 {
     if (!shape || !IsCacheableProtoChain(obj, holder))
         return false;
 
     if (!shape->hasGetterValue() || !shape->getterValue().isObject())
         return false;
 
     return shape->getterValue().toObject().isFunction() &&
            shape->getterValue().toObject().toFunction()->isNative();
 }
 
 static bool
-IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, const Shape *shape)
+IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, UnrootedShape shape)
 {
     if (!shape || !IsCacheableProtoChain(obj, holder))
         return false;
 
     if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter())
         return false;
 
     return true;
@@ -238,17 +238,17 @@ IsCacheableGetPropCallPropertyOp(JSObjec
 
 struct GetNativePropertyStub
 {
     CodeOffsetJump exitOffset;
     CodeOffsetJump rejoinOffset;
     CodeOffsetLabel stubCodePatchOffset;
 
     void generateReadSlot(JSContext *cx, MacroAssembler &masm, JSObject *obj, PropertyName *propName,
-                          JSObject *holder, const Shape *shape, Register object, TypedOrValueRegister output,
+                          JSObject *holder, HandleShape shape, Register object, TypedOrValueRegister output,
                           RepatchLabel *failures, Label *nonRepatchFailures = NULL)
     {
         // If there's a single jump to |failures|, we can patch the shape guard
         // jump directly. Otherwise, jump to the end of the stub, so there's a
         // common point to patch.
         bool multipleFailureJumps = (nonRepatchFailures != NULL) && nonRepatchFailures->used();
         exitOffset = masm.branchPtrWithPatch(Assembler::NotEqual,
                                              Address(object, JSObject::offsetOfShape()),
@@ -349,17 +349,17 @@ struct GetNativePropertyStub
             exitOffset = masm.jumpWithPatch(&exit_);
             masm.bind(&exit_);
         } else {
             masm.bind(failures);
         }
     }
 
     bool generateCallGetter(JSContext *cx, MacroAssembler &masm, JSObject *obj,
-                            PropertyName *propName, JSObject *holder, const Shape *shape,
+                            PropertyName *propName, JSObject *holder, HandleShape shape,
                             RegisterSet &liveRegs, Register object, TypedOrValueRegister output,
                             void *returnAddr, jsbytecode *pc,
                             RepatchLabel *failures, Label *nonRepatchFailures = NULL)
     {
         // Initial shape check.
         Label stubFailure;
         masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()),
                        ImmGCPtr(obj->lastProperty()), &stubFailure);
@@ -614,17 +614,17 @@ struct GetNativePropertyStub
         masm.bind(&exit_);
 
         return true;
     }
 };
 
 bool
 IonCacheGetProperty::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
-                                    const Shape *shape)
+                                    HandleShape shape)
 {
     MacroAssembler masm;
     RepatchLabel failures;
 
     GetNativePropertyStub getprop;
     getprop.generateReadSlot(cx, masm, obj, name(), holder, shape, object(), output(), &failures);
 
     Linker linker(masm);
@@ -649,19 +649,20 @@ IonCacheGetProperty::attachReadSlot(JSCo
     IonSpew(IonSpew_InlineCaches, "Generated native GETPROP stub at %p %s", code->raw(),
             idempotent() ? "(idempotent)" : "(not idempotent)");
 
     return true;
 }
 
 bool
 IonCacheGetProperty::attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj,
-                                      JSObject *holder, const Shape *shape,
+                                      JSObject *holder, HandleShape shape,
                                       const SafepointIndex *safepointIndex, void *returnAddr)
 {
+    AssertCanGC();
     MacroAssembler masm;
     RepatchLabel failures;
 
     JS_ASSERT(!idempotent());
     JS_ASSERT(allowGetters());
 
     // Need to set correct framePushed on the masm so that exit frame descriptors are
     // properly constructed.
@@ -1131,18 +1132,18 @@ IonCacheSetProperty::attachSetterCall(JS
 
     IonSpew(IonSpew_InlineCaches, "Generated SETPROP calling case stub at %p", code->raw());
 
     return true;
 }
 
 bool
 IonCacheSetProperty::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj,
-                                        const Shape *oldShape, const Shape *newShape,
-                                        const Shape *propShape)
+                                        HandleShape oldShape, HandleShape newShape,
+                                        HandleShape propShape)
 {
     MacroAssembler masm;
 
     Label failures;
 
     /* Guard the type of the object */
     masm.branchPtr(Assembler::NotEqual, Address(object(), JSObject::offsetOfType()),
                    ImmGCPtr(obj->type()), &failures);
@@ -1151,17 +1152,17 @@ IonCacheSetProperty::attachNativeAdding(
     masm.branchTestObjShape(Assembler::NotEqual, object(), oldShape, &failures);
 
     Label protoFailures;
     masm.push(object());    // save object reg because we clobber it
 
     JSObject *proto = obj->getProto();
     Register protoReg = object();
     while (proto) {
-        Shape *protoShape = proto->lastProperty();
+        UnrootedShape protoShape = proto->lastProperty();
 
         // load next prototype
         masm.loadPtr(Address(protoReg, JSObject::offsetOfType()), protoReg);
         masm.loadPtr(Address(protoReg, offsetof(types::TypeObject, proto)), protoReg);
 
         // ensure that the prototype is not NULL and that its shape matches
         masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &protoFailures);
         masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &protoFailures);
@@ -1243,17 +1244,17 @@ IsPropertyInlineable(JSObject *obj, IonC
         return false;
 
     return true;
 }
 
 static bool
 IsPropertySetInlineable(JSContext *cx, HandleObject obj, HandleId id, MutableHandleShape pshape)
 {
-    Shape *shape = obj->nativeLookup(cx, id);
+    UnrootedShape shape = obj->nativeLookup(cx, id);
 
     if (!shape)
         return false;
 
     if (!shape->hasSlot())
         return false;
 
     if (!shape->hasDefaultSetter())
@@ -1294,17 +1295,17 @@ IsPropertySetterCallInlineable(JSContext
 static bool
 IsPropertyAddInlineable(JSContext *cx, HandleObject obj, jsid id, uint32_t oldSlots,
                         MutableHandleShape pShape)
 {
     // This is not a Add, the property exists.
     if (pShape.get())
         return false;
 
-    Shape *shape = obj->nativeLookup(cx, id);
+    RootedShape shape(cx, obj->nativeLookup(cx, id));
     if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter())
         return false;
 
     // If object has a non-default resolve hook, don't inline
     if (obj->getClass()->resolve != JS_ResolveStub)
         return false;
 
     if (!obj->isExtensible() || !shape->writable())
@@ -1314,17 +1315,17 @@ IsPropertyAddInlineable(JSContext *cx, H
     // are native, and that all prototypes have no getter or setter
     // defined on the property
     for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
         // if prototype is non-native, don't optimize
         if (!proto->isNative())
             return false;
 
         // if prototype defines this property in a non-plain way, don't optimize
-        const Shape *protoShape = proto->nativeLookup(cx, id);
+        UnrootedShape protoShape = proto->nativeLookup(cx, id);
         if (protoShape && !protoShape->hasDefaultSetter())
             return false;
 
         // Otherise, if there's no such property, watch out for a resolve hook that would need
         // to be invoked and thus prevent inlining of property addition.
         if (proto->getClass()->resolve != JS_ResolveStub)
              return false;
     }
@@ -1371,26 +1372,26 @@ js::ion::SetPropertyCache(JSContext *cx,
                 cache.incrementStubCount();
                 if (!cache.attachSetterCall(cx, ion, obj, holder, shape, returnAddr))
                     return false;
             }
         }
     }
 
     uint32_t oldSlots = obj->numDynamicSlots();
-    const Shape *oldShape = obj->lastProperty();
+    RootedShape oldShape(cx, obj->lastProperty());
 
     // Set/Add the property on the object, the inlined cache are setup for the next execution.
     if (!SetProperty(cx, obj, name, value, cache.strict(), isSetName))
         return false;
 
     // The property did not exists before, now we can try again to inline the
     // procedure which is adding the property.
     if (inlinable && IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape)) {
-        const Shape *newShape = obj->lastProperty();
+        RootedShape newShape(cx, obj->lastProperty());
         cache.incrementStubCount();
         if (!cache.attachNativeAdding(cx, ion, obj, oldShape, newShape, shape))
             return false;
     }
 
     return true;
 }
 
@@ -1614,17 +1615,17 @@ IonCacheBindName::attachGlobal(JSContext
     updateLastJump(exitJump);
 
     IonSpew(IonSpew_InlineCaches, "Generated BINDNAME global stub at %p", code->raw());
     return true;
 }
 
 static inline void
 GenerateScopeChainGuard(MacroAssembler &masm, JSObject *scopeObj,
-                        Register scopeObjReg, Shape *shape, Label *failures)
+                        Register scopeObjReg, UnrootedShape shape, Label *failures)
 {
     AutoAssertNoGC nogc;
     if (scopeObj->isCall()) {
         // We can skip a guard on the call object if the script's bindings are
         // guaranteed to be immutable (and thus cannot introduce shadowing
         // variables).
         CallObject *callObj = &scopeObj->asCall();
         if (!callObj->isForEval()) {
@@ -1784,18 +1785,20 @@ js::ion::BindNameCache(JSContext *cx, si
             IonSpew(IonSpew_InlineCaches, "BINDNAME uncacheable scope chain");
         }
     }
 
     return holder;
 }
 
 bool
-IonCacheName::attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject holder, Shape *shape)
+IonCacheName::attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject holder,
+                     HandleShape shape)
 {
+    AssertCanGC();
     MacroAssembler masm;
     Label failures;
 
     Register scratchReg = outputReg().valueReg().scratchReg();
 
     masm.mov(scopeChainReg(), scratchReg);
     GenerateScopeChainGuards(masm, scopeChain, holder, scratchReg, &failures);
 
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -268,19 +268,19 @@ class IonCacheGetProperty : public IonCa
     }
 
     Register object() const { return u.getprop.object; }
     PropertyName *name() const { return u.getprop.name; }
     TypedOrValueRegister output() const { return u.getprop.output.data(); }
     bool allowGetters() const { return u.getprop.allowGetters; }
 
     bool attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
-                        const Shape *shape);
+                        HandleShape shape);
     bool attachCallGetter(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
-                          const Shape *shape,
+                          HandleShape shape,
                           const SafepointIndex *safepointIndex, void *returnAddr);
 };
 
 class IonCacheSetProperty : public IonCache
 {
   public:
     IonCacheSetProperty(CodeOffsetJump initialJump,
                         CodeOffsetLabel rejoinLabel,
@@ -300,18 +300,18 @@ class IonCacheSetProperty : public IonCa
     Register object() const { return u.setprop.object; }
     PropertyName *name() const { return u.setprop.name; }
     ConstantOrRegister value() const { return u.setprop.value.data(); }
     bool strict() const { return u.setprop.strict; }
 
     bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape);
     bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj,
                           HandleObject holder, HandleShape shape, void *returnAddr);
-    bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, const Shape *oldshape,
-                            const Shape *newshape, const Shape *propshape);
+    bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape,
+                            HandleShape newshape, HandleShape propshape);
 };
 
 class IonCacheGetElement : public IonCache
 {
   public:
     IonCacheGetElement(CodeOffsetJump initialJump,
                        CodeOffsetLabel rejoinLabel,
                        CodeOffsetLabel cacheLabel,
@@ -407,17 +407,17 @@ class IonCacheName : public IonCache
     TypedOrValueRegister outputReg() const {
         return u.name.output.data();
     }
     bool isTypeOf() const {
         return kind_ == NameTypeOf;
     }
 
     bool attach(JSContext *cx, IonScript *ion, HandleObject scopeChain, HandleObject obj,
-                Shape *shape);
+                HandleShape shape);
 };
 
 bool
 GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp);
 
 bool
 SetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value,
                  bool isSetName);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -4450,18 +4450,18 @@ class MGetElementCache
         return this;
     }
 };
 
 class MBindNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
-    PropertyName *name_;
-    JSScript *script_;
+    CompilerRootPropertyName name_;
+    CompilerRootScript script_;
     jsbytecode *pc_;
 
     MBindNameCache(MDefinition *scopeChain, PropertyName *name, JSScript *script, jsbytecode *pc)
       : MUnaryInstruction(scopeChain), name_(name), script_(script), pc_(pc)
     {
         setResultType(MIRType_Object);
     }
 
@@ -4490,43 +4490,43 @@ class MBindNameCache
     }
 };
 
 // Guard on an object's shape.
 class MGuardShape
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
-    const Shape *shape_;
+    CompilerRootShape shape_;
     BailoutKind bailoutKind_;
 
-    MGuardShape(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind)
+    MGuardShape(MDefinition *obj, UnrootedShape shape, BailoutKind bailoutKind)
       : MUnaryInstruction(obj),
         shape_(shape),
         bailoutKind_(bailoutKind)
     {
         setGuard();
         setMovable();
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(GuardShape)
 
-    static MGuardShape *New(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind) {
+    static MGuardShape *New(MDefinition *obj, UnrootedShape shape, BailoutKind bailoutKind) {
         return new MGuardShape(obj, shape, bailoutKind);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
     MDefinition *obj() const {
         return getOperand(0);
     }
-    const Shape *shape() const {
+    const UnrootedShape shape() const {
         return shape_;
     }
     BailoutKind bailoutKind() const {
         return bailoutKind_;
     }
     bool congruentTo(MDefinition * const &ins) const {
         if (!ins->isGuardShape())
             return false;
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -137,17 +137,17 @@ bool
 InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value)
 {
     // Copy the incoming value. This may be overwritten; the return value is discarded.
     RootedValue rval(cx, value);
     RootedId id(cx, NameToId(name));
 
     if (name == cx->names().proto)
         return baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, false);
-    return !!DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0);
+    return DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0);
 }
 
 template<bool Equal>
 bool
 LooselyEqual(JSContext *cx, HandleValue lhs, HandleValue rhs, JSBool *res)
 {
     bool equal;
     if (!js::LooselyEqual(cx, lhs, rhs, &equal))
--- a/js/src/ion/shared/Assembler-shared.h
+++ b/js/src/ion/shared/Assembler-shared.h
@@ -93,16 +93,22 @@ struct ImmWord
 
 // Used for immediates which require relocation.
 struct ImmGCPtr
 {
     uintptr_t value;
 
     explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast<uintptr_t>(ptr))
     { }
+
+    // ImmGCPtr is rooted so we can convert safely directly from Unrooted<T>.
+    template <typename T>
+    explicit ImmGCPtr(Unrooted<T> ptr)
+      : value(reinterpret_cast<uintptr_t>(static_cast<T>(ptr)))
+    { }
 };
 
 // Specifies a hardcoded, absolute address.
 struct AbsoluteAddress {
     void *addr;
 
     explicit AbsoluteAddress(void *addr)
       : addr(addr)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3824,18 +3824,18 @@ DefinePropertyById(JSContext *cx, Handle
                             ? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
                             : NULL,
                             (attrs & JSPROP_SETTER)
                             ? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
                             : NULL);
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
     if (flags != 0 && obj->isNative()) {
-        return !!DefineNativeProperty(cx, obj, id, value, getter, setter,
-                                      attrs, flags, tinyid);
+        return DefineNativeProperty(cx, obj, id, value, getter, setter,
+                                    attrs, flags, tinyid);
     }
     return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DefinePropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval valueArg,
                       JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
@@ -4472,53 +4472,55 @@ JS_DeleteElement(JSContext *cx, JSObject
 
 JS_PUBLIC_API(JSBool)
 JS_DeleteProperty(JSContext *cx, JSObject *objArg, const char *name)
 {
     jsval junk;
     return JS_DeleteProperty2(cx, objArg, name, &junk);
 }
 
-static Shape *
+static UnrootedShape
 LastConfigurableShape(JSObject *obj)
 {
     for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
-        Shape *shape = &r.front();
+        UnrootedShape shape = &r.front();
         if (shape->configurable())
             return shape;
     }
-    return NULL;
+    return UnrootedShape(NULL);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearNonGlobalObject(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     JS_ASSERT(!obj->isGlobal());
 
     if (!obj->isNative())
         return;
 
     /* Remove all configurable properties from obj. */
-    while (Shape *shape = LastConfigurableShape(obj)) {
+    RootedShape shape(cx);
+    while ((shape = LastConfigurableShape(obj))) {
         if (!obj->removeProperty(cx, shape->propid()))
             return;
     }
 
     /* Set all remaining writable plain data properties to undefined. */
     for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
-        Shape *shape = &r.front();
+        UnrootedShape shape = &r.front();
         if (shape->isDataDescriptor() &&
             shape->writable() &&
             shape->hasDefaultSetter() &&
-            shape->hasSlot()) {
+            shape->hasSlot())
+        {
             obj->nativeSetSlot(shape->slot(), UndefinedValue());
         }
     }
 }
 
 JS_PUBLIC_API(void)
 JS_SetAllNonReservedSlotsToUndefined(JSContext *cx, JSObject *objArg)
 {
@@ -4551,17 +4553,17 @@ JS_Enumerate(JSContext *cx, JSObject *ob
         return NULL;
     return ida;
 }
 
 /*
  * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's
  *     prop_iterator_class somehow...
  * + preserve the obj->enumerate API while optimizing the native object case
- * + native case here uses a Shape *, but that iterates in reverse!
+ * + native case here uses a JSShape *, but that iterates in reverse!
  * + so we make non-native match, by reverse-iterating after JS_Enumerating
  */
 const uint32_t JSSLOT_ITER_INDEX = 0;
 
 static void
 prop_iter_finalize(FreeOp *fop, RawObject obj)
 {
     void *pdata = obj->getPrivate();
@@ -4583,17 +4585,17 @@ prop_iter_trace(JSTracer *trc, RawObject
         return;
 
     if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) {
         /*
          * Native case: just mark the next property to visit. We don't need a
          * barrier here because the pointer is updated via setPrivate, which
          * always takes a barrier.
          */
-        Shape *tmp = (Shape *)pdata;
+        UnrootedShape tmp = static_cast<RawShape>(pdata);
         MarkShapeUnbarriered(trc, &tmp, "prop iter shape");
         obj->setPrivateUnbarriered(tmp);
     } else {
         /* Non-native case: mark each id in the JSIdArray private. */
         JSIdArray *ida = (JSIdArray *) pdata;
         MarkIdRange(trc, ida->length, ida->vector, "prop iter");
     }
 }
@@ -4648,42 +4650,39 @@ JS_NewPropertyIterator(JSContext *cx, JS
     return iterobj;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_NextProperty(JSContext *cx, JSObject *iterobjArg, jsid *idp)
 {
     RootedObject iterobj(cx, iterobjArg);
     AutoAssertNoGC nogc;
-    int32_t i;
-    Shape *shape;
-    JSIdArray *ida;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, iterobj);
-    i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32();
+    int32_t i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32();
     if (i < 0) {
         /* Native case: private data is a property tree node pointer. */
         JS_ASSERT(iterobj->getParent()->isNative());
-        shape = (Shape *) iterobj->getPrivate();
+        UnrootedShape shape = static_cast<RawShape>(iterobj->getPrivate());
 
         while (shape->previous() && !shape->enumerable())
             shape = shape->previous();
 
         if (!shape->previous()) {
             JS_ASSERT(shape->isEmptyShape());
             *idp = JSID_VOID;
         } else {
-            iterobj->setPrivateGCThing(const_cast<Shape *>(shape->previous().get()));
+            iterobj->setPrivateGCThing(const_cast<RawShape>(shape->previous().get()));
             *idp = shape->propid();
         }
     } else {
         /* Non-native case: use the ida enumerated when iterobj was created. */
-        ida = (JSIdArray *) iterobj->getPrivate();
+        JSIdArray *ida = (JSIdArray *) iterobj->getPrivate();
         JS_ASSERT(i <= ida->length);
         STATIC_ASSUME(i <= ida->length);
         if (i == 0) {
             *idp = JSID_VOID;
         } else {
             *idp = ida->vector[--i];
             iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(i));
         }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -201,24 +201,24 @@ js::StringIsArrayIndex(JSLinearString *s
         JS_ASSERT(index <= MAX_ARRAY_INDEX);
         *indexp = index;
         return true;
     }
 
     return false;
 }
 
-Shape *
+UnrootedShape
 js::GetDenseArrayShape(JSContext *cx, HandleObject globalObj)
 {
     JS_ASSERT(globalObj);
 
     JSObject *proto = globalObj->global().getOrCreateArrayPrototype(cx);
     if (!proto)
-        return NULL;
+        return UnrootedShape(NULL);
 
     return EmptyShape::getInitialShape(cx, &ArrayClass, proto, proto->getParent(),
                                        gc::FINALIZE_OBJECT0);
 }
 
 bool
 JSObject::willBeSparseDenseArray(unsigned requiredCapacity, unsigned newElementsHint)
 {
@@ -260,17 +260,17 @@ JSObject::arrayGetOwnDataElement(JSConte
             *vp = getDenseArrayElement(uint32_t(i));
         return true;
     }
 
     jsid id;
     if (!IndexToId(cx, i, &id))
         return false;
 
-    Shape *shape = nativeLookup(cx, id);
+    UnrootedShape shape = nativeLookup(cx, id);
     if (!shape || !shape->isDataDescriptor())
         vp->setMagic(JS_ARRAY_HOLE);
     else
         *vp = getSlot(shape->slot());
     return true;
 }
 
 bool
@@ -1249,18 +1249,18 @@ AddLengthProperty(JSContext *cx, HandleO
      */
 
     RootedId lengthId(cx, NameToId(cx->names().length));
     JS_ASSERT(!obj->nativeLookup(cx, lengthId));
 
     if (!obj->allocateSlowArrayElements(cx))
         return false;
 
-    return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter,
-                            SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
+    return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
+                                 SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
 }
 
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 /* static */ bool
 JSObject::makeDenseArraySlow(JSContext *cx, HandleObject obj)
 {
@@ -1289,37 +1289,39 @@ JSObject::makeDenseArraySlow(JSContext *
     /* Root all values in the array during conversion. */
     AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized);
 
     /*
      * Save old map now, before calling InitScopeForObject. We'll have to undo
      * on error. This is gross, but a better way is not obvious. Note: the
      * exact contents of the array are not preserved on error.
      */
-    js::Shape *oldShape = obj->lastProperty();
+    RootedShape oldShape(cx, obj->lastProperty());
 
     /* Create a native scope. */
-    gc::AllocKind kind = obj->getAllocKind();
-    Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, obj->getProto(),
-                                               oldShape->getObjectParent(), kind);
-    if (!shape)
-        return false;
-
-    /*
-     * In case an incremental GC is already running, we need to write barrier
-     * the elements before (temporarily) destroying them.
-     *
-     * Note: this has to happen after getInitialShape (which can trigger
-     * incremental GC) and *before* we overwrite shape, making us no longer a
-     * dense array.
-     */
-    if (obj->compartment()->needsBarrier())
-        obj->prepareElementRangeForOverwrite(0, arrayInitialized);
-
-    obj->shape_ = shape;
+    {
+        gc::AllocKind kind = obj->getAllocKind();
+        UnrootedShape shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, obj->getProto(),
+                                                          oldShape->getObjectParent(), kind);
+        if (!shape)
+            return false;
+
+        /*
+         * In case an incremental GC is already running, we need to write barrier
+         * the elements before (temporarily) destroying them.
+         *
+         * Note: this has to happen after getInitialShape (which can trigger
+         * incremental GC) and *before* we overwrite shape, making us no longer a
+         * dense array.
+         */
+        if (obj->compartment()->needsBarrier())
+            obj->prepareElementRangeForOverwrite(0, arrayInitialized);
+
+        obj->shape_ = shape;
+    }
 
     /* Reset to an empty dense array. */
     obj->elements = emptyObjectElements;
 
     /*
      * Begin with the length property to share more of the property tree.
      * The getter/setter here will directly access the object's private value.
      */
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -71,17 +71,17 @@ NewDenseCopiedArray(JSContext *cx, uint3
 extern JSObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values, RawObject proto = NULL);
 
 /* Create a sparse array. */
 extern JSObject *
 NewSlowEmptyArray(JSContext *cx);
 
 /* Get the common shape used by all dense arrays with a prototype at globalObj. */
-extern Shape *
+extern UnrootedShape
 GetDenseArrayShape(JSContext *cx, HandleObject globalObj);
 
 extern JSBool
 GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp);
 
 extern JSBool
 SetLengthProperty(JSContext *cx, HandleObject obj, double length);
 
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -423,16 +423,17 @@ js::InternNonIntElementId(JSContext *cx,
     vp.setString(atom);
     return true;
 }
 
 template<XDRMode mode>
 bool
 js::XDRAtom(XDRState<mode> *xdr, MutableHandleAtom atomp)
 {
+    AssertCanGC();
     if (mode == XDR_ENCODE) {
         uint32_t nchars = atomp->length();
         if (!xdr->codeUint32(&nchars))
             return false;
 
         jschar *chars = const_cast<jschar *>(atomp->getChars(xdr->cx()));
         if (!chars)
             return false;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -568,17 +568,17 @@ js_ReportErrorVA(JSContext *cx, unsigned
 }
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 void
 js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg)
 {
     const char *usageStr = "usage";
     PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
-    DebugOnly<Shape *> shape = callee->nativeLookup(cx, NameToId(usageAtom));
+    DebugOnly<RawShape> shape = static_cast<RawShape>(callee->nativeLookup(cx, NameToId(usageAtom)));
     JS_ASSERT(!shape->configurable());
     JS_ASSERT(!shape->writable());
     JS_ASSERT(shape->hasDefaultGetter());
 
     jsval usage;
     if (!JS_LookupProperty(cx, callee, "usage", &usage))
         return;
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -312,17 +312,17 @@ class NewObjectCache
     inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
 
     /* Fill an entry after a cache miss. */
     inline void fillProto(EntryIndex entry, Class *clasp, js::TaggedProto proto, gc::AllocKind kind, JSObject *obj);
     inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
     inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
 
     /* Invalidate any entries which might produce an object with shape/proto. */
-    void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
+    void invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto);
 
   private:
     inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry);
     inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
     static inline void copyCachedToObject(JSObject *dst, JSObject *src);
 };
 
 /*
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -793,17 +793,17 @@ JS_EvaluateInStackFrame(JSContext *cx, J
     return ok;
 }
 
 /************************************************************************/
 
 /* This all should be reworked to avoid requiring JSScopeProperty types. */
 
 static JSBool
-GetPropertyDesc(JSContext *cx, JSObject *obj_, Shape *shape, JSPropertyDesc *pd)
+GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd)
 {
     assertSameCompartment(cx, obj_);
     pd->id = IdToJsval(shape->propid());
 
     RootedObject obj(cx, obj_);
 
     JSBool wasThrowing = cx->isExceptionPending();
     Value lastException = UndefinedValue();
@@ -896,17 +896,17 @@ JS_GetPropertyDescArray(JSContext *cx, J
     for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
         pd[i].id = JSVAL_NULL;
         pd[i].value = JSVAL_NULL;
         pd[i].alias = JSVAL_NULL;
         if (!js_AddRoot(cx, &pd[i].id, NULL))
             goto bad;
         if (!js_AddRoot(cx, &pd[i].value, NULL))
             goto bad;
-        Shape *shape = const_cast<Shape *>(&r.front());
+        RootedShape shape(cx, const_cast<Shape *>(&r.front()));
         if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
             goto bad;
         if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
             goto bad;
         if (++i == obj->propertyCount())
             break;
     }
     pda->length = i;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -251,17 +251,17 @@ JS_FRIEND_API(JSBool)
 JS_WrapAutoIdVector(JSContext *cx, js::AutoIdVector &props)
 {
     return cx->compartment->wrap(cx, props);
 }
 
 JS_FRIEND_API(void)
 JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
 {
-    MarkCycleCollectorChildren(trc, (Shape *)shape);
+    MarkCycleCollectorChildren(trc, static_cast<RawShape>(shape));
 }
 
 static bool
 DefineHelpProperty(JSContext *cx, HandleObject obj, const char *prop, const char *value)
 {
     JSAtom *atom = Atomize(cx, value, strlen(value));
     if (!atom)
         return false;
@@ -900,17 +900,17 @@ js::IncrementalReferenceBarrier(void *pt
     uint32_t kind = gc::GetGCThingTraceKind(ptr);
     if (kind == JSTRACE_OBJECT)
         JSObject::writeBarrierPre(reinterpret_cast<RawObject>(ptr));
     else if (kind == JSTRACE_STRING)
         JSString::writeBarrierPre(reinterpret_cast<RawString>(ptr));
     else if (kind == JSTRACE_SCRIPT)
         JSScript::writeBarrierPre(reinterpret_cast<RawScript>(ptr));
     else if (kind == JSTRACE_SHAPE)
-        Shape::writeBarrierPre((Shape *) ptr);
+        Shape::writeBarrierPre(reinterpret_cast<RawShape>(ptr));
     else if (kind == JSTRACE_BASE_SHAPE)
         BaseShape::writeBarrierPre(reinterpret_cast<RawBaseShape>(ptr));
     else if (kind == JSTRACE_TYPE_OBJECT)
         types::TypeObject::writeBarrierPre((types::TypeObject *) ptr);
     else
         JS_NOT_REACHED("invalid trace kind");
 }
 
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -590,17 +590,17 @@ js_NewGCExternalString(JSContext *cx)
 }
 
 inline JSScript *
 js_NewGCScript(JSContext *cx)
 {
     return js::gc::NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
 }
 
-inline js::Shape *
+inline js::UnrootedShape
 js_NewGCShape(JSContext *cx)
 {
     return js::gc::NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
 }
 
 inline js::UnrootedBaseShape
 js_NewGCBaseShape(JSContext *cx)
 {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -802,42 +802,43 @@ class TypeConstraintFilterPrimitive : pu
 
 void
 HeapTypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target)
 {
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintFilterPrimitive>(target));
 }
 
 /* If id is a normal slotful 'own' property of an object, get its shape. */
-static inline Shape *
-GetSingletonShape(JSContext *cx, HandleObject obj, jsid id)
-{
+static inline UnrootedShape
+GetSingletonShape(JSContext *cx, RawObject obj, RawId id)
+{
+    AssertCanGC();
     if (!obj->isNative())
-        return NULL;
-    Shape *shape = obj->nativeLookup(cx, id);
+        return UnrootedShape(NULL);
+    UnrootedShape shape = DropUnrooted(obj)->nativeLookup(cx, DropUnrooted(id));
     if (shape && shape->hasDefaultGetter() && shape->hasSlot())
         return shape;
-    return NULL;
+    return UnrootedShape(NULL);
 }
 
 void
 ScriptAnalysis::pruneTypeBarriers(JSContext *cx, uint32_t offset)
 {
+    AssertCanGC();
     TypeBarrier **pbarrier = &getCode(offset).typeBarriers;
     while (*pbarrier) {
         TypeBarrier *barrier = *pbarrier;
         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));
-            RootedObject barrierSingleton(cx, barrier->singleton);
-            Shape *shape = GetSingletonShape(cx, barrierSingleton, barrier->singletonId);
+            UnrootedShape shape = GetSingletonShape(cx, barrier->singleton, barrier->singletonId);
             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
@@ -1669,16 +1670,17 @@ class TypeConstraintFreezeObjectFlags : 
     {}
 
     const char *kind() { return "freezeObjectFlags"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type) {}
 
     void newObjectState(JSContext *cx, TypeObject *object, bool force)
     {
+        AutoAssertNoGC nogc;
         if (!marked && (object->hasAnyFlags(flags) || (!flags && force))) {
             marked = true;
             cx->compartment->types.addPendingRecompile(cx, info);
         }
     }
 };
 
 bool
@@ -2543,16 +2545,17 @@ TypeCompartment::nukeTypes(FreeOp *fop)
 #endif /* JS_METHODJIT */
 
     pendingNukeTypes = false;
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
 {
+    AutoAssertNoGC nogc;
     CompilerOutput *co = info.compilerOutput(cx);
 
     if (co->pendingRecompilation)
         return;
 
     if (co->isValid())
         CancelOffThreadIonCompile(cx->compartment, co->script);
 
@@ -3022,23 +3025,24 @@ struct types::ObjectTableKey
     typedef JSObject * Lookup;
 
     static inline uint32_t hash(JSObject *obj) {
         return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^
                          obj->slotSpan() ^ obj->numFixedSlots() ^
                          ((uint32_t)obj->getTaggedProto().toWord() >> 2));
     }
 
-    static inline bool match(const ObjectTableKey &v, JSObject *obj) {
+    static inline bool match(const ObjectTableKey &v, RawObject obj) {
         if (obj->slotSpan() != v.nslots ||
             obj->numFixedSlots() != v.nfixed ||
             obj->getTaggedProto() != v.proto) {
             return false;
         }
-        Shape *shape = obj->lastProperty();
+        UnrootedShape shape = obj->lastProperty();
+        obj = NULL;
         while (!shape->isEmptyShape()) {
             if (shape->propid() != v.ids[shape->slot()])
                 return false;
             shape = shape->previous();
         }
         return true;
     }
 };
@@ -3070,28 +3074,28 @@ TypeCompartment::fixObjectType(JSContext
      * generic unknown type.
      */
     JS_ASSERT(obj->isObject());
 
     if (obj->slotSpan() == 0 || obj->inDictionaryMode())
         return;
 
     ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(obj.get());
-    Shape *baseShape = obj->lastProperty();
+    RootedShape baseShape(cx, obj->lastProperty());
 
     if (p) {
         /* The lookup ensures the shape matches, now check that the types match. */
         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();
-                        Shape *shape = baseShape;
+                        RootedShape shape(cx, baseShape);
                         while (!shape->isEmptyShape()) {
                             if (shape->slot() == i) {
                                 Type type = Type::DoubleType();
                                 if (!p->value.object->unknownProperties()) {
                                     jsid id = MakeTypeId(cx, shape->propid());
                                     p->value.object->addPropertyType(cx, id, type);
                                 }
                                 break;
@@ -3122,17 +3126,17 @@ TypeCompartment::fixObjectType(JSContext
         }
 
         Type *types = cx->pod_calloc<Type>(obj->slotSpan());
         if (!types) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
-        Shape *shape = baseShape;
+        RootedShape shape(cx, baseShape);
         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()]);
             }
             shape = shape->previous();
@@ -3189,17 +3193,17 @@ TypeObject::getFromPrototypes(JSContext 
         return;
 
     protoTypes->addSubset(cx, types);
 
     proto->getType(cx)->getFromPrototypes(cx, id, protoTypes);
 }
 
 static inline void
-UpdatePropertyType(JSContext *cx, TypeSet *types, HandleObject obj, Shape *shape, bool force)
+UpdatePropertyType(JSContext *cx, TypeSet *types, RawObject obj, UnrootedShape shape, bool force)
 {
     types->setOwnProperty(cx, false);
     if (!shape->writable())
         types->setOwnProperty(cx, true);
 
     if (shape->hasGetterValue() || shape->hasSetterValue()) {
         types->setOwnProperty(cx, true);
         types->addType(cx, Type::UnknownType());
@@ -3234,24 +3238,24 @@ 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.
          */
 
         RootedObject rSingleton(cx, singleton);
         if (JSID_IS_VOID(id)) {
             /* Go through all shapes on the object to get integer-valued properties. */
-            Shape *shape = singleton->lastProperty();
+            UnrootedShape shape = singleton->lastProperty();
             while (!shape->isEmptyShape()) {
                 if (JSID_IS_VOID(MakeTypeId(cx, shape->propid())))
                     UpdatePropertyType(cx, &base->types, rSingleton, shape, true);
                 shape = shape->previous();
             }
         } else if (!JSID_IS_EMPTY(id) && singleton->isNative()) {
-            Shape *shape = singleton->nativeLookup(cx, id);
+            UnrootedShape shape = singleton->nativeLookup(cx, id);
             if (shape)
                 UpdatePropertyType(cx, &base->types, rSingleton, shape, false);
         }
 
         if (singleton->watched()) {
             /*
              * Mark the property as configured, to inhibit optimizations on it
              * and avoid bypassing the watchpoint handler.
@@ -3273,17 +3277,17 @@ bool
 TypeObject::addDefiniteProperties(JSContext *cx, HandleObject obj)
 {
     if (unknownProperties())
         return true;
 
     /* Mark all properties of obj as definite properties of this type. */
     AutoEnterTypeInference enter(cx);
 
-    Shape *shape = obj->lastProperty();
+    RootedShape shape(cx, obj->lastProperty());
     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());
@@ -3301,17 +3305,17 @@ TypeObject::matchDefiniteProperties(Hand
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
         if (!prop)
             continue;
         if (prop->types.definiteProperty()) {
             unsigned slot = prop->types.definiteSlot();
 
             bool found = false;
-            Shape *shape = obj->lastProperty();
+            UnrootedShape shape = obj->lastProperty();
             while (!shape->isEmptyShape()) {
                 if (shape->slot() == slot && shape->propid() == prop->id) {
                     found = true;
                     break;
                 }
                 shape = shape->previous();
             }
             if (!found)
@@ -3320,16 +3324,17 @@ TypeObject::matchDefiniteProperties(Hand
     }
 
     return true;
 }
 
 inline void
 InlineAddTypeProperty(JSContext *cx, TypeObject *obj, jsid id, Type type)
 {
+    AssertCanGC();
     JS_ASSERT(id == MakeTypeId(cx, id));
 
     AutoEnterTypeInference enter(cx);
 
     TypeSet *types = obj->getProperty(cx, id, true);
     if (!types || types->hasType(type))
         return;
 
@@ -3348,16 +3353,17 @@ void
 TypeObject::addPropertyType(JSContext *cx, jsid id, const Value &value)
 {
     InlineAddTypeProperty(cx, this, id, GetValueType(cx, value));
 }
 
 void
 TypeObject::addPropertyType(JSContext *cx, const char *name, Type type)
 {
+    AssertCanGC();
     jsid id = JSID_VOID;
     if (name) {
         JSAtom *atom = Atomize(cx, name, strlen(name));
         if (!atom) {
             AutoEnterTypeInference enter(cx);
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
@@ -3382,16 +3388,18 @@ TypeObject::markPropertyConfigured(JSCon
     TypeSet *types = getProperty(cx, id, true);
     if (types)
         types->setOwnProperty(cx, true);
 }
 
 void
 TypeObject::markStateChange(JSContext *cx)
 {
+    AutoAssertNoGC nogc;
+
     if (unknownProperties())
         return;
 
     AutoEnterTypeInference enter(cx);
     TypeSet *types = maybeGetProperty(cx, JSID_EMPTY);
     if (types) {
         TypeConstraint *constraint = types->constraintList;
         while (constraint) {
@@ -5015,16 +5023,18 @@ CheckNewScriptProperties(JSContext *cx, 
     type->newScript = (TypeNewScript *) cx->calloc_(numBytes);
 #endif
 
     if (!type->newScript) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
+    AutoAssertNoGC nogc;
+
     type->newScript->fun = fun;
     type->newScript->allocKind = kind;
     type->newScript->shape = baseobj->lastProperty();
 
     type->newScript->initializerList = (TypeNewScript::Initializer *)
         ((char *) type->newScript.get() + sizeof(TypeNewScript));
     PodCopy(type->newScript->initializerList, initializerList.begin(), initializerList.length());
 }
@@ -5823,16 +5833,17 @@ JSObject::setNewTypeUnknown(JSContext *c
     }
 
     return true;
 }
 
 TypeObject *
 JSCompartment::getNewType(JSContext *cx, TaggedProto proto_, JSFunction *fun_, bool isDOM)
 {
+    AssertCanGC();
     JS_ASSERT_IF(fun_, proto_.isObject());
     JS_ASSERT_IF(proto_.isObject(), cx->compartment == proto_.toObject()->compartment());
 
     if (!newTypeObjects.initialized() && !newTypeObjects.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = newTypeObjects.lookupForAdd(proto_);
     if (p) {
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -270,16 +270,17 @@ TypeFlagPrimitive(TypeFlags flags)
 /*
  * Get the canonical representation of an id to use when doing inference.  This
  * maintains the constraint that if two different jsids map to the same property
  * in JS (e.g. 3 and "3"), they have the same type representation.
  */
 inline jsid
 MakeTypeId(JSContext *cx, jsid id)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(!JSID_IS_EMPTY(id));
 
     /*
      * All integers must map to the aggregate property for index types, including
      * negative integers.
      */
     if (JSID_IS_INT(id))
         return JSID_VOID;
@@ -541,54 +542,60 @@ TypeMonitorCall(JSContext *cx, const js:
     }
 
     return true;
 }
 
 inline bool
 TrackPropertyTypes(JSContext *cx, HandleObject obj, jsid id)
 {
+    AutoAssertNoGC nogc;
+
     if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
         return false;
 
     if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(cx, id))
         return false;
 
     return true;
 }
 
 /* Add a possible type for a property of obj. */
 inline void
 AddTypePropertyId(JSContext *cx, HandleObject obj, jsid id, Type type)
 {
+    AssertCanGC();
     if (cx->typeInferenceEnabled())
         id = MakeTypeId(cx, id);
     if (TrackPropertyTypes(cx, obj, id))
         obj->type()->addPropertyType(cx, id, type);
 }
 
 inline void
 AddTypePropertyId(JSContext *cx, HandleObject obj, jsid id, const Value &value)
 {
+    AssertCanGC();
     if (cx->typeInferenceEnabled())
         id = MakeTypeId(cx, id);
     if (TrackPropertyTypes(cx, obj, id))
         obj->type()->addPropertyType(cx, id, value);
 }
 
 inline void
 AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
 {
+    AssertCanGC();
     if (cx->typeInferenceEnabled() && !obj->unknownProperties())
         obj->addPropertyType(cx, name, type);
 }
 
 inline void
 AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value)
 {
+    AssertCanGC();
     if (cx->typeInferenceEnabled() && !obj->unknownProperties())
         obj->addPropertyType(cx, name, value);
 }
 
 /* Set one or more dynamic flags on a type object. */
 inline void
 MarkTypeObjectFlags(JSContext *cx, RawObject obj, TypeObjectFlags flags)
 {
@@ -624,18 +631,19 @@ MarkTypePropertyConfigured(JSContext *cx
     if (cx->typeInferenceEnabled())
         id = MakeTypeId(cx, id);
     if (TrackPropertyTypes(cx, obj, id))
         obj->type()->markPropertyConfigured(cx, id);
 }
 
 /* Mark a state change on a particular object. */
 inline void
-MarkObjectStateChange(JSContext *cx, HandleObject obj)
+MarkObjectStateChange(JSContext *cx, RawObject obj)
 {
+    AutoAssertNoGC nogc;
     if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties())
         obj->type()->markStateChange(cx);
 }
 
 /*
  * For an array or object which has not yet escaped and been referenced elsewhere,
  * pick a new type based on the object's current contents.
  */
@@ -1560,16 +1568,17 @@ TypeObject::getProperty(JSContext *cx, j
         types->setOwnProperty(cx, false);
 
     return types;
 }
 
 inline HeapTypeSet *
 TypeObject::maybeGetProperty(JSContext *cx, jsid id)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == MakeTypeId(cx, id));
     JS_ASSERT(!unknownProperties());
 
     Property *prop = HashSetLookup<jsid,Property,Property>
         (propertySet, basePropertyCount(), id);
 
     return prop ? &prop->types : NULL;
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -169,25 +169,26 @@ ValuePropertyBearer(JSContext *cx, Stack
         return global.getOrCreateBooleanPrototype(cx);
 
     JS_ASSERT(v.isNull() || v.isUndefined());
     js_ReportIsNullOrUndefined(cx, spindex, v, NullPtr());
     return NULL;
 }
 
 inline bool
-NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shape,
+NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shapeArg,
           unsigned getHow, MutableHandleValue vp)
 {
-    if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
+    if (shapeArg->isDataDescriptor() && shapeArg->hasDefaultGetter()) {
         /* Fast path for Object instance properties. */
-        JS_ASSERT(shape->hasSlot());
-        vp.set(pobj->nativeGetSlot(shape->slot()));
+        JS_ASSERT(shapeArg->hasSlot());
+        vp.set(pobj->nativeGetSlot(shapeArg->slot()));
     } else {
-        if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp.address()))
+        RootedShape shape(cx, shapeArg);
+        if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))
             return false;
     }
     return true;
 }
 
 #if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS)
 extern void
 AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -426,18 +426,18 @@ NewPropertyIteratorObject(JSContext *cx,
 }
 
 NativeIterator *
 NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
 {
     size_t plength = props.length();
     NativeIterator *ni = (NativeIterator *)
         cx->malloc_(sizeof(NativeIterator)
-                    + plength * sizeof(JSString *)
-                    + slength * sizeof(Shape *));
+                    + plength * sizeof(RawString)
+                    + slength * sizeof(RawShape));
     if (!ni)
         return NULL;
     AutoValueVector strings(cx);
     ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
     ni->props_end = ni->props_array + plength;
     if (plength) {
         for (size_t i = 0; i < plength; i++) {
             JSFlatString *str = IdToString(cx, props[i]);
@@ -597,17 +597,17 @@ js::GetIterator(JSContext *cx, HandleObj
 
         RawObject obj = ToObject(cx, vp);
         if (!obj)
             return false;
         vp.setObject(*obj);
         return true;
     }
 
-    Vector<Shape *, 8> shapes(cx);
+    Vector<RawShape, 8> shapes(cx);
     uint32_t key = 0;
 
     bool keysOnly = (flags == JSITER_ENUMERATE);
 
     if (obj) {
         if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
             RawObject iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
             if (!iterobj)
@@ -646,28 +646,29 @@ js::GetIterator(JSContext *cx, HandleObj
 
             /*
              * The iterator object for JSITER_ENUMERATE never escapes, so we
              * don't care for the proper parent/proto to be set. This also
              * allows us to re-use a previous iterator object that is not
              * currently active.
              */
             {
+                AutoAssertNoGC nogc;
                 RawObject pobj = obj;
                 do {
                     if (!pobj->isNative() ||
                         pobj->hasUncacheableProto() ||
                         obj->getOps()->enumerate ||
                         pobj->getClass()->enumerate != JS_EnumerateStub) {
                         shapes.clear();
                         goto miss;
                     }
-                    Shape *shape = pobj->lastProperty();
+                    RawShape shape = pobj->lastProperty();
                     key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
-                    if (!shapes.append((Shape *) shape))
+                    if (!shapes.append(shape))
                         return false;
                     pobj = pobj->getProto();
                 } while (pobj);
             }
 
             PropertyIteratorObject *iterobj = cx->runtime->nativeIterCache.get(key);
             if (iterobj) {
                 NativeIterator *ni = iterobj->getNativeIterator();
--- a/js/src/jsmemorymetrics.cpp
+++ b/js/src/jsmemorymetrics.cpp
@@ -192,17 +192,17 @@ StatsCellCallback(JSRuntime *rt, void *d
         } else {
             cStats->gcHeapStringsNormal += thingSize;
             cStats->stringCharsNonHuge += strSize;
         }
         break;
     }
     case JSTRACE_SHAPE:
     {
-        Shape *shape = static_cast<Shape*>(thing);
+        UnrootedShape shape = static_cast<RawShape>(thing);
         size_t propTableSize, kidsSize;
         shape->sizeOfExcludingThis(rtStats->mallocSizeOf, &propTableSize, &kidsSize);
         if (shape->inDictionary()) {
             cStats->gcHeapShapesDict += thingSize;
             cStats->shapesExtraDictTables += propTableSize;
             JS_ASSERT(kidsSize == 0);
         } else {
             if (shape->base()->getObjectParent() == shape->compartment()->maybeGlobal()) {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1343,17 +1343,17 @@ DefinePropertyOnObject(JSContext *cx, Ha
                 if (!shape->configurable() &&
                     (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
                     desc.isDataDescriptor() &&
                     (desc.hasWritable() ? desc.writable() : shape->writable()))
                 {
                     return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
                 }
 
-                if (!js_NativeGet(cx, obj, obj2, shape, 0, v.address()))
+                if (!js_NativeGet(cx, obj, obj2, shape, 0, &v))
                     return JS_FALSE;
             }
 
             if (desc.isDataDescriptor()) {
                 if (!shape->isDataDescriptor())
                     break;
 
                 bool same;
@@ -1875,21 +1875,21 @@ JSObject::sealOrFreeze(JSContext *cx, Ha
     if (obj->isNative() && !obj->inDictionaryMode()) {
         /*
          * Seal/freeze non-dictionary objects by constructing a new shape
          * hierarchy mirroring the original one, which can be shared if many
          * objects with the same structure are sealed/frozen. If we use the
          * generic path below then any non-empty object will be converted to
          * dictionary mode.
          */
-        Shape *last = EmptyShape::getInitialShape(cx, obj->getClass(),
-                                                  obj->getTaggedProto(),
-                                                  obj->getParent(),
-                                                  obj->getAllocKind(),
-                                                  obj->lastProperty()->getObjectFlags());
+        RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
+                                                         obj->getTaggedProto(),
+                                                         obj->getParent(),
+                                                         obj->getAllocKind(),
+                                                         obj->lastProperty()->getObjectFlags()));
         if (!last)
             return false;
 
         /* Get an in order list of the shapes in this object. */
         AutoShapeVector shapes(cx);
         for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
             if (!shapes.append(&r.front()))
                 return false;
@@ -2108,16 +2108,17 @@ js_Object(JSContext *cx, unsigned argc, 
     vp->setObject(*obj);
     return JS_TRUE;
 }
 
 static inline JSObject *
 NewObject(JSContext *cx, Class *clasp, types::TypeObject *type_, JSObject *parent,
           gc::AllocKind kind)
 {
+    AssertCanGC();
     JS_ASSERT(clasp != &ArrayClass);
     JS_ASSERT_IF(clasp == &FunctionClass,
                  kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
     JS_ASSERT_IF(parent, &parent->global() == cx->compartment->maybeGlobal());
 
     RootedTypeObject type(cx, type_);
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(type->proto),
@@ -2286,24 +2287,26 @@ js::NewReshapedObject(JSContext *cx, Han
     if (!res)
         return NULL;
 
     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++) {
-        if (!ids.append(JSID_VOID))
-            return NULL;
-    }
-    js::Shape *nshape = shape;
-    while (!nshape->isEmptyShape()) {
-        ids[nshape->slot()] = nshape->propid();
-        nshape = nshape->previous();
+    {
+        for (unsigned i = 0; i <= shape->slot(); i++) {
+            if (!ids.append(JSID_VOID))
+                return NULL;
+        }
+        UnrootedShape nshape = shape;
+        while (!nshape->isEmptyShape()) {
+            ids[nshape->slot()] = nshape->propid();
+            nshape = nshape->previous();
+        }
     }
 
     /* Construct the new shape. */
     RootedId id(cx);
     RootedValue undefinedValue(cx, UndefinedValue());
     for (unsigned i = 0; i < ids.length(); i++) {
         id = ids[i];
         if (!DefineNativeProperty(cx, res, id, undefinedValue, NULL, NULL,
@@ -2335,19 +2338,20 @@ CreateThisForFunctionWithType(JSContext 
     if (type->newScript) {
         /*
          * Make an object with the type's associated finalize kind and shape,
          * which reflects any properties that will definitely be added to the
          * object before it is read from.
          */
         gc::AllocKind kind = type->newScript->allocKind;
         RootedObject res(cx, NewObjectWithType(cx, type, parent, kind));
-        if (res)
-            JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res,
-                                                     (Shape *) type->newScript->shape.get()));
+        if (res) {
+            RootedShape shape(cx, type->newScript->shape);
+            JS_ALWAYS_TRUE(JSObject::setLastProperty(cx, res, shape));
+        }
         return res;
     }
 
     gc::AllocKind kind = NewObjectGCKind(&ObjectClass);
     return NewObjectWithType(cx, type, parent, kind);
 }
 
 JSObject *
@@ -2545,31 +2549,34 @@ JS_CopyPropertiesFrom(JSContext *cx, JSO
         return true;
 
     AutoShapeVector shapes(cx);
     for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
         if (!shapes.append(&r.front()))
             return false;
     }
 
+    RootedShape shape(cx);
+    RootedValue v(cx);
+    RootedId id(cx);
     size_t n = shapes.length();
     while (n > 0) {
-        Shape *shape = shapes[--n];
+        shape = shapes[--n];
         unsigned attrs = shape->attributes();
         PropertyOp getter = shape->getter();
         StrictPropertyOp setter = shape->setter();
         AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
         if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter))
             return false;
         if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter))
             return false;
-        RootedValue v(cx, shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue());
+        v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue();
         if (!cx->compartment->wrap(cx, v.address()))
             return false;
-        Rooted<jsid> id(cx, shape->propid());
+        id = shape->propid();
         if (!JSObject::defineGeneric(cx, target, id, v, getter, setter, attrs))
             return false;
     }
     return true;
 }
 
 static bool
 CopySlots(JSContext *cx, JSObject *from, JSObject *to)
@@ -2643,41 +2650,42 @@ js::CloneObjectLiteral(JSContext *cx, Ha
     return NewReshapedObject(cx, typeObj, parent, srcObj->getAllocKind(), shape);
 }
 
 struct JSObject::TradeGutsReserved {
     Vector<Value> avals;
     Vector<Value> bvals;
     int newafixed;
     int newbfixed;
-    Shape *newashape;
-    Shape *newbshape;
+    RootedShape newashape;
+    RootedShape newbshape;
     HeapSlot *newaslots;
     HeapSlot *newbslots;
 
     TradeGutsReserved(JSContext *cx)
         : avals(cx), bvals(cx),
           newafixed(0), newbfixed(0),
-          newashape(NULL), newbshape(NULL),
+          newashape(cx), newbshape(cx),
           newaslots(NULL), newbslots(NULL)
     {}
 
     ~TradeGutsReserved()
     {
         if (newaslots)
             js_free(newaslots);
         if (newbslots)
             js_free(newbslots);
     }
 };
 
 bool
 JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                               TradeGutsReserved &reserved)
 {
+    AssertCanGC();
     JS_ASSERT(a->compartment() == b->compartment());
     AutoCompartment ac(cx, a);
 
     /*
      * 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.
      */
@@ -2777,16 +2785,17 @@ JSObject::ReserveForTradeGuts(JSContext 
     }
 
     return true;
 }
 
 void
 JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(a->compartment() == b->compartment());
     JS_ASSERT(a->isFunction() == b->isFunction());
 
     /* Don't try to swap a JSFunction for a plain function JSObject. */
     JS_ASSERT_IF(a->isFunction(), a->sizeOfThis() == b->sizeOfThis());
 
     /*
      * Regexp guts are more complicated -- we would need to migrate the
@@ -2951,21 +2960,20 @@ DefineStandardSlot(JSContext *cx, Handle
         /*
          * Initializing an actual standard class on a global object. If the
          * property is not yet present, force it into a new one bound to a
          * reserved slot. Otherwise, go through the normal property path.
          */
         JS_ASSERT(obj->isGlobal());
         JS_ASSERT(obj->isNative());
 
-        Shape *shape = obj->nativeLookup(cx, id);
-        if (!shape) {
+        if (!obj->nativeLookup(cx, id)) {
             uint32_t slot = 2 * JSProto_LIMIT + key;
             obj->setReservedSlot(slot, v);
-            if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
+            if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
                 return false;
             AddTypePropertyId(cx, obj, id, v);
 
             named = true;
             return true;
         }
     }
 
@@ -3229,17 +3237,17 @@ JSObject::updateSlotsForSpan(JSContext *
         if (oldCount > newCount)
             JSObject::shrinkSlots(cx, obj, oldCount, newCount);
     }
 
     return true;
 }
 
 /* static */ bool
-JSObject::setLastProperty(JSContext *cx, HandleObject obj, js::Shape *shape)
+JSObject::setLastProperty(JSContext *cx, HandleObject obj, HandleShape shape)
 {
     JS_ASSERT(!obj->inDictionaryMode());
     JS_ASSERT(!shape->inDictionary());
     JS_ASSERT(shape->compartment() == obj->compartment());
     JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
 
     size_t oldSpan = obj->lastProperty()->slotSpan();
     size_t newSpan = shape->slotSpan();
@@ -3684,54 +3692,54 @@ js_FindClassObject(JSContext *cx, JSProt
             if (v.get().isPrimitive())
                 v.get().setUndefined();
         }
     }
     vp.set(v);
     return true;
 }
 
-bool
-JSObject::allocSlot(JSContext *cx, uint32_t *slotp)
+/* static */ bool
+JSObject::allocSlot(JSContext *cx, HandleObject obj, uint32_t *slotp)
 {
-    uint32_t slot = slotSpan();
-    JS_ASSERT(slot >= JSSLOT_FREE(getClass()));
+    AssertCanGC();
+    uint32_t slot = obj->slotSpan();
+    JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
 
     /*
      * If this object is in dictionary mode, try to pull a free slot from the
      * shape table's slot-number freelist.
      */
-    if (inDictionaryMode()) {
-        ShapeTable &table = lastProperty()->table();
+    if (obj->inDictionaryMode()) {
+        ShapeTable &table = obj->lastProperty()->table();
         uint32_t last = table.freelist;
         if (last != SHAPE_INVALID_SLOT) {
 #ifdef DEBUG
             JS_ASSERT(last < slot);
-            uint32_t next = getSlot(last).toPrivateUint32();
+            uint32_t next = obj->getSlot(last).toPrivateUint32();
             JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
 #endif
 
             *slotp = last;
 
-            const Value &vref = getSlot(last);
+            const Value &vref = obj->getSlot(last);
             table.freelist = vref.toPrivateUint32();
-            setSlot(last, UndefinedValue());
+            obj->setSlot(last, UndefinedValue());
             return true;
         }
     }
 
     if (slot >= SHAPE_MAXIMUM_SLOT) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     *slotp = slot;
 
-    RootedObject self(cx, this);
-    if (inDictionaryMode() && !setSlotSpan(cx, self, slot + 1))
+    if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
         return false;
 
     return true;
 }
 
 void
 JSObject::freeSlot(uint32_t slot)
 {
@@ -3753,23 +3761,22 @@ JSObject::freeSlot(uint32_t slot)
             last = slot;
             return;
         }
     }
     setSlot(slot, UndefinedValue());
 }
 
 static bool
-PurgeProtoChain(JSContext *cx, JSObject *obj_, jsid id_)
+PurgeProtoChain(JSContext *cx, RawObject objArg, HandleId id)
 {
-    Shape *shape;
-
-    RootedObject obj(cx, obj_);
-    RootedId id(cx, id_);
-
+    /* Root locally so we can re-assign. */
+    RootedObject obj(cx, objArg);
+
+    RootedShape shape(cx);
     while (obj) {
         /* Lookups will not be cached through non-native protos. */
         if (!obj->isNative())
             break;
 
         shape = obj->nativeLookup(cx, id);
         if (shape) {
             if (!obj->shadowingShapeChange(cx, *shape))
@@ -3780,20 +3787,20 @@ PurgeProtoChain(JSContext *cx, JSObject 
         }
         obj = obj->getProto();
     }
 
     return true;
 }
 
 bool
-js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj_, jsid id_)
+js_PurgeScopeChainHelper(JSContext *cx, HandleObject objArg, HandleId id)
 {
-    RootedObject obj(cx, obj_);
-    RootedId id(cx, id_);
+    /* Re-root locally so we can re-assign. */
+    RootedObject obj(cx, objArg);
 
     JS_ASSERT(obj->isNative());
     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
@@ -3805,57 +3812,55 @@ js_PurgeScopeChainHelper(JSContext *cx, 
             if (!PurgeProtoChain(cx, obj, id))
                 return false;
         }
     }
 
     return true;
 }
 
-Shape *
-js_AddNativeProperty(JSContext *cx, HandleObject obj, jsid id_,
+UnrootedShape
+js_AddNativeProperty(JSContext *cx, HandleObject obj, HandleId id,
                      PropertyOp getter, StrictPropertyOp setter, uint32_t slot,
                      unsigned attrs, unsigned flags, int shortid)
 {
-    RootedId id(cx, 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.
      */
     if (!js_PurgeScopeChain(cx, obj, id))
-        return NULL;
-
-    return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
+        return UnrootedShape(NULL);
+
+    return JSObject::putProperty(cx, obj, id, getter, setter, slot, attrs, flags, shortid);
 }
 
 JSBool
 baseops::DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
+    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
 }
 
 JSBool
 baseops::DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx);
     if (index <= JSID_INT_MAX) {
         id = INT_TO_JSID(index);
-        return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
+        return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
     }
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     if (!IndexToId(cx, index, id.address()))
         return false;
 
-    return !!DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
+    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
 }
 
 /*
  * Backward compatibility requires allowing addProperty hooks to mutate the
  * nominal initial value of a slotful property, while GC safety wants that
  * value to be stored before the call-out through the hook.  Optimize to do
  * both while saving cycles for classes that stub their addProperty hook.
  */
@@ -3873,17 +3878,17 @@ CallAddPropertyHook(JSContext *cx, Class
         if (value.get() != nominal) {
             if (shape->hasSlot())
                 JSObject::nativeSetSlotWithType(cx, obj, shape, value);
         }
     }
     return true;
 }
 
-Shape *
+bool
 js::DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                          unsigned flags, int shortid, unsigned defineHow /* = 0 */)
 {
     JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
                              DNP_SKIP_TYPE)) == 0);
     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
 
@@ -3903,44 +3908,44 @@ js::DefineNativeProperty(JSContext *cx, 
         /*
          * If we are defining a getter whose setter was already defined, or
          * vice versa, finish the job via obj->changeProperty, and refresh the
          * property cache line for (obj, id) to map shape.
          */
         RootedObject pobj(cx);
         RootedShape prop(cx);
         if (!baseops::LookupProperty(cx, obj, id, &pobj, &prop))
-            return NULL;
+            return false;
         if (prop && pobj == obj) {
             shape = prop;
             if (shape->isAccessorDescriptor()) {
                 shape = JSObject::changeProperty(cx, obj, shape, attrs,
                                                  JSPROP_GETTER | JSPROP_SETTER,
                                                  (attrs & JSPROP_GETTER)
                                                  ? getter
                                                  : shape->getter(),
                                                  (attrs & JSPROP_SETTER)
                                                  ? setter
                                                  : shape->setter());
                 if (!shape)
-                    return NULL;
+                    return false;
             } else {
                 shape = NULL;
             }
         }
     }
 
     /*
      * 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)) {
         if (!js_PurgeScopeChain(cx, obj, id))
-            return NULL;
+            return false;
     }
 
     /* Use the object's class getter and setter by default. */
     Class *clasp = obj->getClass();
     if (!getter && !(attrs & JSPROP_GETTER))
         getter = clasp->getProperty;
     if (!setter && !(attrs & JSPROP_SETTER))
         setter = clasp->setProperty;
@@ -3951,29 +3956,29 @@ js::DefineNativeProperty(JSContext *cx, 
          * initial value of the property.
          */
         AddTypePropertyId(cx, obj, id, value);
         if (attrs & JSPROP_READONLY)
             MarkTypePropertyConfigured(cx, obj, id);
     }
 
     if (!shape) {
-        shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
-                                 attrs, flags, shortid);
+        shape = JSObject::putProperty(cx, obj, id, getter, setter, SHAPE_INVALID_SLOT,
+                                      attrs, flags, shortid);
         if (!shape)
-            return NULL;
+            return false;
     }
 
     /* Store valueCopy before calling addProperty, in case the latter GC's. */
     if (shape->hasSlot())
         obj->nativeSetSlot(shape->slot(), value);
 
     if (!CallAddPropertyHook(cx, clasp, obj, shape, value)) {
         obj->removeProperty(cx, id);
-        return NULL;
+        return false;
     }
 
     return shape;
 }
 
 /*
  * Call obj's resolve hook.
  *
@@ -4046,37 +4051,39 @@ CallResolveOp(JSContext *cx, HandleObjec
         objp.set(obj2);
     } else {
         if (!resolve(cx, obj, id))
             return false;
 
         objp.set(obj);
     }
 
-    Shape *shape;
+    UnrootedShape shape;
     if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id)))
         propp.set(shape);
     else
         objp.set(NULL);
 
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 LookupPropertyWithFlagsInline(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                               MutableHandleObject objp, MutableHandleShape propp)
 {
     /* Search scopes starting with obj and following the prototype link. */
     RootedObject current(cx, obj);
     while (true) {
-        Shape *shape = current->nativeLookup(cx, id);
-        if (shape) {
-            objp.set(current);
-            propp.set(shape);
-            return true;
+        {
+            UnrootedShape shape = current->nativeLookup(cx, id);
+            if (shape) {
+                objp.set(current);
+                propp.set(shape);
+                return true;
+            }
         }
 
         /* Try obj's class resolve hook if id was not found in obj's scope. */
         if (current->getClass()->resolve != JS_ResolveStub) {
             bool recursed;
             if (!CallResolveOp(cx, current, id, flags, objp, propp, &recursed))
                 return false;
             if (recursed)
@@ -4172,58 +4179,54 @@ js::LookupNameWithGlobalDefault(JSContex
             break;
     }
 
     objp.set(scope);
     return true;
 }
 
 static JS_ALWAYS_INLINE JSBool
-js_NativeGetInline(JSContext *cx, Handle<JSObject*> receiver, JSObject *obj, JSObject *pobj,
-                   Shape *shape, unsigned getHow, Value *vp)
+js_NativeGetInline(JSContext *cx, Handle<JSObject*> receiver, Handle<JSObject*> obj,
+                   Handle<JSObject*> pobj, Handle<Shape*> shape, unsigned getHow,
+                   MutableHandle<Value> vp)
 {
     JS_ASSERT(pobj->isNative());
 
     if (shape->hasSlot()) {
-        *vp = pobj->nativeGetSlot(shape->slot());
-        JS_ASSERT(!vp->isMagic());
+        vp.set(pobj->nativeGetSlot(shape->slot()));
+        JS_ASSERT(!vp.isMagic());
         JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetter(),
-                     js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp));
+                     js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
     } else {
-        vp->setUndefined();
+        vp.setUndefined();
     }
     if (shape->hasDefaultGetter())
         return true;
 
     jsbytecode *pc;
     JSScript *script = cx->stack.currentScript(&pc);
     if (script && script->hasAnalysis()) {
         analyze::Bytecode *code = script->analysis()->maybeCode(pc);
         if (code)
             code->accessGetter = true;
     }
 
-    Rooted<Shape*> shapeRoot(cx, shape);
-    RootedObject pobjRoot(cx, pobj);
-    RootedValue nvp(cx, *vp);
-
-    if (!shape->get(cx, receiver, obj, pobj, &nvp))
+    if (!shape->get(cx, receiver, obj, pobj, vp))
         return false;
 
     /* Update slotful shapes according to the value produced by the getter. */
-    if (shapeRoot->hasSlot() && pobjRoot->nativeContains(cx, shapeRoot))
-        pobjRoot->nativeSetSlot(shapeRoot->slot(), nvp);
-
-    *vp = nvp;
+    if (shape->hasSlot() && pobj->nativeContains(cx, shape))
+        pobj->nativeSetSlot(shape->slot(), vp);
+
     return true;
 }
 
 JSBool
-js_NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shape,
-             unsigned getHow, Value *vp)
+js_NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Handle<Shape*> shape,
+             unsigned getHow, MutableHandle<Value> vp)
 {
     return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
 }
 
 JSBool
 js_NativeSet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> receiver,
              HandleShape shape, bool added, bool strict, Value *vp)
 {
@@ -4353,17 +4356,17 @@ js_GetPropertyHelperInline(JSContext *cx
                ? Proxy::get(cx, obj2, receiver, id, vp)
                : JSObject::getGeneric(cx, obj2, obj2, id, vp);
     }
 
     if (getHow & JSGET_CACHE_RESULT)
         cx->propertyCache().fill(cx, obj, obj2, shape);
 
     /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
-    if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp.address()))
+    if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp))
         return JS_FALSE;
 
     return JS_TRUE;
 }
 
 bool
 js::GetPropertyHelper(JSContext *cx, HandleObject obj, HandleId id, uint32_t getHow, MutableHandleValue vp)
 {
@@ -4633,18 +4636,18 @@ baseops::SetPropertyHelper(JSContext *cx
 
         /*
          * Purge the property cache of now-shadowed id in obj's scope chain.
          * Do this early, before locking obj to avoid nesting locks.
          */
         if (!js_PurgeScopeChain(cx, obj, id))
             return JS_FALSE;
 
-        shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
-                                 attrs, flags, shortid);
+        shape = JSObject::putProperty(cx, obj, id, getter, setter, SHAPE_INVALID_SLOT,
+                                      attrs, flags, shortid);
         if (!shape)
             return JS_FALSE;
 
         /*
          * Initialize the new property value (passed to setter) to undefined.
          * Note that we store before calling addProperty, to match the order
          * in DefineNativeProperty.
          */
@@ -4804,17 +4807,17 @@ baseops::DeleteSpecial(JSContext *cx, Ha
 {
     Rooted<jsid> id(cx, SPECIALID_TO_JSID(sid));
     return baseops::DeleteGeneric(cx, obj, id, rval, strict);
 }
 
 bool
 js::HasDataProperty(JSContext *cx, HandleObject obj, jsid id, Value *vp)
 {
-    if (Shape *shape = obj->nativeLookup(cx, id)) {
+    if (UnrootedShape shape = obj->nativeLookup(cx, id)) {
         if (shape->hasDefaultGetter() && shape->hasSlot()) {
             *vp = obj->nativeGetSlot(shape->slot());
             return true;
         }
     }
 
     return false;
 }
@@ -5153,17 +5156,17 @@ js_ValueToNonNullObject(JSContext *cx, c
 void
 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
 {
     JS_ASSERT(trc->debugPrinter == js_GetObjectSlotName);
 
     JSObject *obj = (JSObject *)trc->debugPrintArg;
     uint32_t slot = uint32_t(trc->debugPrintIndex);
 
-    Shape *shape;
+    UnrootedShape shape;
     if (obj->isNative()) {
         shape = obj->lastProperty();
         while (shape && (!shape->hasSlot() || shape->slot() != slot))
             shape = shape->previous();
     } else {
         shape = NULL;
     }
 
@@ -5290,17 +5293,17 @@ js_DumpId(jsid id)
 }
 
 static void
 DumpProperty(JSObject *obj, Shape &shape)
 {
     jsid id = shape.propid();
     uint8_t attrs = shape.attributes();
 
-    fprintf(stderr, "    ((Shape *) %p) ", (void *) &shape);
+    fprintf(stderr, "    ((JSShape *) %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.hasGetterValue())
         fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
     else if (!shape.hasDefaultGetter())
@@ -5391,17 +5394,17 @@ JSObject::dump()
             fprintf(stderr, "(reserved) ");
         fprintf(stderr, "= ");
         dumpValue(obj->getSlot(i));
         fputc('\n', stderr);
     }
 
     if (obj->isNative()) {
         fprintf(stderr, "properties:\n");
-        Vector<Shape *, 8, SystemAllocPolicy> props;
+        Vector<RawShape, 8, SystemAllocPolicy> props;
         for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
             props.append(&r.front());
         for (size_t i = props.length(); i-- != 0;)
             DumpProperty(obj, *props[i]);
     }
     fputc('\n', stderr);
 }
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -16,33 +16,32 @@
  * values, called slots.  The map/slot pointer pair is GC'ed, while the map
  * is reference counted and the slot vector is malloc'ed.
  */
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsclass.h"
 #include "jsfriendapi.h"
 #include "jsinfer.h"
-#include "jspubtd.h"
-#include "jsprvtd.h"
-#include "jslock.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 
 #include "vm/ObjectImpl.h"
 #include "vm/String.h"
 
 namespace js {
 
 class AutoPropDescArrayRooter;
 class BaseProxyHandler;
 class CallObject;
 struct GCMarker;
 struct NativeIterator;
+ForwardDeclare(Shape);
+struct StackShape;
 
 namespace mjit { class Compiler; }
 
 inline JSObject *
 CastAsObject(PropertyOp op)
 {
     return JS_FUNC_TO_DATA_PTR(JSObject *, op);
 }
@@ -275,20 +274,20 @@ struct JSObject : public js::ObjectImpl
     /* Make the type object to use for LAZY_TYPE objects. */
     js::types::TypeObject *makeLazyType(JSContext *cx);
 
   public:
     /*
      * Update the last property, keeping the number of allocated slots in sync
      * with the object's new slot span.
      */
-    static bool setLastProperty(JSContext *cx, js::HandleObject obj, js::Shape *shape);
+    static bool setLastProperty(JSContext *cx, JS::HandleObject obj, js::HandleShape shape);
 
     /* As above, but does not change the slot span. */
-    inline void setLastPropertyInfallible(js::Shape *shape);
+    inline void setLastPropertyInfallible(js::UnrootedShape shape);
 
     /* Make a non-array object with the specified initial state. */
     static inline JSObject *create(JSContext *cx,
                                    js::gc::AllocKind kind,
                                    js::HandleShape shape,
                                    js::HandleTypeObject type,
                                    js::HeapSlot *slots);
 
@@ -721,17 +720,17 @@ struct JSObject : public js::ObjectImpl
 
     /*
      * 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_t *slotp);
+    static bool allocSlot(JSContext *cx, JS::HandleObject obj, uint32_t *slotp);
     void freeSlot(uint32_t slot);
 
   public:
     static bool reportReadOnly(JSContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
     bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
     bool reportNotExtensible(JSContext *cx, unsigned report = JSREPORT_ERROR);
 
     /*
@@ -739,71 +738,78 @@ struct JSObject : public js::ObjectImpl
      * given arguments, providing this object as |this|. If the property isn't
      * callable a TypeError will be thrown. On success the value returned by
      * the call is stored in *vp.
      */
     bool callMethod(JSContext *cx, js::HandleId id, unsigned argc, js::Value *argv,
                     js::MutableHandleValue vp);
 
   private:
-    js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::StackShape &child);
+    static js::UnrootedShape getChildProperty(JSContext *cx, JS::HandleObject obj,
+                                                   js::HandleShape parent, js::StackShape &child);
 
   protected:
     /*
      * Internal helper that adds a shape not yet mapped by this object.
      *
      * Notes:
      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
      * 2. !isExtensible() checking must be done by callers.
      */
-    js::Shape *addPropertyInternal(JSContext *cx, jsid id,
-                                   JSPropertyOp getter, JSStrictPropertyOp setter,
-                                   uint32_t slot, unsigned attrs,
-                                   unsigned flags, int shortid, js::Shape **spp,
-                                   bool allowDictionary);
+    static js::UnrootedShape addPropertyInternal(JSContext *cx,
+                                                 JS::HandleObject obj, JS::HandleId id,
+                                                 JSPropertyOp getter, JSStrictPropertyOp setter,
+                                                 uint32_t slot, unsigned attrs,
+                                                 unsigned flags, int shortid, js::Shape **spp,
+                                                 bool allowDictionary);
 
   private:
     bool toDictionaryMode(JSContext *cx);
 
     struct TradeGutsReserved;
     static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                                     TradeGutsReserved &reserved);
 
     static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b,
                           TradeGutsReserved &reserved);
 
   public:
     /* Add a property whose id is not yet in this scope. */
-    js::Shape *addProperty(JSContext *cx, jsid id,
-                           JSPropertyOp getter, JSStrictPropertyOp setter,
-                           uint32_t slot, unsigned attrs,
-                           unsigned flags, int shortid, bool allowDictionary = true);
+    static js::UnrootedShape addProperty(JSContext *cx, JS::HandleObject, JS::HandleId id,
+                                         JSPropertyOp getter, JSStrictPropertyOp setter,
+                                         uint32_t slot, unsigned attrs, unsigned flags,
+                                         int shortid, bool allowDictionary = true);
 
     /* Add a data property whose id is not yet in this scope. */
-    js::Shape *addDataProperty(JSContext *cx, jsid id, uint32_t slot, unsigned attrs) {
+    js::UnrootedShape addDataProperty(JSContext *cx, jsid id_, uint32_t slot, unsigned attrs) {
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-        return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0);
+        js::RootedObject self(cx, this);
+        js::RootedId id(cx, id_);
+        return addProperty(cx, self, id, NULL, NULL, slot, attrs, 0, 0);
     }
 
     /* Add or overwrite a property for id in this scope. */
-    js::Shape *putProperty(JSContext *cx, jsid id,
-                           JSPropertyOp getter, JSStrictPropertyOp setter,
-                           uint32_t slot, unsigned attrs,
-                           unsigned flags, int shortid);
-    inline js::Shape *
-    putProperty(JSContext *cx, js::PropertyName *name,
-                JSPropertyOp getter, JSStrictPropertyOp setter,
-                uint32_t slot, unsigned attrs, unsigned flags, int shortid) {
-        return putProperty(cx, js::NameToId(name), getter, setter, slot, attrs, flags, shortid);
+    static js::UnrootedShape putProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
+                                         JSPropertyOp getter, JSStrictPropertyOp setter,
+                                         uint32_t slot, unsigned attrs,
+                                         unsigned flags, int shortid);
+    static js::UnrootedShape putProperty(JSContext *cx, JS::HandleObject obj,
+                                         js::PropertyName *name,
+                                         JSPropertyOp getter, JSStrictPropertyOp setter,
+                                         uint32_t slot, unsigned attrs,
+                                         unsigned flags, int shortid)
+    {
+        js::RootedId id(cx, js::NameToId(name));
+        return putProperty(cx, obj, id, getter, setter, slot, attrs, flags, shortid);
     }
 
     /* Change the given property into a sibling with the same id in this scope. */
-    static js::Shape *changeProperty(JSContext *cx, js::HandleObject obj,
-                                     js::Shape *shape, unsigned attrs, unsigned mask,
-                                     JSPropertyOp getter, JSStrictPropertyOp setter);
+    static js::UnrootedShape changeProperty(JSContext *cx, js::HandleObject obj,
+                                            js::RawShape shape, unsigned attrs, unsigned mask,
+                                            JSPropertyOp getter, JSStrictPropertyOp setter);
 
     static inline bool changePropertyAttributes(JSContext *cx, js::HandleObject obj,
                                                 js::Shape *shape, unsigned attrs);
 
     /* Remove the property named by id from this object. */
     bool removeProperty(JSContext *cx, jsid id);
 
     /* Clear the scope, making it empty. */
@@ -1171,24 +1177,24 @@ js_CreateThisForFunction(JSContext *cx, 
 // Generic call for constructing |this|.
 extern JSObject *
 js_CreateThis(JSContext *cx, js::Class *clasp, js::HandleObject callee);
 
 /*
  * Find or create a property named by id in obj's scope, with the given getter
  * and setter, slot, attributes, and other members.
  */
-extern js::Shape *
-js_AddNativeProperty(JSContext *cx, js::HandleObject obj, jsid id,
+extern js::UnrootedShape
+js_AddNativeProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                      JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot,
                      unsigned attrs, unsigned flags, int shortid);
 
 extern JSBool
-js_DefineOwnProperty(JSContext *cx, js::HandleObject obj, js::HandleId id,
-                     const js::Value &descriptor, JSBool *bp);
+js_DefineOwnProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
+                     const JS::Value &descriptor, JSBool *bp);
 
 namespace js {
 
 JSObject *
 CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
 
 /*
  * Flags for the defineHow parameter of js_DefineNativeProperty.
@@ -1198,22 +1204,22 @@ const unsigned DNP_DONT_PURGE   = 2;   /
 const unsigned DNP_UNQUALIFIED  = 4;   /* Unqualified property set.  Only used in
                                        the defineHow argument of
                                        js_SetPropertyHelper. */
 const unsigned DNP_SKIP_TYPE    = 8;   /* Don't update type information */
 
 /*
  * Return successfully added or changed shape or NULL on error.
  */
-extern Shape *
+extern bool
 DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                      unsigned flags, int shortid, unsigned defineHow = 0);
 
-inline Shape *
+inline bool
 DefineNativeProperty(JSContext *cx, HandleObject obj, PropertyName *name, HandleValue value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                      unsigned flags, int shortid, unsigned defineHow = 0)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, flags,
                                 shortid, defineHow);
 }
@@ -1287,17 +1293,17 @@ const unsigned JSGET_CACHE_RESULT = 1; /
 /*
  * NB: js_NativeGet and js_NativeSet are called with the scope containing shape
  * (pobj's scope for Get, obj's for Set) locked, and on successful return, that
  * scope is again locked.  But on failure, both functions return false with the
  * scope containing shape unlocked.
  */
 extern JSBool
 js_NativeGet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> pobj,
-             js::Shape *shape, unsigned getHow, js::Value *vp);
+             js::Handle<js::Shape*> shape, unsigned getHow, js::MutableHandle<js::Value> vp);
 
 extern JSBool
 js_NativeSet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> receiver,
              js::Handle<js::Shape*> shape, bool added, bool strict, js::Value *vp);
 
 namespace js {
 
 bool
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -267,47 +267,48 @@ JSObject::isFixedSlot(size_t slot)
 inline size_t
 JSObject::dynamicSlotIndex(size_t slot)
 {
     JS_ASSERT(!isDenseArray() && slot >= numFixedSlots());
     return slot - numFixedSlots();
 }
 
 inline void
-JSObject::setLastPropertyInfallible(js::Shape *shape)
+JSObject::setLastPropertyInfallible(js::UnrootedShape shape)
 {
     JS_ASSERT(!shape->inDictionary());
     JS_ASSERT(shape->compartment() == compartment());
     JS_ASSERT(!inDictionaryMode());
     JS_ASSERT(slotSpan() == shape->slotSpan());
     JS_ASSERT(numFixedSlots() == shape->numFixedSlots());
 
     shape_ = shape;
 }
 
 inline void
 JSObject::removeLastProperty(JSContext *cx)
 {
     JS_ASSERT(canRemoveLastProperty());
     js::RootedObject self(cx, this);
-    JS_ALWAYS_TRUE(setLastProperty(cx, self, lastProperty()->previous()));
+    js::RootedShape prev(cx, lastProperty()->previous());
+    JS_ALWAYS_TRUE(setLastProperty(cx, self, prev));
 }
 
 inline bool
 JSObject::canRemoveLastProperty()
 {
     /*
      * Check that the information about the object stored in the last
      * property's base shape is consistent with that stored in the previous
      * shape. If not consistent, then the last property cannot be removed as it
      * will induce a change in the object itself, and the object must be
      * converted to dictionary mode instead. See BaseShape comment in jsscope.h
      */
     JS_ASSERT(!inDictionaryMode());
-    js::Shape *previous = lastProperty()->previous();
+    js::UnrootedShape previous = lastProperty()->previous().get();
     return previous->getObjectParent() == lastProperty()->getObjectParent()
         && previous->getObjectFlags() == lastProperty()->getObjectFlags();
 }
 
 inline const js::HeapSlot *
 JSObject::getRawSlots()
 {
     JS_ASSERT(isGlobal());
@@ -1517,17 +1518,18 @@ CopyInitializerObject(JSContext *cx, Han
     gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
     kind = gc::GetBackgroundAllocKind(kind);
     JS_ASSERT(kind == baseobj->getAllocKind());
     RootedObject obj(cx, NewBuiltinClassInstance(cx, &ObjectClass, kind));
 
     if (!obj)
         return NULL;
 
-    if (!JSObject::setLastProperty(cx, obj, baseobj->lastProperty()))
+    RootedShape lastProp(cx, baseobj->lastProperty());
+    if (!JSObject::setLastProperty(cx, obj, lastProp))
         return NULL;
 
     return obj;
 }
 
 JSObject *
 NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
                   gc::AllocKind kind, HandleShape shape);
@@ -1553,17 +1555,17 @@ GuessArrayGCKind(size_t numSlots)
     return gc::FINALIZE_OBJECT8;
 }
 
 /*
  * Fill slots with the initial slot array to use for a newborn object which
  * may or may not need dynamic slots.
  */
 inline bool
-PreallocateObjectDynamicSlots(JSContext *cx, Shape *shape, HeapSlot **slots)
+PreallocateObjectDynamicSlots(JSContext *cx, UnrootedShape shape, HeapSlot **slots)
 {
     if (size_t count = JSObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) {
         *slots = cx->pod_malloc<HeapSlot>(count);
         if (!*slots)
             return false;
         Debug_SetSlotRangeToCrashOnTouch(*slots, count);
         return true;
     }
@@ -1668,20 +1670,20 @@ js_InitClass(JSContext *cx, js::HandleOb
 
 /*
  * 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 bool
-js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id);
+js_PurgeScopeChainHelper(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 
 inline bool
-js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
+js_PurgeScopeChain(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
 {
     if (obj->isDelegate())
         return js_PurgeScopeChainHelper(cx, obj, id);
     return true;
 }
 
 inline void
 js::DestroyIdArray(FreeOp *fop, JSIdArray *ida)
--- a/js/src/jspropertycacheinlines.h
+++ b/js/src/jspropertycacheinlines.h
@@ -30,17 +30,17 @@
 JS_ALWAYS_INLINE void
 js::PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj,
                         JSObject *&pobj, PropertyCacheEntry *&entry, PropertyName *&name)
 {
     AutoAssertNoGC nogc;
 
     JS_ASSERT(this == &cx->propertyCache());
 
-    Shape *kshape = obj->lastProperty();
+    UnrootedShape kshape = obj->lastProperty();
     entry = &table[hash(pc, kshape)];
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     JS_ASSERT(&obj != &pobj);
     if (entry->kpc == pc && entry->kshape == kshape) {
         JSObject *tmp;
         pobj = obj;
         if (entry->isPrototypePropertyHit() &&
@@ -59,19 +59,21 @@ js::PropertyCache::test(JSContext *cx, j
     if (name)
         PCMETER(misses++);
 }
 
 JS_ALWAYS_INLINE bool
 js::PropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj,
                               PropertyCacheEntry **entryp, JSObject **obj2p, PropertyName **namep)
 {
+    AutoAssertNoGC nogc;
+
     JS_ASSERT(this == &cx->propertyCache());
 
-    Shape *kshape = obj->lastProperty();
+    UnrootedShape kshape = obj->lastProperty();
     PropertyCacheEntry *entry = &table[hash(pc, kshape)];
     *entryp = entry;
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     PCMETER(settests++);
     if (entry->kpc == pc && entry->kshape == kshape)
         return true;
 
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -25,60 +25,58 @@ ShapeHasher::hash(const Lookup &l)
 }
 
 inline bool
 ShapeHasher::match(const Key k, const Lookup &l)
 {
     return k->matches(l);
 }
 
-Shape *
+UnrootedShape
 PropertyTree::newShape(JSContext *cx)
 {
-    Shape *shape = js_NewGCShape(cx);
-    if (!shape) {
+    UnrootedShape shape = js_NewGCShape(cx);
+    if (!shape)
         JS_ReportOutOfMemory(cx);
-        return NULL;
-    }
     return shape;
 }
 
 static KidsHash *
-HashChildren(Shape *kid1, Shape *kid2)
+HashChildren(UnrootedShape kid1, UnrootedShape kid2)
 {
     KidsHash *hash = js_new<KidsHash>();
     if (!hash || !hash->init(2)) {
         js_delete(hash);
         return NULL;
     }
 
     JS_ALWAYS_TRUE(hash->putNew(kid1, kid1));
     JS_ALWAYS_TRUE(hash->putNew(kid2, kid2));
     return hash;
 }
 
 bool
-PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child)
+PropertyTree::insertChild(JSContext *cx, UnrootedShape parent, UnrootedShape child)
 {
     JS_ASSERT(!parent->inDictionary());
     JS_ASSERT(!child->parent);
     JS_ASSERT(!child->inDictionary());
     JS_ASSERT(cx->compartment == compartment);
     JS_ASSERT(child->compartment() == parent->compartment());
 
     KidsPointer *kidp = &parent->kids;
 
     if (kidp->isNull()) {
         child->setParent(parent);
         kidp->setShape(child);
         return true;
     }
 
     if (kidp->isShape()) {
-        Shape *shape = kidp->toShape();
+        UnrootedShape shape = kidp->toShape();
         JS_ASSERT(shape != child);
         JS_ASSERT(!shape->matches(child));
 
         KidsHash *hash = HashChildren(shape, child);
         if (!hash) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
@@ -92,17 +90,17 @@ PropertyTree::insertChild(JSContext *cx,
         return false;
     }
 
     child->setParent(parent);
     return true;
 }
 
 void
-Shape::removeChild(Shape *child)
+Shape::removeChild(UnrootedShape child)
 {
     JS_ASSERT(!child->inDictionary());
     JS_ASSERT(child->parent == this);
 
     KidsPointer *kidp = &kids;
 
     if (kidp->isShape()) {
         JS_ASSERT(kidp->toShape() == child);
@@ -122,84 +120,86 @@ Shape::removeChild(Shape *child)
         KidsHash::Range r = hash->all();
         Shape *otherChild = r.front();
         JS_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
         kidp->setShape(otherChild);
         js_delete(hash);
     }
 }
 
-Shape *
+UnrootedShape
 PropertyTree::getChild(JSContext *cx, Shape *parent_, uint32_t nfixed, const StackShape &child)
 {
     AssertCanGC();
 
-    Shape *shape = NULL;
+    {
+        UnrootedShape shape = NULL;
 
-    JS_ASSERT(parent_);
+        JS_ASSERT(parent_);
 
-    /*
-     * 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.
-     */
-    KidsPointer *kidp = &parent_->kids;
-    if (kidp->isShape()) {
-        Shape *kid = kidp->toShape();
-        if (kid->matches(child))
-            shape = kid;
-    } else if (kidp->isHash()) {
-        if (KidsHash::Ptr p = kidp->toHash()->lookup(child))
-            shape = *p;
-    } else {
-        /* If kidp->isNull(), we always insert. */
-    }
+        /*
+         * 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.
+         */
+        KidsPointer *kidp = &parent_->kids;
+        if (kidp->isShape()) {
+            UnrootedShape kid = kidp->toShape();
+            if (kid->matches(child))
+                shape = kid;
+        } else if (kidp->isHash()) {
+            if (KidsHash::Ptr p = kidp->toHash()->lookup(child))
+                shape = *p;
+        } else {
+            /* If kidp->isNull(), we always insert. */
+        }
 
 #ifdef JSGC_INCREMENTAL
-    if (shape) {
-        JSCompartment *comp = shape->compartment();
-        if (comp->needsBarrier()) {
-            /*
-             * We need a read barrier for the shape tree, since these are weak
-             * pointers.
-             */
-            Shape *tmp = shape;
-            MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
-            JS_ASSERT(tmp == shape);
-        } else if (comp->isGCSweeping() && !shape->isMarked() &&
-                   !shape->arenaHeader()->allocatedDuringIncremental)
-        {
-            /*
-             * The shape we've found is unreachable and due to be finalized, so
-             * remove our weak reference to it and don't use it.
-             */
-            JS_ASSERT(parent_->isMarked());
-            parent_->removeChild(shape);
-            shape = NULL;
+        if (shape) {
+            JSCompartment *comp = shape->compartment();
+            if (comp->needsBarrier()) {
+                /*
+                 * We need a read barrier for the shape tree, since these are weak
+                 * pointers.
+                 */
+                Shape *tmp = shape;
+                MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+                JS_ASSERT(tmp == shape);
+            } else if (comp->isGCSweeping() && !shape->isMarked() &&
+                       !shape->arenaHeader()->allocatedDuringIncremental)
+            {
+                /*
+                 * The shape we've found is unreachable and due to be finalized, so
+                 * remove our weak reference to it and don't use it.
+                 */
+                JS_ASSERT(parent_->isMarked());
+                parent_->removeChild(shape);
+                shape = NULL;
+            }
         }
-    }
 #endif
 
-    if (shape)
-        return shape;
+        if (shape)
+            return shape;
+    }
 
     StackShape::AutoRooter childRoot(cx, &child);
     RootedShape parent(cx, parent_);
 
-    shape = newShape(cx);
+    UnrootedShape shape = newShape(cx);
     if (!shape)
-        return NULL;
+        return UnrootedShape(NULL);
 
     new (shape) Shape(child, nfixed);
 
     if (!insertChild(cx, parent, shape))
-        return NULL;
+        return UnrootedShape(NULL);
 
     return shape;
 }
 
 void
 Shape::finalize(FreeOp *fop)
 {
     if (!inDictionary()) {
@@ -231,17 +231,17 @@ Shape::finalize(FreeOp *fop)
         if (kids.isHash())
             fop->delete_(kids.toHash());
     }
 }
 
 #ifdef DEBUG
 
 void
-KidsPointer::checkConsistency(Shape *aKid) const
+KidsPointer::checkConsistency(UnrootedShape aKid) const
 {
     if (isShape()) {
         JS_ASSERT(toShape() == aKid);
     } else {
         JS_ASSERT(isHash());
         KidsHash *hash = toHash();
         KidsHash::Ptr ptr = hash->lookup(aKid);
         JS_ASSERT(*ptr == aKid);
@@ -318,23 +318,23 @@ Shape::dumpSubtree(JSContext *cx, int le
     } else {
         fprintf(fp, "%*sid ", level, "");
         dump(cx, fp);
     }
 
     if (!kids.isNull()) {
         ++level;
         if (kids.isShape()) {
-            Shape *kid = kids.toShape();
+            UnrootedShape kid = kids.toShape();
             JS_ASSERT(kid->parent == this);
             kid->dumpSubtree(cx, level, fp);
         } else {
             const KidsHash &hash = *kids.toHash();
             for (KidsHash::Range range = hash.all(); !range.empty(); range.popFront()) {
-                Shape *kid = range.front();
+                RawShape kid = range.front();
 
                 JS_ASSERT(kid->parent == this);
                 kid->dumpSubtree(cx, level, fp);
             }
         }
     }
 }
 
@@ -358,16 +358,16 @@ js::PropertyTree::dumpShapes(JSRuntime *
 
     for (gc::GCCompartmentsIter c(rt); !c.done(); c.next()) {
         fprintf(dumpfp, "*** Compartment %p ***\n", (void *)c.get());
 
         /*
         typedef JSCompartment::EmptyShapeSet HS;
         HS &h = c->emptyShapes;
         for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
-            Shape *empty = r.front();
+            RawShape empty = r.front();
             empty->dumpSubtree(rt, 0, dumpfp);
             putc('\n', dumpfp);
         }
         */
     }
 }
 #endif
--- a/js/src/jspropertytree.h
+++ b/js/src/jspropertytree.h
@@ -2,95 +2,95 @@
 /* vim: set ts=4 sw=4 et tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jspropertytree_h___
 #define jspropertytree_h___
 
-#include "jsprvtd.h"
-
 #include "js/HashTable.h"
 
 namespace js {
 
+ForwardDeclare(Shape);
+struct StackShape;
+
 struct ShapeHasher {
-    typedef js::Shape *Key;
-    typedef js::StackShape Lookup;
+    typedef RawShape Key;
+    typedef StackShape Lookup;
 
     static inline HashNumber hash(const Lookup &l);
     static inline bool match(Key k, const Lookup &l);
 };
 
-typedef HashSet<js::Shape *, ShapeHasher, SystemAllocPolicy> KidsHash;
+typedef HashSet<RawShape, ShapeHasher, SystemAllocPolicy> KidsHash;
 
 class KidsPointer {
   private:
     enum {
         SHAPE = 0,
         HASH  = 1,
         TAG   = 1
     };
 
     uintptr_t w;
 
   public:
     bool isNull() const { return !w; }
     void setNull() { w = 0; }
 
     bool isShape() const { return (w & TAG) == SHAPE && !isNull(); }
-    js::Shape *toShape() const {
+    UnrootedShape toShape() const {
         JS_ASSERT(isShape());
-        return reinterpret_cast<js::Shape *>(w & ~uintptr_t(TAG));
+        return reinterpret_cast<RawShape>(w & ~uintptr_t(TAG));
     }
-    void setShape(js::Shape *shape) {
+    void setShape(UnrootedShape shape) {
         JS_ASSERT(shape);
-        JS_ASSERT((reinterpret_cast<uintptr_t>(shape) & TAG) == 0);
-        w = reinterpret_cast<uintptr_t>(shape) | SHAPE;
+        JS_ASSERT((reinterpret_cast<uintptr_t>(static_cast<RawShape>(shape)) & TAG) == 0);
+        w = reinterpret_cast<uintptr_t>(static_cast<RawShape>(shape)) | SHAPE;
     }
 
     bool isHash() const { return (w & TAG) == HASH; }
     KidsHash *toHash() const {
         JS_ASSERT(isHash());
         return reinterpret_cast<KidsHash *>(w & ~uintptr_t(TAG));
     }
     void setHash(KidsHash *hash) {
         JS_ASSERT(hash);
         JS_ASSERT((reinterpret_cast<uintptr_t>(hash) & TAG) == 0);
         w = reinterpret_cast<uintptr_t>(hash) | HASH;
     }
 
 #ifdef DEBUG
-    void checkConsistency(js::Shape *aKid) const;
+    void checkConsistency(UnrootedShape aKid) const;
 #endif
 };
 
 class PropertyTree
 {
     friend struct ::JSFunction;
 
     JSCompartment *compartment;
 
-    bool insertChild(JSContext *cx, js::Shape *parent, js::Shape *child);
+    bool insertChild(JSContext *cx, UnrootedShape parent, UnrootedShape child);
 
     PropertyTree();
 
   public:
     enum { MAX_HEIGHT = 128 };
 
     PropertyTree(JSCompartment *comp)
         : compartment(comp)
     {
     }
 
-    js::Shape *newShape(JSContext *cx);
-    js::Shape *getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child);
+    UnrootedShape newShape(JSContext *cx);
+    UnrootedShape getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child);
 
 #ifdef DEBUG
     static void dumpShapes(JSRuntime *rt);
-    static void meter(JSBasicStats *bs, js::Shape *node);
 #endif
 };
 
 } /* namespace js */
 
 #endif /* jspropertytree_h___ */
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -137,25 +137,17 @@ class GlobalObject;
 
 template <typename K,
           typename V,
           size_t InlineElems>
 class InlineMap;
 
 class LifoAlloc;
 
-class BaseShape;
-class UnownedBaseShape;
 struct Shape;
-struct EmptyShape;
-class ShapeKindArray;
-class Bindings;
-
-struct StackBaseShape;
-struct StackShape;
 
 class Breakpoint;
 class BreakpointSite;
 class Debugger;
 class WatchpointMap;
 
 /*
  * Env is the type of what ES5 calls "lexical environments" (runtime
@@ -202,28 +194,26 @@ namespace types {
 class TypeSet;
 struct TypeCallsite;
 struct TypeObject;
 struct TypeCompartment;
 
 } /* namespace types */
 
 typedef JS::Handle<Shape*>             HandleShape;
-typedef JS::Handle<BaseShape*>         HandleBaseShape;
 typedef JS::Handle<types::TypeObject*> HandleTypeObject;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 
 typedef JSAtom *                       RawAtom;
 
 typedef js::Rooted<Shape*>             RootedShape;
-typedef js::Rooted<BaseShape*>         RootedBaseShape;
 typedef js::Rooted<types::TypeObject*> RootedTypeObject;
 typedef js::Rooted<JSAtom*>            RootedAtom;
 typedef js::Rooted<PropertyName*>      RootedPropertyName;
 
 enum XDRMode {
     XDR_ENCODE,
     XDR_DECODE
 };
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -32,17 +32,17 @@
 #include "jsscopeinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::DebugOnly;
 
 bool
-ShapeTable::init(JSRuntime *rt, Shape *lastProp)
+ShapeTable::init(JSRuntime *rt, UnrootedShape lastProp)
 {
     /*
      * Either we're creating a table for a large scope that was populated
      * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
      * JSOP_SETPROP; or else calloc failed at least once already. In any
      * event, let's try to grow, overallocating to hold at least twice the
      * current population.
      */
@@ -87,17 +87,17 @@ Shape::makeOwnBaseShape(JSContext *cx, H
     nbase->setOwned(shape->base()->toUnowned());
 
     shape->base_ = nbase;
 
     return true;
 }
 
 void
-Shape::handoffTableTo(Shape *shape)
+Shape::handoffTableTo(UnrootedShape shape)
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(inDictionary() && shape->inDictionary());
 
     if (this == shape)
         return;
 
     JS_ASSERT(base()->isOwned() && !shape->base()->isOwned());
@@ -268,110 +268,106 @@ ShapeTable::grow(JSContext *cx)
 
     if (!change(delta, cx) && entryCount + removedCount == size - 1) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
     return true;
 }
 
-Shape *
+UnrootedShape
 Shape::getChildBinding(JSContext *cx, const StackShape &child)
 {
     AssertCanGC();
     JS_ASSERT(!inDictionary());
 
     /* Try to allocate all slots inline. */
     uint32_t slots = child.slotSpan();
     gc::AllocKind kind = gc::GetGCObjectKind(slots);
     uint32_t nfixed = gc::GetGCKindSlots(kind);
 
     return cx->propertyTree().getChild(cx, this, nfixed, child);
 }
 
-/* static */ Shape *
+/* static */ UnrootedShape
 Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base,
-                           TaggedProto proto, Shape *shape_)
+                           TaggedProto proto, HandleShape shape)
 {
-    RootedShape shape(cx, shape_);
-
     JS_ASSERT(!shape->inDictionary());
 
     if (!shape->parent) {
         /* Treat as resetting the initial property of the shape hierarchy. */
         AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
         return EmptyShape::getInitialShape(cx, base.clasp, proto,
                                            base.parent, kind,
                                            base.flags & BaseShape::OBJECT_FLAG_MASK);
     }
 
     StackShape child(shape);
     {
         UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
-            return NULL;
+            return UnrootedShape(NULL);
 
         child.base = nbase;
     }
 
     return cx->propertyTree().getChild(cx, shape->parent, shape->numFixedSlots(), child);
 }
 
 /*
  * Get or create a property-tree or dictionary child property of |parent|,
  * which must be lastProperty() if inDictionaryMode(), else parent must be
  * one of lastProperty() or lastProperty()->parent.
  */
-Shape *
-JSObject::getChildProperty(JSContext *cx, Shape *parent, StackShape &child)
+/* static */ UnrootedShape
+JSObject::getChildProperty(JSContext *cx, HandleObject obj, HandleShape parent, StackShape &child)
 {
     /*
      * 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.hasSlot()) {
         child.setSlot(parent->maybeSlot());
     } else {
         if (child.hasMissingSlot()) {
             uint32_t slot;
-            if (!allocSlot(cx, &slot))
-                return NULL;
+            if (!allocSlot(cx, obj, &slot))
+                return UnrootedShape(NULL);
             child.setSlot(slot);
         } else {
             /* Slots can only be allocated out of order on objects in dictionary mode. */
-            JS_ASSERT(inDictionaryMode() ||
+            JS_ASSERT(obj->inDictionaryMode() ||
                       parent->hasMissingSlot() ||
                       child.slot() == parent->maybeSlot() + 1);
         }
     }
 
-    Shape *shape;
+    RootedShape shape(cx);
 
-    RootedObject self(cx, this);
-
-    if (inDictionaryMode()) {
-        JS_ASSERT(parent == lastProperty());
+    if (obj->inDictionaryMode()) {
+        JS_ASSERT(parent == obj->lastProperty());
         StackShape::AutoRooter childRoot(cx, &child);
         shape = js_NewGCShape(cx);
         if (!shape)
-            return NULL;
-        if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) {
-            if (!JSObject::setSlotSpan(cx, self, child.slot() + 1))
-                return NULL;
+            return UnrootedShape(NULL);
+        if (child.hasSlot() && child.slot() >= obj->lastProperty()->base()->slotSpan()) {
+            if (!JSObject::setSlotSpan(cx, obj, child.slot() + 1))
+                return UnrootedShape(NULL);
         }
-        shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_);
+        shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
     } else {
-        shape = cx->propertyTree().getChild(cx, parent, self->numFixedSlots(), child);
+        shape = cx->propertyTree().getChild(cx, parent, obj->numFixedSlots(), child);
         if (!shape)
-            return NULL;
+            return UnrootedShape(NULL);
         //JS_ASSERT(shape->parent == parent);
         //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
-        if (!JSObject::setLastProperty(cx, self, shape))
-            return NULL;
+        if (!JSObject::setLastProperty(cx, obj, shape))
+            return UnrootedShape(NULL);
     }
 
     return shape;
 }
 
 bool
 JSObject::toDictionaryMode(JSContext *cx)
 {
@@ -392,17 +388,17 @@ JSObject::toDictionaryMode(JSContext *cx
      */
     RootedShape root(cx);
     RootedShape dictionaryShape(cx);
 
     RootedShape shape(cx, lastProperty());
     while (shape) {
         JS_ASSERT(!shape->inDictionary());
 
-        Shape *dprop = js_NewGCShape(cx);
+        UnrootedShape dprop = js_NewGCShape(cx);
         if (!dprop) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
         HeapPtrShape *listp = dictionaryShape
                               ? &dictionaryShape->parent
                               : (HeapPtrShape *) root.address();
@@ -447,127 +443,121 @@ NormalizeGetterAndSetter(JSObject *obj,
     if (getter == JS_PropertyStub) {
         JS_ASSERT(!(attrs & JSPROP_GETTER));
         getter = NULL;
     }
 
     return true;
 }
 
-Shape *
-JSObject::addProperty(JSContext *cx, jsid id,
+/* static */ UnrootedShape
+JSObject::addProperty(JSContext *cx, HandleObject obj, HandleId id,
                       PropertyOp getter, StrictPropertyOp setter,
                       uint32_t slot, unsigned attrs,
                       unsigned flags, int shortid, bool allowDictionary)
 {
     JS_ASSERT(!JSID_IS_VOID(id));
 
-    if (!isExtensible()) {
-        reportNotExtensible(cx);
-        return NULL;
+    if (!obj->isExtensible()) {
+        obj->reportNotExtensible(cx);
+        return UnrootedShape(NULL);
     }
 
-    NormalizeGetterAndSetter(this, id, attrs, flags, getter, setter);
-
-    RootedObject self(cx, this);
+    NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter);
 
     Shape **spp = NULL;
-    if (inDictionaryMode())
-        spp = lastProperty()->table().search(id, true);
+    if (obj->inDictionaryMode())
+        spp = obj->lastProperty()->table().search(id, true);
 
-    return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid,
-                                     spp, allowDictionary);
+    return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, shortid,
+                               spp, allowDictionary);
 }
 
-Shape *
-JSObject::addPropertyInternal(JSContext *cx, jsid id_,
+/* static */ UnrootedShape
+JSObject::addPropertyInternal(JSContext *cx, HandleObject obj, HandleId id,
                               PropertyOp getter, StrictPropertyOp setter,
                               uint32_t slot, unsigned attrs,
                               unsigned flags, int shortid, Shape **spp,
                               bool allowDictionary)
 {
     AssertCanGC();
-    JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
-
-    RootedId id(cx, id_);
-    RootedObject self(cx, this);
+    JS_ASSERT_IF(!allowDictionary, !obj->inDictionaryMode());
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     ShapeTable *table = NULL;
-    if (!inDictionaryMode()) {
+    if (!obj->inDictionaryMode()) {
         bool stableSlot =
             (slot == SHAPE_INVALID_SLOT) ||
-            lastProperty()->hasMissingSlot() ||
-            (slot == lastProperty()->maybeSlot() + 1);
+            obj->lastProperty()->hasMissingSlot() ||
+            (slot == obj->lastProperty()->maybeSlot() + 1);
         JS_ASSERT_IF(!allowDictionary, stableSlot);
         if (allowDictionary &&
-            (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
-            if (!toDictionaryMode(cx))
-                return NULL;
-            table = &self->lastProperty()->table();
+            (!stableSlot || obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
+            if (!obj->toDictionaryMode(cx))
+                return UnrootedShape(NULL);
+            table = &obj->lastProperty()->table();
             spp = table->search(id, true);
         }
     } else {
-        table = &lastProperty()->table();
+        table = &obj->lastProperty()->table();
         if (table->needsToGrow()) {
             if (!table->grow(cx))
-                return NULL;
+                return UnrootedShape(NULL);
             spp = table->search(id, true);
             JS_ASSERT(!SHAPE_FETCH(spp));
         }
     }
 
     JS_ASSERT(!!table == !!spp);
 
     /* Find or create a property tree node labeled by our arguments. */
-    Shape *shape;
+    UnrootedShape shape;
     {
-        shape = self->lastProperty();
+        RootedShape last(cx, obj->lastProperty());
 
         uint32_t index;
         bool indexed = js_IdIsIndex(id, &index);
 
         UnrootedUnownedBaseShape nbase;
-        if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
-            nbase = shape->base()->unowned();
+        if (last->base()->matchesGetterSetter(getter, setter) && !indexed) {
+            nbase = last->base()->unowned();
         } else {
-            StackBaseShape base(shape->base());
+            StackBaseShape base(last->base());
             base.updateGetterSetter(attrs, getter, setter);
             if (indexed)
                 base.flags |= BaseShape::INDEXED;
             nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
-                return NULL;
+                return UnrootedShape(NULL);
         }
 
-        StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
-        DropUnrooted(nbase);
-        shape = self->getChildProperty(cx, self->lastProperty(), child);
+        StackShape child(DropUnrooted(nbase), id, slot, obj->numFixedSlots(), attrs, flags, shortid);
+        shape = getChildProperty(cx, obj, last, child);
     }
 
     if (shape) {
-        JS_ASSERT(shape == self->lastProperty());
+        JS_ASSERT(shape == obj->lastProperty());
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
-            SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
+            SHAPE_STORE_PRESERVING_COLLISION(spp, static_cast<RawShape>(shape));
             ++table->entryCount;
 
             /* Pass the table along to the new last property, namely shape. */
             JS_ASSERT(&shape->parent->table() == table);
             shape->parent->handoffTableTo(shape);
         }
 
-        self->checkShapeConsistency();
+        obj->checkShapeConsistency();
         return shape;
     }
 
-    self->checkShapeConsistency();
-    return NULL;
+    obj->checkShapeConsistency();
+    return UnrootedShape(NULL);
 }
 
 /*
  * Check and adjust the new attributes for the shape to make sure that our
  * slot access optimizations are sound. It is responsibility of the callers to
  * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
  */
 inline bool
@@ -584,171 +574,170 @@ CheckCanChangeAttrs(JSContext *cx, JSObj
         (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
         obj->reportNotConfigurable(cx, shape->propid());
         return false;
     }
 
     return true;
 }
 
-Shape *
-JSObject::putProperty(JSContext *cx, jsid id_,
+/* static */ UnrootedShape
+JSObject::putProperty(JSContext *cx, HandleObject obj, HandleId id,
                       PropertyOp getter, StrictPropertyOp setter,
                       uint32_t slot, unsigned attrs,
                       unsigned flags, int shortid)
 {
-    RootedId id(cx, id_);
     JS_ASSERT(!JSID_IS_VOID(id));
 
-    NormalizeGetterAndSetter(this, id, attrs, flags, getter, setter);
+    NormalizeGetterAndSetter(obj, id, attrs, flags, getter, setter);
 
-    RootedObject self(cx, this);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     /* Search for id in order to claim its entry if table has been allocated. */
     Shape **spp;
-    RootedShape shape(cx, Shape::search(cx, lastProperty(), id, &spp, true));
+    RootedShape shape(cx, Shape::search(cx, obj->lastProperty(), id, &spp, true));
     if (!shape) {
         /*
          * You can't add properties to a non-extensible object, but you can change
          * attributes of properties in such objects.
          */
-        if (!self->isExtensible()) {
-            self->reportNotExtensible(cx);
-            return NULL;
+        if (!obj->isExtensible()) {
+            obj->reportNotExtensible(cx);
+            return UnrootedShape(NULL);
         }
 
-        return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
+        return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, shortid, spp, true);
     }
 
     /* Property exists: search must have returned a valid *spp. */
     JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp));
 
-    if (!CheckCanChangeAttrs(cx, self, shape, &attrs))
-        return NULL;
+    if (!CheckCanChangeAttrs(cx, obj, shape, &attrs))
+        return UnrootedShape(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_t oldSlot = shape->maybeSlot();
     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
         slot = oldSlot;
 
     Rooted<UnownedBaseShape*> nbase(cx);
     {
         uint32_t index;
         bool indexed = js_IdIsIndex(id, &index);
-        StackBaseShape base(self->lastProperty()->base());
+        StackBaseShape base(obj->lastProperty()->base());
         base.updateGetterSetter(attrs, getter, setter);
         if (indexed)
             base.flags |= BaseShape::INDEXED;
         nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
-            return NULL;
+            return UnrootedShape(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(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 != self->lastProperty() && !self->inDictionaryMode()) {
-        if (!self->toDictionaryMode(cx))
-            return NULL;
-        spp = self->lastProperty()->table().search(shape->propid(), false);
+    if (shape != obj->lastProperty() && !obj->inDictionaryMode()) {
+        if (!obj->toDictionaryMode(cx))
+            return UnrootedShape(NULL);
+        spp = obj->lastProperty()->table().search(shape->propid(), false);
         shape = SHAPE_FETCH(spp);
     }
 
     JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
 
-    if (self->inDictionaryMode()) {
+    if (obj->inDictionaryMode()) {
         /*
          * Updating some property in a dictionary-mode object. Create a new
          * shape for the existing property, and also generate a new shape for
          * the last property of the dictionary (unless the modified property
          * is also the last property).
          */
-        bool updateLast = (shape == self->lastProperty());
-        shape = self->replaceWithNewEquivalentShape(cx, shape);
+        bool updateLast = (shape == obj->lastProperty());
+        shape = obj->replaceWithNewEquivalentShape(cx, shape);
         if (!shape)
-            return NULL;
-        if (!updateLast && !self->generateOwnShape(cx))
-            return NULL;
+            return UnrootedShape(NULL);
+        if (!updateLast && !obj->generateOwnShape(cx))
+            return UnrootedShape(NULL);
 
         /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
         if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
-            if (!self->allocSlot(cx, &slot))
-                return NULL;
+            if (!allocSlot(cx, obj, &slot))
+                return UnrootedShape(NULL);
         }
 
         if (updateLast)
             shape->base()->adoptUnowned(nbase);
         else
             shape->base_ = nbase;
 
         shape->setSlot(slot);
         shape->attrs = uint8_t(attrs);
         shape->flags = flags | Shape::IN_DICTIONARY;
         shape->shortid_ = int16_t(shortid);
     } else {
         /*
          * Updating the last property in a non-dictionary-mode object. Find an
          * alternate shared child of the last property's previous shape.
          */
-        StackBaseShape base(self->lastProperty()->base());
+        StackBaseShape base(obj->lastProperty()->base());
         base.updateGetterSetter(attrs, getter, setter);
 
         UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
-            return NULL;
+            return UnrootedShape(NULL);
 
-        JS_ASSERT(shape == self->lastProperty());
+        JS_ASSERT(shape == obj->lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
-        StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
+        StackShape child(nbase, id, slot, obj->numFixedSlots(), attrs, flags, shortid);
         DropUnrooted(nbase);
-        Shape *newShape = self->getChildProperty(cx, shape->parent, child);
+        RootedShape parent(cx, shape->parent);
+        UnrootedShape newShape = JSObject::getChildProperty(cx, obj, parent, child);
 
         if (!newShape) {
-            self->checkShapeConsistency();
-            return NULL;
+            obj->checkShapeConsistency();
+            return UnrootedShape(NULL);
         }
 
         shape = newShape;
     }
 
     /*
      * Can't fail now, so free the previous incarnation's slot if the new shape
      * has no slot. But we do not need to free oldSlot (and must not, as trying
      * to will botch an assertion in JSObject::freeSlot) if the new last
      * property (shape here) has a slotSpan that does not cover it.
      */
     if (hadSlot && !shape->hasSlot()) {
-        if (oldSlot < self->slotSpan())
-            self->freeSlot(oldSlot);
+        if (oldSlot < obj->slotSpan())
+            obj->freeSlot(oldSlot);
         ++cx->runtime->propertyRemovals;
     }
 
-    self->checkShapeConsistency();
+    obj->checkShapeConsistency();
 
     return shape;
 }
 
-/* static */ Shape *
-JSObject::changeProperty(JSContext *cx, HandleObject obj, Shape *shape, unsigned attrs, unsigned mask,
+/* static */ UnrootedShape
+JSObject::changeProperty(JSContext *cx, HandleObject obj, RawShape shape, unsigned attrs, unsigned mask,
                          PropertyOp getter, StrictPropertyOp setter)
 {
     JS_ASSERT(obj->nativeContainsNoAllocation(*shape));
 
     attrs |= shape->attrs & mask;
 
     /* Allow only shared (slotless) => unshared (slotful) transition. */
     JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
@@ -759,29 +748,30 @@ JSObject::changeProperty(JSContext *cx, 
         types::AddTypePropertyId(cx, obj, shape->propid(), types::Type::UnknownType());
 
     if (getter == JS_PropertyStub)
         getter = NULL;
     if (setter == JS_StrictPropertyStub)
         setter = NULL;
 
     if (!CheckCanChangeAttrs(cx, obj, shape, &attrs))
-        return NULL;
+        return UnrootedShape(NULL);
 
     if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
         return shape;
 
     /*
      * 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 *newShape = obj->putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
-                                       attrs, shape->flags, shape->maybeShortid());
+    RootedId propid(cx, shape->propid());
+    UnrootedShape newShape = putProperty(cx, obj, propid, getter, setter, shape->maybeSlot(),
+                                         attrs, shape->flags, shape->maybeShortid());
 
     obj->checkShapeConsistency();
     return newShape;
 }
 
 bool
 JSObject::removeProperty(JSContext *cx, jsid id_)
 {
@@ -857,28 +847,30 @@ JSObject::removeProperty(JSContext *cx, 
             --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.
              */
-            Shape *aprop = self->lastProperty();
+            UnrootedShape aprop = self->lastProperty();
             for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
                 JS_ASSERT_IF(aprop != shape, self->nativeContainsNoAllocation(*aprop));
 #endif
         }
 
-        /* Remove shape from its non-circular doubly linked list. */
-        Shape *oldLastProp = self->lastProperty();
-        shape->removeFromDictionary(self);
+        {
+            /* Remove shape from its non-circular doubly linked list. */
+            UnrootedShape oldLastProp = self->lastProperty();
+            shape->removeFromDictionary(self);
 
-        /* Hand off table from the old to new last property. */
-        oldLastProp->handoffTableTo(self->lastProperty());
+            /* Hand off table from the old to new last property. */
+            oldLastProp->handoffTableTo(self->lastProperty());
+        }
 
         /* Generate a new shape for the object, infallibly. */
         JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
 
         /* Consider shrinking table if its load factor is <= .25. */
         uint32_t size = table.capacity();
         if (size > ShapeTable::MIN_SIZE && table.entryCount <= size >> 2)
             (void) table.change(-1, cx);
@@ -895,17 +887,17 @@ JSObject::removeProperty(JSContext *cx, 
 
     self->checkShapeConsistency();
     return true;
 }
 
 /* static */ void
 JSObject::clear(JSContext *cx, HandleObject obj)
 {
-    Shape *shape = obj->lastProperty();
+    RootedShape shape(cx, obj->lastProperty());
     JS_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
 
     while (shape->parent) {
         shape = shape->parent;
         JS_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
     }
     JS_ASSERT(shape->isEmptyShape());
 
@@ -931,16 +923,17 @@ JSObject::rollbackProperties(JSContext *
         JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
         removeLastProperty(cx);
     }
 }
 
 Shape *
 JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape)
 {
+    AssertCanGC();
     JS_ASSERT(cx->compartment == oldShape->compartment());
     JS_ASSERT_IF(oldShape != lastProperty(),
                  inDictionaryMode() &&
                  nativeLookupNoAllocation(oldShape->propidRef()) == oldShape);
 
     JSObject *self = this;
 
     if (!inDictionaryMode()) {
@@ -1011,34 +1004,35 @@ JSObject::setParent(JSContext *cx, Handl
         UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         obj->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_);
+    UnrootedShape newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_);
     if (!newShape)
         return false;
 
     obj->shape_ = newShape;
     return true;
 }
 
-/* static */ Shape *
+/* static */ UnrootedShape
 Shape::setObjectParent(JSContext *cx, JSObject *parent, TaggedProto proto, Shape *last)
 {
     if (last->getObjectParent() == parent)
         return last;
 
     StackBaseShape base(last);
     base.parent = parent;
 
-    return replaceLastProperty(cx, base, proto, last);
+    RootedShape lastRoot(cx, last);
+    return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
 bool
 JSObject::preventExtensions(JSContext *cx)
 {
     JS_ASSERT(isExtensible());
 
     RootedObject self(cx, this);
@@ -1075,34 +1069,35 @@ JSObject::setFlag(JSContext *cx, /*BaseS
         UnrootedUnownedBaseShape nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         self->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    Shape *newShape = Shape::setObjectFlag(cx, flag, getTaggedProto(), lastProperty());
+    UnrootedShape newShape = Shape::setObjectFlag(cx, flag, getTaggedProto(), lastProperty());
     if (!newShape)
         return false;
 
     self->shape_ = newShape;
     return true;
 }
 
-/* static */ Shape *
+/* static */ UnrootedShape
 Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last)
 {
     if (last->getObjectFlags() & flag)
         return last;
 
     StackBaseShape base(last);
     base.flags |= flag;
 
-    return replaceLastProperty(cx, base, proto, last);
+    RootedShape lastRoot(cx, last);
+    return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
 /* static */ inline HashNumber
 StackBaseShape::hash(const StackBaseShape *base)
 {
     HashNumber hash = base->flags;
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3);
@@ -1233,73 +1228,74 @@ EmptyShape::getInitialShape(JSContext *c
     Rooted<TaggedProto> protoRoot(cx, lookup.proto);
     RootedObject parentRoot(cx, lookup.parent);
 
     StackBaseShape base(clasp, parent, objectFlags);
     Rooted<UnownedBaseShape*> nbase(cx, BaseShape::getUnowned(cx, base));
     if (!nbase)
         return NULL;
 
-    Shape *shape = cx->propertyTree().newShape(cx);
+    UnrootedShape shape = cx->propertyTree().newShape(cx);
     if (!shape)
         return NULL;
     new (shape) EmptyShape(nbase, nfixed);
 
     lookup.proto = protoRoot;
     lookup.parent = parentRoot;
 
     if (!table.relookupOrAdd(p, lookup, InitialShapeEntry(shape, lookup.proto)))
         return NULL;
 
     return shape;
 }
 
 void
-NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto_)
+NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto)
 {
+    AssertCanGC();
     Class *clasp = shape->getObjectClass();
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     if (CanBeFinalizedInBackground(kind, clasp))
         kind = GetBackgroundAllocKind(kind);
 
     Rooted<GlobalObject *> global(cx, &shape->getObjectParent()->global());
-    RootedObject proto(cx, proto_);
-    types::TypeObject *type = proto->getNewType(cx);
+    Rooted<types::TypeObject *> type(cx, proto->getNewType(cx));
 
     EntryIndex entry;
     if (lookupGlobal(clasp, global, kind, &entry))
         PodZero(&entries[entry]);
     if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry))
         PodZero(&entries[entry]);
     if (lookupType(clasp, type, kind, &entry))
         PodZero(&entries[entry]);
 }
 
 /* static */ void
-EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
+EmptyShape::insertInitialShape(JSContext *cx, HandleShape shape, HandleObject proto)
 {
-    InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
-                                     shape->numFixedSlots(), shape->getObjectFlags());
+    InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
+                                     shape->getObjectParent(), shape->numFixedSlots(),
+                                     shape->getObjectFlags());
 
     InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
     JS_ASSERT(p);
 
     InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
     JS_ASSERT(entry.shape->isEmptyShape());
 
     /* The new shape had better be rooted at the old one. */
 #ifdef DEBUG
-    Shape *nshape = shape;
+    RawShape nshape = shape;
     while (!nshape->isEmptyShape())
         nshape = nshape->previous();
     JS_ASSERT(nshape == entry.shape);
 #endif
 
-    entry.shape = shape;
+    entry.shape = shape.get();
 
     /*
      * This affects the shape that will be produced by the various NewObject
      * methods, so clear any cache entry referring to the old shape. This is
      * not required for correctness (though it may bust on the above asserts):
      * the NewObject must always check for a nativeEmpty() result and generate
      * the appropriate properties if found. Clearing the cache entry avoids
      * this duplicate regeneration.
@@ -1310,17 +1306,17 @@ EmptyShape::insertInitialShape(JSContext
 void
 JSCompartment::sweepInitialShapeTable()
 {
     gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_TABLES_INITIAL_SHAPE);
 
     if (initialShapes.initialized()) {
         for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
             const InitialShapeEntry &entry = e.front();
-            Shape *shape = entry.shape;
+            RawShape shape = entry.shape;
             JSObject *proto = entry.proto.raw();
             if (IsShapeAboutToBeFinalized(&shape) || (entry.proto.isObject() && IsObjectAboutToBeFinalized(&proto))) {
                 e.removeFront();
             } else {
 #ifdef DEBUG
                 DebugOnly<JSObject *> parent = shape->getObjectParent();
                 JS_ASSERT(!parent || !IsObjectAboutToBeFinalized(&parent));
                 JS_ASSERT(parent == shape->getObjectParent());
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -85,18 +85,22 @@
  * trees are more space-efficient than alternatives.  This was removed in bug
  * 631138; see that bug for the full details.
  *
  * Because many Shapes have similar data, there is actually a secondary type
  * called a BaseShape that holds some of a Shape's data.  Many shapes can share
  * a single BaseShape.
  */
 
+struct JSObject;
+
 namespace js {
 
+class Bindings;
+
 /* Limit on the number of slotful properties in an object. */
 static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
 static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
 
 /*
  * Shapes use multiplicative hashing, but specialized to
  * minimize footprint.
  */
@@ -127,17 +131,17 @@ struct ShapeTable {
     ~ShapeTable() {
         js_free(entries);
     }
 
     /* By definition, hashShift = HASH_BITS - log2(capacity). */
     uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); }
 
     /* Computes the size of the entries array for a given capacity. */
-    static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); }
+    static size_t sizeOfEntries(size_t cap) { return cap * sizeof(RawShape); }
 
     /*
      * This counts the ShapeTable object itself (which must be
      * heap-allocated) and its |entries| array.
      */
     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
         return mallocSizeOf(this) + mallocSizeOf(entries);
     }
@@ -155,29 +159,21 @@ struct ShapeTable {
      */
     bool grow(JSContext *cx);
 
     /*
      * NB: init and change are fallible but do not report OOM, so callers can
      * cope or ignore. They do however use JSRuntime's calloc_ method in order
      * to update the malloc counter on success.
      */
-    bool            init(JSRuntime *rt, js::Shape *lastProp);
+    bool            init(JSRuntime *rt, UnrootedShape lastProp);
     bool            change(int log2Delta, JSContext *cx);
-    js::Shape       **search(jsid id, bool adding);
+    Shape           **search(jsid id, bool adding);
 };
 
-} /* namespace js */
-
-struct JSObject;
-
-namespace js {
-
-class PropertyTree;
-
 /*
  * 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
@@ -224,16 +220,17 @@ class PropertyTree;
  * last property is used to query information about the object. Care must be
  * taken to call JSObject::canRemoveLastProperty when unwinding an object to
  * an earlier property, however.
  */
 
 ForwardDeclare(UnownedBaseShape);
 ForwardDeclare(BaseShape);
 ForwardDeclare(Shape);
+struct StackBaseShape;
 
 class BaseShape : public js::gc::Cell
 {
   public:
     friend struct Shape;
     friend struct StackBaseShape;
     friend struct StackShape;
 
@@ -267,23 +264,23 @@ class BaseShape : public js::gc::Cell
   private:
     Class               *clasp;         /* Class of referring object. */
     HeapPtrObject       parent;         /* Parent of referring object. */
     uint32_t            flags;          /* Vector of above flags. */
     uint32_t            slotSpan_;      /* Object slot span for BaseShapes at
                                          * dictionary last properties. */
 
     union {
-        js::PropertyOp  rawGetter;      /* getter hook for shape */
+        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 */
+        StrictPropertyOp rawSetter;     /* setter hook for shape */
         JSObject        *setterObj;     /* user-defined callable "set" object or
                                            null if shape->hasSetterValue() */
     };
 
     /* For owned BaseShapes, the canonical unowned BaseShape. */
     HeapPtr<UnownedBaseShape> unowned_;
 
     /* For owned BaseShapes, the shape's shape table. */
@@ -388,33 +385,33 @@ struct StackBaseShape
     typedef const StackBaseShape *Lookup;
 
     uint32_t flags;
     Class *clasp;
     JSObject *parent;
     PropertyOp rawGetter;
     StrictPropertyOp rawSetter;
 
-    StackBaseShape(UnrootedBaseShape base)
+    explicit StackBaseShape(UnrootedBaseShape base)
       : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
         clasp(base->clasp),
         parent(base->parent),
         rawGetter(NULL),
         rawSetter(NULL)
     {}
 
     StackBaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags)
       : flags(objectFlags),
         clasp(clasp),
         parent(parent),
         rawGetter(NULL),
         rawSetter(NULL)
     {}
 
-    inline StackBaseShape(Shape *shape);
+    inline StackBaseShape(UnrootedShape shape);
 
     inline void updateGetterSetter(uint8_t attrs,
                                    PropertyOp rawGetter,
                                    StrictPropertyOp rawSetter);
 
     static inline HashNumber hash(const StackBaseShape *lookup);
     static inline bool match(RawUnownedBaseShape key, const StackBaseShape *lookup);
 
@@ -493,50 +490,50 @@ struct Shape : public js::gc::Cell
         KidsPointer kids;       /* null, single child, or a tagged ptr
                                    to many-kids data structure */
         HeapPtrShape *listp;    /* dictionary list starting at shape_
                                    has a double-indirect back pointer,
                                    either to the next shape's parent if not
                                    last, else to obj->shape_ */
     };
 
-    static inline Shape *search(JSContext *cx, Shape *start, jsid id,
-                                Shape ***pspp, bool adding = false);
+    static inline UnrootedShape search(JSContext *cx, Shape *start, jsid id,
+                                       Shape ***pspp, bool adding = false);
 
-    static inline Shape *searchNoAllocation(Shape *start, jsid id);
+    static inline UnrootedShape searchNoAllocation(UnrootedShape start, jsid id);
 
     inline void removeFromDictionary(JSObject *obj);
     inline void insertIntoDictionary(HeapPtrShape *dictp);
 
     inline void initDictionaryShape(const StackShape &child, uint32_t nfixed,
                                     HeapPtrShape *dictp);
 
-    Shape *getChildBinding(JSContext *cx, const StackShape &child);
+    UnrootedShape getChildBinding(JSContext *cx, const StackShape &child);
 
     /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
-    static Shape *replaceLastProperty(JSContext *cx, const StackBaseShape &base,
-                                      TaggedProto proto, Shape *shape);
+    static UnrootedShape replaceLastProperty(JSContext *cx, const StackBaseShape &base,
+                                             TaggedProto proto, HandleShape shape);
 
     static bool hashify(JSContext *cx, HandleShape shape);
-    void handoffTableTo(Shape *newShape);
+    void handoffTableTo(UnrootedShape newShape);
 
-    inline void setParent(js::Shape *p);
+    inline void setParent(UnrootedShape p);
 
     bool ensureOwnBaseShape(JSContext *cx) {
         if (base()->isOwned())
             return true;
         RootedShape self(cx, this);
         return makeOwnBaseShape(cx, self);
     }
 
     static bool makeOwnBaseShape(JSContext *cx, HandleShape shape);
 
   public:
     bool hasTable() const { return base()->hasTable(); }
-    js::ShapeTable &table() const { return base()->table(); }
+    ShapeTable &table() const { return base()->table(); }
 
     void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf,
                              size_t *propTableSize, size_t *kidsSize) const {
         *propTableSize = hasTable() ? table().sizeOfIncludingThis(mallocSizeOf) : 0;
         *kidsSize = !inDictionary() && kids.isHash()
                   ? kids.toHash()->sizeOfIncludingThis(mallocSizeOf)
                   : 0;
     }
@@ -548,20 +545,22 @@ struct Shape : public js::gc::Cell
 
     const HeapPtrShape &previous() const {
         return parent;
     }
 
     class Range {
       protected:
         friend struct Shape;
-        Shape *cursor;
+
+        /* |cursor| is rooted manually when necessary using Range::AutoRooter. */
+        RawShape cursor;
 
       public:
-        Range(Shape *shape) : cursor(shape) { }
+        Range(UnrootedShape shape) : cursor(shape) { }
 
         bool empty() const {
             return !cursor || cursor->isEmptyShape();
         }
 
         Shape &front() const {
             JS_ASSERT(!empty());
             return *cursor;
@@ -594,18 +593,18 @@ struct Shape : public js::gc::Cell
 
     Range all() {
         return Range(this);
     }
 
     Class *getObjectClass() const { return base()->clasp; }
     JSObject *getObjectParent() const { return base()->parent; }
 
-    static Shape *setObjectParent(JSContext *cx, JSObject *obj, TaggedProto proto, Shape *last);
-    static Shape *setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last);
+    static UnrootedShape setObjectParent(JSContext *cx, JSObject *obj, TaggedProto proto, Shape *last);
+    static UnrootedShape setObjectFlag(JSContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last);
 
     uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
     bool hasObjectFlag(BaseShape::Flag flag) const {
         JS_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
         return !!(base()->flags & flag);
     }
 
   protected:
@@ -655,54 +654,54 @@ struct Shape : public js::gc::Cell
     PropertyOp getter() const { return base()->rawGetter; }
     bool hasDefaultGetter() const  { return !base()->rawGetter; }
     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 base()->getterObj ? js::ObjectValue(*base()->getterObj) : js::UndefinedValue();
+        return base()->getterObj ? ObjectValue(*base()->getterObj) : UndefinedValue();
     }
 
     Value getterOrUndefined() const {
         return (hasGetterValue() && base()->getterObj)
                ? ObjectValue(*base()->getterObj)
                : UndefinedValue();
     }
 
     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 base()->setterObj ? js::ObjectValue(*base()->setterObj) : js::UndefinedValue();
+        return base()->setterObj ? ObjectValue(*base()->setterObj) : UndefinedValue();
     }
 
     Value setterOrUndefined() const {
         return (hasSetterValue() && base()->setterObj)
                ? ObjectValue(*base()->setterObj)
                : UndefinedValue();
     }
 
-    void update(js::PropertyOp getter, js::StrictPropertyOp setter, uint8_t attrs);
+    void update(PropertyOp getter, StrictPropertyOp setter, uint8_t attrs);
 
-    inline bool matches(const Shape *other) const;
+    inline bool matches(const UnrootedShape other) const;
     inline bool matches(const StackShape &other) const;
     inline bool matchesParamsAfterId(UnrootedBaseShape base,
                                      uint32_t aslot, unsigned aattrs, unsigned aflags,
                                      int ashortid) const;
 
     bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
     bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp);
 
-    BaseShape* base() const { return base_; }
+    RawBaseShape base() const { return base_.get(); }
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
     uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; }
 
     bool isEmptyShape() const {
         JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
         return JSID_IS_EMPTY(propid_);
@@ -794,58 +793,58 @@ struct Shape : public js::gc::Cell
         JS_ASSERT_IF(isDataDescriptor(), writable());
         return hasSlot() || (attrs & JSPROP_SHADOWABLE);
     }
 
     uint32_t entryCount() {
         if (hasTable())
             return table().entryCount;
 
-        js::Shape *shape = this;
+        UnrootedShape shape = this;
         uint32_t count = 0;
-        for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront())
+        for (Shape::Range r = shape->all(); !r.empty(); r.popFront())
             ++count;
         return count;
     }
 
     bool isBigEnoughForAShapeTable() {
         JS_ASSERT(!hasTable());
-        js::Shape *shape = this;
+        UnrootedShape shape = this;
         uint32_t count = 0;
-        for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) {
+        for (Shape::Range r = shape->all(); !r.empty(); r.popFront()) {
             ++count;
             if (count >= ShapeTable::MIN_ENTRIES)
                 return true;
         }
         return false;
     }
 
 #ifdef DEBUG
     void dump(JSContext *cx, FILE *fp) const;
     void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
 #endif
 
     void finalize(FreeOp *fop);
-    void removeChild(js::Shape *child);
+    void removeChild(UnrootedShape child);
 
-    static inline void writeBarrierPre(Shape *shape);
-    static inline void writeBarrierPost(Shape *shape, void *addr);
+    static inline void writeBarrierPre(UnrootedShape shape);
+    static inline void writeBarrierPost(UnrootedShape shape, void *addr);
 
     /*
      * All weak references need a read barrier for incremental GC. This getter
      * method implements the read barrier. It's used to obtain initial shapes
      * from the compartment.
      */
-    static inline void readBarrier(Shape *shape);
+    static inline void readBarrier(UnrootedShape shape);
 
     static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
 
     inline void markChildren(JSTracer *trc);
 
-    inline Shape *search(JSContext *cx, jsid id) {
+    inline UnrootedShape search(JSContext *cx, jsid id) {
         Shape **_;
         return search(cx, this, id, &_);
     }
 
     /* For JIT usage */
     static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
 
   private:
@@ -908,17 +907,17 @@ struct EmptyShape : public js::Shape
     static Shape *getInitialShape(JSContext *cx, Class *clasp, TaggedProto proto,
                                   JSObject *parent, gc::AllocKind kind, uint32_t objectFlags = 0);
 
     /*
      * Reinsert an alternate initial shape, to be returned by future
      * getInitialShape calls, until the new shape becomes unreachable in a GC
      * and the table entry is purged.
      */
-    static void insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto);
+    static void insertInitialShape(JSContext *cx, HandleShape shape, HandleObject proto);
 };
 
 /*
  * Entries for the per-compartment initialShapes set indexing initial shapes
  * for objects in the compartment and the associated types.
  */
 struct InitialShapeEntry
 {
@@ -965,33 +964,33 @@ struct StackShape
     /* For performance, StackShape only roots when absolutely necessary. */
     RawUnownedBaseShape base;
     RawId               propid;
     uint32_t            slot_;
     uint8_t             attrs;
     uint8_t             flags;
     int16_t             shortid;
 
-    StackShape(UnrootedUnownedBaseShape base, jsid propid, uint32_t slot,
-               uint32_t nfixed, unsigned attrs, unsigned flags, int shortid)
+    explicit StackShape(UnrootedUnownedBaseShape base, jsid propid, uint32_t slot,
+                        uint32_t nfixed, unsigned attrs, unsigned flags, int shortid)
       : base(base),
         propid(propid),
         slot_(slot),
         attrs(uint8_t(attrs)),
         flags(uint8_t(flags)),
         shortid(int16_t(shortid))
     {
         JS_ASSERT(base);
         JS_ASSERT(!JSID_IS_VOID(propid));
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
     }
 
-    StackShape(const Shape *shape)
+    StackShape(const UnrootedShape &shape)
       : base(shape->base()->unowned()),
-        propid(const_cast<Shape *>(shape)->propidRef()),
+        propid(shape->propidRef()),
         slot_(shape->slotInfo & Shape::SLOT_MASK),
         attrs(shape->attrs),
         flags(shape->flags),
         shortid(shape->shortid_)
     {}
 
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
@@ -1029,36 +1028,36 @@ struct StackShape
         JS_DECL_USE_GUARD_OBJECT_NOTIFIER
     };
  };
 
 } /* namespace js */
 
 /* js::Shape pointer tag bit indicating a collision. */
 #define SHAPE_COLLISION                 (uintptr_t(1))
-#define SHAPE_REMOVED                   ((js::Shape *) SHAPE_COLLISION)
+#define SHAPE_REMOVED                   ((RawShape) SHAPE_COLLISION)
 
 /* Macros to get and set shape pointer values and collision flags. */
 #define SHAPE_IS_FREE(shape)            ((shape) == NULL)
 #define SHAPE_IS_REMOVED(shape)         ((shape) == SHAPE_REMOVED)
 #define SHAPE_IS_LIVE(shape)            ((shape) > SHAPE_REMOVED)
-#define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (js::Shape *)               \
+#define SHAPE_FLAG_COLLISION(spp,shape) (*(spp) = (RawShape)                  \
                                          (uintptr_t(shape) | SHAPE_COLLISION))
 #define SHAPE_HAD_COLLISION(shape)      (uintptr_t(shape) & SHAPE_COLLISION)
 #define SHAPE_FETCH(spp)                SHAPE_CLEAR_COLLISION(*(spp))
 
 #define SHAPE_CLEAR_COLLISION(shape)                                          \
-    ((js::Shape *) (uintptr_t(shape) & ~SHAPE_COLLISION))
+    ((RawShape) (uintptr_t(shape) & ~SHAPE_COLLISION))
 
 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape)                          \
-    (*(spp) = (js::Shape *) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp))))
+    (*(spp) = (RawShape) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp))))
 
 namespace js {
 
-inline Shape *
+inline UnrootedShape
 Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding)
 {
     AssertCanGC();
 #ifdef DEBUG
     {
         SkipRoot skip0(cx, &start);
         SkipRoot skip1(cx, &id);
         MaybeCheckStackRoots(cx);
@@ -1092,38 +1091,38 @@ Shape::search(JSContext *cx, Shape *star
          * No table built -- there weren't enough entries, or OOM occurred.
          * Don't increment numLinearSearches, to keep hasTable() false.
          */
         JS_ASSERT(!start->hasTable());
     } else {
         start->incrementNumLinearSearches();
     }
 
-    for (Shape *shape = start; shape; shape = shape->parent) {
+    for (UnrootedShape shape = start; shape; shape = shape->parent) {
         if (shape->propidRef() == id)
             return shape;
     }
 
-    return NULL;
+    return UnrootedShape(NULL);
 }
 
-/* static */ inline Shape *
-Shape::searchNoAllocation(Shape *start, jsid id)
+/* static */ inline UnrootedShape
+Shape::searchNoAllocation(UnrootedShape start, jsid id)
 {
     if (start->hasTable()) {
         Shape **spp = start->table().search(id, false);
         return SHAPE_FETCH(spp);
     }
 
-    for (Shape *shape = start; shape; shape = shape->parent) {
+    for (UnrootedShape shape = start; shape; shape = shape->parent) {
         if (shape->propidRef() == id)
             return shape;
     }
 
-    return NULL;
+    return UnrootedShape(NULL);
 }
 
 void
 MarkNonNativePropertyFound(HandleObject obj, MutableHandleShape propp);
 
 template<> struct RootKind<Shape *> : SpecificRootKind<Shape *, THING_ROOT_SHAPE> {};
 template<> struct RootKind<BaseShape *> : SpecificRootKind<BaseShape *, THING_ROOT_BASE_SHAPE> {};
 
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -119,17 +119,17 @@ BaseShape::operator=(const BaseShape &ot
 
 inline bool
 BaseShape::matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const
 {
     return rawGetter == this->rawGetter && rawSetter == this->rawSetter;
 }
 
 inline
-StackBaseShape::StackBaseShape(Shape *shape)
+StackBaseShape::StackBaseShape(UnrootedShape shape)
   : flags(shape->getObjectFlags()),
     clasp(shape->getObjectClass()),
     parent(shape->getObjectParent())
 {
     updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
 }
 
 inline void
@@ -233,17 +233,17 @@ StackShape::hash() const
     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);
     return hash;
 }
 
 inline bool
-Shape::matches(const js::Shape *other) const
+Shape::matches(const UnrootedShape other) const
 {
     return propid_.get() == other->propid_.get() &&
            matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs,
                                 other->flags, other->shortid_);
 }
 
 inline bool
 Shape::matches(const StackShape &other) const
@@ -329,17 +329,17 @@ Shape::set(JSContext* cx, HandleObject o
         RootedObject nobj(cx, &obj->asWith().object());
         return CallJSPropertyOpSetter(cx, self->setterOp(), nobj, id, strict, vp);
     }
 
     return CallJSPropertyOpSetter(cx, self->setterOp(), obj, id, strict, vp);
 }
 
 inline void
-Shape::setParent(js::Shape *p)
+Shape::setParent(UnrootedShape p)
 {
     JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
                  p->maybeSlot() <= maybeSlot());
     JS_ASSERT_IF(p && !inDictionary(),
                  hasSlot() == (p->maybeSlot() != maybeSlot()));
     parent = p;
 }
 
@@ -368,17 +368,17 @@ Shape::insertIntoDictionary(HeapPtrShape
      */
     JS_ASSERT(inDictionary());
     JS_ASSERT(!listp);
 
     JS_ASSERT_IF(*dictp, (*dictp)->inDictionary());
     JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp);
     JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment());
 
-    setParent(*dictp);
+    setParent(dictp->get());
     if (parent)
         parent->listp = &parent;
     listp = (HeapPtrShape *) dictp;
     *dictp = this;
 }
 
 void
 Shape::initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp)
@@ -395,43 +395,43 @@ EmptyShape::EmptyShape(UnrootedUnownedBa
   : js::Shape(base, nfixed)
 {
     /* Only empty shapes can be NON_NATIVE. */
     if (!getObjectClass()->isNative())
         flags |= NON_NATIVE;
 }
 
 inline void
-Shape::writeBarrierPre(Shape *shape)
+Shape::writeBarrierPre(UnrootedShape shape)
 {
 #ifdef JSGC_INCREMENTAL
     if (!shape)
         return;
 
     JSCompartment *comp = shape->compartment();
     if (comp->needsBarrier()) {
-        Shape *tmp = const_cast<Shape *>(shape);
+        UnrootedShape tmp = shape;
         MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
         JS_ASSERT(tmp == shape);
     }
 #endif
 }
 
 inline void
-Shape::writeBarrierPost(Shape *shape, void *addr)
+Shape::writeBarrierPost(UnrootedShape shape, void *addr)
 {
 }
 
 inline void
-Shape::readBarrier(Shape *shape)
+Shape::readBarrier(UnrootedShape shape)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = shape->compartment();
     if (comp->needsBarrier()) {
-        Shape *tmp = const_cast<Shape *>(shape);
+        UnrootedShape tmp = shape;
         MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
         JS_ASSERT(tmp == shape);
     }
 #endif
 }
 
 inline void
 Shape::markChildren(JSTracer *trc)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -127,19 +127,21 @@ Bindings::initWithTemporaryStorage(JSCon
 
         RootedId id(cx, NameToId(bi->name()));
         unsigned attrs = JSPROP_PERMANENT | JSPROP_ENUMERATE |
                          (bi->kind() == CONSTANT ? JSPROP_READONLY : 0);
         unsigned frameIndex = bi.frameIndex();
         StackShape child(nbase, id, slot++, 0, attrs, Shape::HAS_SHORTID, frameIndex);
         DropUnrooted(nbase);
 
-        self->callObjShape_ = self->callObjShape_->getChildBinding(cx, child);
-        if (!self->callObjShape_)
+        UnrootedShape shape = self->callObjShape_->getChildBinding(cx, child);
+        if (!shape)
             return false;
+
+        self->callObjShape_ = shape;
     }
     JS_ASSERT(!bi);
 
     return true;
 }
 
 uint8_t *
 Bindings::switchToScriptStorage(Binding *newBindingArray)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -186,17 +186,17 @@ class Bindings
     static bool clone(JSContext *cx, InternalBindingsHandle self, uint8_t *dstScriptData,
                       HandleScript srcScript);
 
     unsigned numArgs() const { return numArgs_; }
     unsigned numVars() const { return numVars_; }
     unsigned count() const { return numArgs() + numVars(); }
 
     /* Return the initial shape of call objects created for this scope. */
-    Shape *callObjShape() const { return callObjShape_; }
+    UnrootedShape callObjShape() const { return callObjShape_.get(); }
 
     /* Convenience method to get the var index of 'arguments'. */
     static unsigned argumentsVarIndex(JSContext *cx, InternalBindingsHandle);
 
     /* Return whether the binding at bindingIndex is aliased. */
     bool bindingIsAliased(unsigned bindingIndex);
 
     /* Return whether this scope has any aliased bindings. */
@@ -205,17 +205,17 @@ class Bindings
     void trace(JSTracer *trc);
 };
 
 template <>
 struct RootMethods<Bindings> {
     static Bindings initial();
     static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
     static bool poisoned(const Bindings &bindings) {
-        return IsPoisonedPtr(bindings.callObjShape());
+        return IsPoisonedPtr(static_cast<RawShape>(bindings.callObjShape()));
     }
 };
 
 class ScriptCounts
 {
     friend struct ::JSScript;
     friend struct ScriptAndCounts;
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3236,23 +3236,23 @@ js::str_fromCharCode(JSContext *cx, unsi
     return JS_TRUE;
 }
 
 static JSFunctionSpec string_static_methods[] = {
     JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
     JS_FS_END
 };
 
-Shape *
+UnrootedShape
 StringObject::assignInitialShape(JSContext *cx)
 {
     JS_ASSERT(nativeEmpty());
 
-    return addDataProperty(cx, NameToId(cx->names().length),
-                           LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
+    RootedId lengthid(cx, NameToId(cx->names().length));
+    return addDataProperty(cx, lengthid, LENGTH_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 JSObject *
 js_InitStringClass(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->asGlobal());
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -127,17 +127,17 @@ WatchpointMap::triggerWatchpoint(JSConte
     /* Copy the entry, since GC would invalidate p. */
     JSWatchPointHandler handler = p->value.handler;
     RootedObject closure(cx, p->value.closure);
 
     /* Determine the property's old value. */
     Value old;
     old.setUndefined();
     if (obj->isNative()) {
-        if (Shape *shape = obj->nativeLookup(cx, id)) {
+        if (UnrootedShape shape = obj->nativeLookup(cx, id)) {
             if (shape->hasSlot())
                 old = obj->nativeGetSlot(shape->slot());
         }
     }
 
     /* Call the handler. */
     return handler(cx, obj, id, old, vp.address(), closure);
 }
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -4719,20 +4719,19 @@ xml_lookupGeneric(JSContext *cx, HandleO
         if (!JSID_IS_VOID(funid))
             return baseops::LookupProperty(cx, obj, funid, objp, propp);
         found = HasNamedProperty(xml, qn);
     }
     if (!found) {
         objp.set(NULL);
         propp.set(NULL);
     } else {
-        Shape *shape =
-            js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
-                                 SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
-                                 0, 0);
+        RootedShape shape(cx, js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
+                                                   SHAPE_INVALID_SLOT, JSPROP_ENUMERATE,
+                                                   0, 0));
         if (!shape)
             return JS_FALSE;
 
         objp.set(obj);
         propp.set(shape);
     }
     return JS_TRUE;
 }
@@ -4751,23 +4750,22 @@ xml_lookupElement(JSContext *cx, HandleO
 {
     JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate());
     if (!HasIndexedProperty(xml, index)) {
         objp.set(NULL);
         propp.set(NULL);
         return true;
     }
 
-    jsid id;
-    if (!IndexToId(cx, index, &id))
+    RootedId id(cx);
+    if (!IndexToId(cx, index, id.address()))
         return false;
 
-    Shape *shape =
-        js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, SHAPE_INVALID_SLOT,
-                             JSPROP_ENUMERATE, 0, 0);
+    RootedShape shape(cx, js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty,
+                                               SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, 0, 0));
     if (!shape)
         return false;
 
     objp.set(obj);
     propp.set(shape);
     return true;
 }
 
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -192,17 +192,17 @@ static const JSC::MacroAssembler::Regist
     void loadPtrFromImm(void *ptr, RegisterID reg) {
         loadPtr(ptr, reg);
     }
 
     void loadShape(RegisterID obj, RegisterID shape) {
         loadPtr(Address(obj, JSObject::offsetOfShape()), shape);
     }
 
-    Jump guardShape(RegisterID objReg, Shape *shape) {
+    Jump guardShape(RegisterID objReg, UnrootedShape shape) {
         return branchPtr(NotEqual, Address(objReg, JSObject::offsetOfShape()), ImmPtr(shape));
     }
 
     Jump guardShape(RegisterID objReg, JSObject *obj) {
         return guardShape(objReg, obj->lastProperty());
     }
 
     /*
@@ -976,17 +976,17 @@ static const JSC::MacroAssembler::Regist
 
     void loadDynamicSlot(RegisterID objReg, uint32_t index,
                          RegisterID typeReg, RegisterID dataReg) {
         loadPtr(Address(objReg, JSObject::offsetOfSlots()), dataReg);
         loadValueAsComponents(Address(dataReg, index * sizeof(Value)), typeReg, dataReg);
     }
 
     void loadObjProp(JSObject *obj, RegisterID objReg,
-                     js::Shape *shape,
+                     js::UnrootedShape shape,
                      RegisterID typeReg, RegisterID dataReg)
     {
         if (obj->isFixedSlot(shape->slot()))
             loadInlineSlot(objReg, shape->slot(), typeReg, dataReg);
         else
             loadDynamicSlot(objReg, obj->dynamicSlotIndex(shape->slot()), typeReg, dataReg);
     }
 
@@ -1337,16 +1337,17 @@ static const JSC::MacroAssembler::Regist
     /*
      * Get a free object for the specified GC kind in compartment, writing it
      * to result and filling it in according to templateObject. Returns a jump
      * taken if a free thing was not retrieved. Note: don't call this directly,
      * use Compiler::getNewObject instead.
      */
     Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject)
     {
+        AutoAssertNoGC nogc;
         gc::AllocKind allocKind = templateObject->getAllocKind();
 
         JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
         int thingSize = (int)gc::Arena::thingSize(allocKind);
 
         JS_ASSERT(cx->typeInferenceEnabled());
         JS_ASSERT(!templateObject->hasDynamicSlots());
         JS_ASSERT(!templateObject->hasDynamicElements());
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6074,17 +6074,17 @@ mjit::Compiler::jsop_aliasedArg(unsigned
 void
 mjit::Compiler::jsop_aliasedVar(ScopeCoordinate sc, bool get, bool poppedAfter)
 {
     RegisterID reg = frame.allocReg(Registers::SavedRegs).reg();
     masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), reg);
     for (unsigned i = 0; i < sc.hops; i++)
         masm.loadPayload(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg);
 
-    Shape *shape = ScopeCoordinateToStaticScope(script_, PC).scopeShape();
+    UnrootedShape shape = ScopeCoordinateToStaticScope(script_, PC).scopeShape();
     Address addr;
     if (shape->numFixedSlots() <= sc.slot) {
         masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
         addr = Address(reg, (sc.slot - shape->numFixedSlots()) * sizeof(Value));
     } else {
         addr = Address(reg, JSObject::getFixedSlotOffset(sc.slot));
     }
 
@@ -6222,17 +6222,17 @@ mjit::Compiler::iter(unsigned flags)
     Jump mismatchedObject = masm.branchPtr(Assembler::NotEqual, T1, T2);
     stubcc.linkExit(mismatchedObject, Uses(1));
 
     /* Compare shape of object's prototype with iterator. */
     masm.loadPtr(Address(reg, JSObject::offsetOfType()), T1);
     masm.loadPtr(Address(T1, offsetof(types::TypeObject, proto)), T1);
     masm.loadShape(T1, T1);
     masm.loadPtr(Address(nireg, offsetof(NativeIterator, shapes_array)), T2);
-    masm.loadPtr(Address(T2, sizeof(Shape *)), T2);
+    masm.loadPtr(Address(T2, sizeof(RawShape)), T2);
     Jump mismatchedProto = masm.branchPtr(Assembler::NotEqual, T1, T2);
     stubcc.linkExit(mismatchedProto, Uses(1));
 
     /*
      * Compare object's prototype's prototype with NULL. The last native
      * iterator will always have a prototype chain length of one
      * (i.e. it must be a plain object), so we do not need to generate
      * a loop here.
@@ -6457,16 +6457,18 @@ mjit::Compiler::jsop_bindgname()
     INLINE_STUBCALL(stubs::BindGlobalName, REJOIN_NONE);
     frame.takeReg(Registers::ReturnReg);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
 }
 
 bool
 mjit::Compiler::jsop_getgname(uint32_t index)
 {
+    AssertCanGC();
+
     /* Optimize undefined, NaN and Infinity. */
     PropertyName *name = script_->getName(index);
     if (name == cx->names().undefined) {
         frame.push(UndefinedValue());
         return true;
     }
     if (name == cx->names().NaN) {
         frame.push(cx->runtime->NaNValue);
@@ -6495,19 +6497,19 @@ mjit::Compiler::jsop_getgname(uint32_t i
         if (!propertyTypes)
             return false;
 
         /*
          * If we are accessing a defined global which is a normal data property
          * then bake its address into the jitcode and guard against future
          * reallocation of the global object's slots.
          */
-        js::Shape *shape = globalObj->nativeLookup(cx, NameToId(name));
+        UnrootedShape shape = globalObj->nativeLookup(cx, NameToId(name));
         if (shape && shape->hasDefaultGetter() && shape->hasSlot()) {
-            HeapSlot *value = &globalObj->getSlotRef(shape->slot());
+            HeapSlot *value = &globalObj->getSlotRef(DropUnrooted(shape)->slot());
             if (!value->isUndefined() &&
                 !propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) {
                 watchGlobalReallocation();
                 RegisterID reg = frame.allocReg();
                 masm.move(ImmPtr(value), reg);
 
                 BarrierState barrier = pushAddressMaybeBarrier(Address(reg), type, true);
                 finishBarrier(barrier, REJOIN_GETTER, 0);
@@ -6621,17 +6623,17 @@ mjit::Compiler::jsop_setgname(PropertyNa
          * Note: object branding is disabled when inference is enabled. With
          * branding there is no way to ensure that a non-function property
          * can't get a function later and cause the global object to become
          * branded, requiring a shape change if it changes again.
          */
         types::HeapTypeSet *types = globalObj->getType(cx)->getProperty(cx, id, false);
         if (!types)
             return false;
-        js::Shape *shape = globalObj->nativeLookup(cx, NameToId(name));
+        UnrootedShape shape = globalObj->nativeLookup(cx, NameToId(name));
         if (shape && shape->hasDefaultSetter() &&
             shape->writable() && shape->hasSlot() &&
             !types->isOwnProperty(cx, globalObj->getType(cx), true)) {
             watchGlobalReallocation();
             HeapSlot *value = &globalObj->getSlotRef(shape->slot());
             RegisterID reg = frame.allocReg();
 #ifdef JSGC_INCREMENTAL_MJ
             /* Write barrier. */
@@ -7906,16 +7908,18 @@ mjit::Compiler::pushedSingleton(unsigned
  *     pushRegs to reflect the final state of the op, which is OK as no inline
  *     code has been emitted since the barrier jump.
  */
 
 mjit::Compiler::BarrierState
 mjit::Compiler::pushAddressMaybeBarrier(Address address, JSValueType type, bool reuseBase,
                                         bool testUndefined)
 {
+    AssertCanGC();
+
     if (!hasTypeBarriers(PC) && !testUndefined) {
         frame.push(address, type, reuseBase);
         return BarrierState();
     }
 
     RegisterID typeReg, dataReg;
     frame.loadIntoRegisters(address, reuseBase, &typeReg, &dataReg);
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1510,17 +1510,17 @@ mjit::Compiler::jsop_setelem(bool popGua
     ic.fastPathStart = masm.label();
 
     // Create the common out-of-line sync block, taking care to link previous
     // guards here after.
     RESERVE_OOL_SPACE(stubcc.masm);
     ic.slowPathStart = stubcc.syncExit(Uses(3));
 
     // Guard obj is a dense array.
-    Shape *shape = GetDenseArrayShape(cx, globalObj);
+    UnrootedShape shape = GetDenseArrayShape(cx, globalObj);
     if (!shape)
         return false;
     ic.shapeGuard = masm.guardShape(ic.objReg, shape);
     stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
 
     // Load the dynamic elements vector.
     masm.loadPtr(Address(ic.objReg, JSObject::offsetOfElements()), ic.objReg);
 
@@ -2088,17 +2088,17 @@ mjit::Compiler::jsop_getelem()
     if (id->mightBeType(JSVAL_TYPE_INT32)) {
         // Always test the type first (see comment in PolyIC.h).
         if (!id->isTypeKnown()) {
             ic.typeGuard = masm.testInt32(Assembler::NotEqual, ic.typeReg);
             stubcc.linkExitDirect(ic.typeGuard.get(), ic.slowPathStart);
         }
 
         // Guard obj is a dense array.
-        Shape *shape = GetDenseArrayShape(cx, globalObj);
+        UnrootedShape shape = GetDenseArrayShape(cx, globalObj);
         if (!shape)
             return false;
         ic.shapeGuard = masm.guardShape(ic.objReg, shape);
         stubcc.linkExitDirect(ic.shapeGuard, ic.slowPathStart);
 
         Int32Key key = id->isConstant()
                        ? Int32Key::FromConstant(id->getValue().toInt32())
                        : Int32Key::FromRegister(ic.id.dataReg());
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1395,22 +1395,22 @@ GetPIC(JSContext *cx, JSScript *script, 
     for (uint32_t i = 0; i < chunk->nPICs; i++) {
         if (pics[i].pc == pc)
             return &pics[i];
     }
 
     return NULL;
 }
 
-Shape *
+UnrootedShape
 mjit::GetPICSingleShape(JSContext *cx, JSScript *script, jsbytecode *pc, bool constructing)
 {
     ic::PICInfo *pic = GetPIC(cx, script, pc, constructing);
     if (!pic)
-        return NULL;
+        return UnrootedShape(NULL);
     return pic->getSingleShape();
 }
 
 void
 JITScript::purgeCaches()
 {
     for (unsigned i = 0; i < nchunks; i++) {
         ChunkDescriptor &desc = chunkDescriptor(i);
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -1030,17 +1030,17 @@ IsLowerableFunCallOrApply(jsbytecode *pc
 #ifdef JS_MONOIC
     return (*pc == JSOP_FUNCALL && GET_ARGC(pc) >= 1) ||
            (*pc == JSOP_FUNAPPLY && GET_ARGC(pc) == 2);
 #else
     return false;
 #endif
 }
 
-Shape *
+UnrootedShape
 GetPICSingleShape(JSContext *cx, JSScript *script, jsbytecode *pc, bool constructing);
 
 static inline void
 PurgeCaches(JSScript *script)
 {
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -63,42 +63,45 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlo
 {
     AssertCanGC();
 
     RootedObject obj(f.cx, &f.fp()->global());
     PropertyName *name = f.script()->getName(GET_UINT32_INDEX(f.pc()));
 
     RecompilationMonitor monitor(f.cx);
 
-    Shape *shape = obj->nativeLookup(f.cx, NameToId(name));
-
-    if (monitor.recompiled()) {
-        stubs::Name(f);
-        return;
-    }
-
-    if (!shape ||
-        !shape->hasDefaultGetter() ||
-        !shape->hasSlot())
+    uint32_t slot;
     {
-        if (shape)
-            PatchGetFallback(f, ic);
-        stubs::Name(f);
-        return;
-    }
-    uint32_t slot = shape->slot();
+        RootedShape shape(f.cx, obj->nativeLookup(f.cx, NameToId(name)));
+
+        if (monitor.recompiled()) {
+            stubs::Name(f);
+            return;
+        }
 
-    /* Patch shape guard. */
-    Repatcher repatcher(f.chunk());
-    repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj->lastProperty());
+        if (!shape ||
+            !shape->hasDefaultGetter() ||
+            !shape->hasSlot())
+        {
+            if (shape)
+                PatchGetFallback(f, ic);
+            stubs::Name(f);
+            return;
+        }
+        slot = shape->slot();
 
-    /* Patch loads. */
-    uint32_t index = obj->dynamicSlotIndex(slot);
-    JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
-    repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
+        /* Patch shape guard. */
+        Repatcher repatcher(f.chunk());
+        repatcher.repatch(ic->fastPathStart.dataLabelPtrAtOffset(ic->shapeOffset), obj->lastProperty());
+
+        /* Patch loads. */
+        uint32_t index = obj->dynamicSlotIndex(slot);
+        JSC::CodeLocationLabel label = ic->fastPathStart.labelAtOffset(ic->loadStoreOffset);
+        repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
+    }
 
     /* Do load anyway... this time. */
     stubs::Name(f);
 }
 
 static void JS_FASTCALL
 DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
@@ -112,24 +115,24 @@ PatchSetFallback(VMFrame &f, ic::SetGlob
 {
     Repatcher repatch(f.chunk());
     VoidStubSetGlobal stub = DisabledSetGlobal;
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
     repatch.relink(ic->slowPathCall, fptr);
 }
 
 void
-SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, Shape *shape)
+SetGlobalNameIC::patchInlineShapeGuard(Repatcher &repatcher, UnrootedShape shape)
 {
     JSC::CodeLocationDataLabelPtr label = fastPathStart.dataLabelPtrAtOffset(shapeOffset);
     repatcher.repatch(label, shape);
 }
 
 static LookupStatus
-UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, Shape *shape)
+UpdateSetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic, JSObject *obj, UnrootedShape shape)
 {
     /* Give globals a chance to appear. */
     if (!shape)
         return Lookup_Uncacheable;
 
     if (!shape->hasDefaultSetter() ||
         !shape->writable() ||
         !shape->hasSlot() ||
@@ -157,22 +160,24 @@ ic::SetGlobalName(VMFrame &f, ic::SetGlo
 {
     AssertCanGC();
 
     RootedObject obj(f.cx, &f.fp()->global());
     RootedPropertyName name(f.cx, f.script()->getName(GET_UINT32_INDEX(f.pc())));
 
     RecompilationMonitor monitor(f.cx);
 
-    Shape *shape = obj->nativeLookup(f.cx, NameToId(name));
+    {
+        UnrootedShape shape = obj->nativeLookup(f.cx, NameToId(name));
 
-    if (!monitor.recompiled()) {
-        LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape);
-        if (status == Lookup_Error)
-            THROW();
+        if (!monitor.recompiled()) {
+            LookupStatus status = UpdateSetGlobalName(f, ic, obj, shape);
+            if (status == Lookup_Error)
+                THROW();
+        }
     }
 
     stubs::SetName(f, name);
 }
 
 class EqualityICLinker : public LinkerHelper
 {
     VMFrame &f;
--- a/js/src/methodjit/MonoIC.h
+++ b/js/src/methodjit/MonoIC.h
@@ -111,17 +111,17 @@ struct SetGlobalNameIC : public GlobalNa
     RegisterID objReg   : 5;    /* Register for object, if objConst is false. */
     RegisterID shapeReg : 5;    /* Register for shape; volatile. */
 
     int32_t fastRejoinOffset : 16;  /* Offset from fastPathStart to rejoin. */
 
     /* SET only. */
     ValueRemat vr;              /* RHS value. */
 
-    void patchInlineShapeGuard(Repatcher &repatcher, Shape *shape);
+    void patchInlineShapeGuard(Repatcher &repatcher, UnrootedShape shape);
 };
 
 void JS_FASTCALL GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic);
 void JS_FASTCALL SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic);
 
 struct EqualityICInfo {
     typedef JSC::MacroAssembler::RegisterID RegisterID;
 
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -184,17 +184,17 @@ class SetPropCompiler : public PICStubCo
                           NULL);
         repatcher.relink(labels.getInlineShapeJump(pic.fastPathStart.labelAtOffset(pic.shapeGuard)),
                          pic.slowPathStart);
 
         FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetPropOrName));
         repatcher.relink(pic.slowPathCall, target);
     }
 
-    LookupStatus patchInline(Shape *shape)
+    LookupStatus patchInline(UnrootedShape shape)
     {
         JS_ASSERT(!pic.inlinePathPatched);
         JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress());
 
         Repatcher repatcher(f.chunk());
         SetPropLabels &labels = pic.setPropLabels();
 
         int32_t offset;
@@ -244,17 +244,17 @@ class SetPropCompiler : public PICStubCo
         } else {
             CodeLocationLabel shapeGuard = label.labelAtOffset(pic.shapeGuard);
             repatcher.relink(pic.setPropLabels().getInlineShapeJump(shapeGuard), cs);
         }
         if (int secondGuardOffset = getLastStubSecondShapeGuard())
             repatcher.relink(label.jumpAtOffset(secondGuardOffset), cs);
     }
 
-    LookupStatus generateStub(Shape *initialShape, Shape *shape, bool adding)
+    LookupStatus generateStub(UnrootedShape initialShape, UnrootedShape shape, bool adding)
     {
         if (hadGC())
             return Lookup_Uncacheable;
 
         /* Exits to the slow path. */
         Vector<Jump, 8> slowExits(cx);
         Vector<Jump, 8> otherGuards(cx);
 
@@ -505,30 +505,29 @@ class SetPropCompiler : public PICStubCo
              */
             JSObject *proto = obj;
             while (proto) {
                 if (!proto->isNative())
                     return disable("non-native proto");
                 proto = proto->getProto();
             }
 
-            Shape *initialShape = obj->lastProperty();
+            RootedShape initialShape(cx, obj->lastProperty());
             uint32_t slots = obj->numDynamicSlots();
 
             unsigned flags = 0;
             PropertyOp getter = clasp->getProperty;
 
             /*
              * Define the property but do not set it yet. For setmethod,
              * populate the slot to satisfy the method invariant (in case we
              * hit an early return below).
              */
-            shape =
-                obj->putProperty(cx, name, getter, clasp->setProperty,
-                                 SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0);
+            shape = JSObject::putProperty(cx, obj, name, getter, clasp->setProperty,
+                                          SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0);
             if (!shape)
                 return error();
 
             if (monitor.recompiled())
                 return Lookup_Uncacheable;
 
             /*
              * Test after calling putProperty since it can switch obj into
@@ -1011,17 +1010,17 @@ class GetPropCompiler : public PICStubCo
             repatcher.relink(pic.getPropLabels().getInlineTypeJump(pic.fastPathStart), start);
         }
 
         disable("generated string length stub");
 
         return Lookup_Cacheable;
     }
 
-    LookupStatus patchInline(JSObject *holder, Shape *shape)
+    LookupStatus patchInline(JSObject *holder, UnrootedShape shape)
     {
         spew("patch", "inline");
         Repatcher repatcher(f.chunk());
         GetPropLabels &labels = pic.getPropLabels();
 
         int32_t offset;
         if (holder->isFixedSlot(shape->slot())) {
             CodeLocationInstruction istr = labels.getDslotsLoad(pic.fastPathRejoin);
@@ -1046,17 +1045,17 @@ class GetPropCompiler : public PICStubCo
         repatcher.patchAddressOffsetForValueLoad(labels.getValueLoad(pic.fastPathRejoin), offset);
 
         pic.inlinePathPatched = true;
 
         return Lookup_Cacheable;
     }
 
     /* For JSPropertyOp getters. */
-    void generateGetterStub(Assembler &masm, Shape *shape, jsid userid,
+    void generateGetterStub(Assembler &masm, UnrootedShape shape, jsid userid,
                             Label start, Vector<Jump, 8> &shapeMismatches)
     {
         AutoAssertNoGC nogc;
 
         /*
          * Getter hook needs to be called from the stub. The state is fully
          * synced and no registers are live except the result registers.
          */
@@ -1158,17 +1157,17 @@ class GetPropCompiler : public PICStubCo
         }
 
         linker.patchJump(pic.fastPathRejoin);
 
         linkerEpilogue(linker, start, shapeMismatches);
     }
 
     /* For getters backed by a JSNative. */
-    void generateNativeGetterStub(Assembler &masm, Shape *shape,
+    void generateNativeGetterStub(Assembler &masm, UnrootedShape shape,
                                   Label start, Vector<Jump, 8> &shapeMismatches)
     {
         AutoAssertNoGC nogc;
 
         /*
          * Getter hook needs to be called from the stub. The state is fully
          * synced and no registers are live except the result registers.
          */
@@ -1699,17 +1698,17 @@ class ScopeNameCompiler : public PICStub
 
         /* For GETXPROP, the object is already in objReg. */
         if (pic.kind == ic::PICInfo::NAME)
             masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
 
         JS_ASSERT(obj == getprop.holder);
         JS_ASSERT(getprop.holder != &scopeChain->global());
 
-        Shape *shape = getprop.shape;
+        UnrootedShape shape = getprop.shape;
         if (!shape->hasDefaultGetter())
             return disable("unhandled callobj sprop getter");
 
         LookupStatus status = walkScopeChain(masm, fails);
         if (status != Lookup_Cacheable)
             return status;
 
         /* If a scope chain walk was required, the final object needs a NULL test. */
@@ -1828,17 +1827,27 @@ class ScopeNameCompiler : public PICStub
                 return false;
             return true;
         }
 
         RootedShape shape(cx, getprop.shape);
         Rooted<JSObject*> normalized(cx, obj);
         if (obj->isWith() && !shape->hasDefaultGetter())
             normalized = &obj->asWith().object();
-        NATIVE_GET(cx, normalized, holder, shape, 0, vp.address(), return false);
+        if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
+            /* Fast path for Object instance properties. */
+            JS_ASSERT(shape->slot() != SHAPE_INVALID_SLOT || !shape->hasDefaultSetter());
+            if (shape->slot() != SHAPE_INVALID_SLOT)
+                vp.set(holder->nativeGetSlot(shape->slot()));
+            else
+                vp.setUndefined();
+        } else {
+            if (!js_NativeGet(cx, normalized, holder, shape, 0, vp))
+                return false;
+        }
         return true;
     }
 };
 
 class BindNameCompiler : public PICStubCompiler
 {
     RootedObject scopeChain;
     RootedPropertyName name;
@@ -2397,17 +2406,17 @@ GetElementIC::attachGetProp(VMFrame &f, 
         masm.move(ImmPtr(holder), holderReg);
         typeRegHasBaseShape = false;
 
         // Guard on the holder's shape.
         protoGuard = masm.guardShape(holderReg, holder);
     }
 
     // Load the value.
-    Shape *shape = getprop.shape;
+    RootedShape shape(cx, getprop.shape);
     masm.loadObjProp(holder, holderReg, shape, typeReg, objReg);
 
     Jump done = masm.jump();
 
     updatePCCounters(f, masm);
 
     PICLinker buffer(masm, *this);
     if (!buffer.init(cx))
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -484,24 +484,24 @@ struct PICInfo : public BasePolyIC {
     PropertyName *name;
 
   private:
     Shape *inlinePathShape_;
 
   public:
     void purge(Repatcher &repatcher);
 
-    void setInlinePathShape(Shape *shape) {
+    void setInlinePathShape(UnrootedShape shape) {
         JS_ASSERT(!inlinePathShape_);
         inlinePathShape_ = shape;
     }
 
-    Shape *getSingleShape() {
+    UnrootedShape getSingleShape() {
         if (disabled || hadUncacheable || stubsGenerated > 0)
-            return NULL;
+            return UnrootedShape(NULL);
         return inlinePathShape_;
     }
 
   protected:
     // Reset the data members to the state of a fresh PIC before any patching
     // or stub generation was done.
     void reset() {
         BasePolyIC::reset();
--- a/js/src/methodjit/StubCalls-inl.h
+++ b/js/src/methodjit/StubCalls-inl.h
@@ -24,28 +24,13 @@ ThrowException(VMFrame &f)
 static inline void
 ReportAtomNotDefined(JSContext *cx, JSAtom *atom)
 {
     JSAutoByteString printable;
     if (js_AtomToPrintableString(cx, atom, &printable))
         js_ReportIsNotDefined(cx, printable.ptr());
 }
 
-#define NATIVE_GET(cx,obj,pobj,shape,getHow,vp,onerr)                         \
-    JS_BEGIN_MACRO                                                            \
-        if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {         \
-            /* Fast path for Object instance properties. */                   \
-            JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT ||                \
-                      !shape->hasDefaultSetter());                            \
-            if (((shape)->slot() != SHAPE_INVALID_SLOT))                      \
-                *(vp) = (pobj)->nativeGetSlot((shape)->slot());               \
-            else                                                              \
-                (vp)->setUndefined();                                         \
-        } else {                                                              \
-            if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))              \
-                onerr;                                                        \
-        }                                                                     \
-    JS_END_MACRO
-
-}}
+} /* namespace mjit */
+} /* namespace js */
 
 #endif /* jslogic_h__ */
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2771,18 +2771,18 @@ CopyProperty(JSContext *cx, HandleObject
         desc.attrs &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
         desc.getter = JS_PropertyStub;
         desc.setter = JS_StrictPropertyStub;
         desc.shortid = 0;
     }
 
     RootedValue value(cx, desc.value);
     objp.set(obj);
-    return !!DefineNativeProperty(cx, obj, id, value, desc.getter, desc.setter,
-                                  desc.attrs, propFlags, desc.shortid);
+    return DefineNativeProperty(cx, obj, id, value, desc.getter, desc.setter,
+                                desc.attrs, propFlags, desc.shortid);
 }
 
 static JSBool
 resolver_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                  MutableHandleObject objp)
 {
     jsval v = JS_GetReservedSlot(obj, 0);
     Rooted<JSObject*> vobj(cx, &v.toObject());
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -844,17 +844,17 @@ Debugger::parseResumptionValue(Maybe<Aut
     if (rv.isNull()) {
         ac.destroy();
         return JSTRAP_ERROR;
     }
 
     /* Check that rv is {return: val} or {throw: val}. */
     JSContext *cx = ac.ref().context();
     Rooted<JSObject*> obj(cx);
-    Shape *shape;
+    RootedShape shape(cx);
     jsid returnId = NameToId(cx->names().return_);
     jsid throwId = NameToId(cx->names().throw_);
     bool okResumption = rv.isObject();
     if (okResumption) {
         obj = &rv.toObject();
         okResumption = obj->isObject();
     }
     if (okResumption) {
@@ -864,18 +864,20 @@ Debugger::parseResumptionValue(Maybe<Aut
              (shape->propid() == returnId || shape->propid() == throwId) &&
              shape->isDataDescriptor();
     }
     if (!okResumption) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_RESUMPTION);
         return handleUncaughtException(ac, vp, callHook);
     }
 
-    if (!js_NativeGet(cx, obj, obj, shape, 0, vp) || !unwrapDebuggeeValue(cx, vp))
-        return handleUncaughtException(ac, vp, callHook);
+    RootedValue v(cx, *vp);
+    if (!js_NativeGet(cx, obj, obj, shape, 0, &v) || !unwrapDebuggeeValue(cx, v.address()))
+        return handleUncaughtException(ac, v.address(), callHook);
+    *vp = v;
 
     ac.destroy();
     if (!cx->compartment->wrap(cx, vp)) {
         vp->setUndefined();
         return JSTRAP_ERROR;
     }
     return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW;
 }
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -35,35 +35,35 @@ Debug_SetSlotRangeToCrashOnTouch(HeapSlo
 {
 #ifdef DEBUG
     Debug_SetValueRangeToCrashOnTouch((Value *) begin, end - begin);
 #endif
 }
 
 } // namespace js
 
-inline js::Shape *
+inline js::UnrootedShape
 js::ObjectImpl::nativeLookup(JSContext *cx, PropertyId pid)
 {
     return nativeLookup(cx, pid.asId());
 }
 
-inline js::Shape *
+inline js::UnrootedShape
 js::ObjectImpl::nativeLookup(JSContext *cx, PropertyName *name)
 {
     return nativeLookup(cx, PropertyId(name));
 }
 
-inline js::Shape *
+inline js::UnrootedShape
 js::ObjectImpl::nativeLookupNoAllocation(PropertyId pid)
 {
     return nativeLookupNoAllocation(pid.asId());
 }
 
-inline js::Shape *
+inline js::UnrootedShape
 js::ObjectImpl::nativeLookupNoAllocation(PropertyName *name)
 {
     return nativeLookupNoAllocation(PropertyId(name));
 }
 
 inline bool
 js::ObjectImpl::nativeContains(JSContext *cx, JS::Handle<jsid> id)
 {
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -159,40 +159,40 @@ js::ObjectImpl::checkShapeConsistency()
         if (throttle < 0)
             throttle = 0;
     }
     if (throttle == 0)
         return;
 
     MOZ_ASSERT(isNative());
 
-    Shape *shape = lastProperty();
-    Shape *prev = NULL;
+    UnrootedShape shape = lastProperty();
+    UnrootedShape prev = NULL;
 
     if (inDictionaryMode()) {
         MOZ_ASSERT(shape->hasTable());
 
         ShapeTable &table = shape->table();
         for (uint32_t fslot = table.freelist; fslot != SHAPE_INVALID_SLOT;
              fslot = getSlot(fslot).toPrivateUint32()) {
             MOZ_ASSERT(fslot < slotSpan());
         }
 
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
-            MOZ_ASSERT_IF(shape != lastProperty(), !shape->hasTable());
+            MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable());
 
             Shape **spp = table.search(shape->propid(), false);
             MOZ_ASSERT(SHAPE_FETCH(spp) == shape);
         }
 
         shape = lastProperty();
         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
             MOZ_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
             if (!prev) {
-                MOZ_ASSERT(shape == lastProperty());
+                MOZ_ASSERT(lastProperty() == shape);
                 MOZ_ASSERT(shape->listp == &shape_);
             } else {
                 MOZ_ASSERT(shape->listp == &prev->parent);
             }
             prev = shape;
         }
     } else {
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
@@ -252,25 +252,26 @@ js::ObjectImpl::slotInRange(uint32_t slo
 #if defined(_MSC_VER) && _MSC_VER >= 1500
 /*
  * Work around a compiler bug in MSVC9 and above, where inlining this function
  * causes stack pointer offsets to go awry and spp to refer to something higher
  * up the stack.
  */
 MOZ_NEVER_INLINE
 #endif
-Shape *
-js::ObjectImpl::nativeLookup(JSContext *cx, jsid id)
+UnrootedShape
+js::ObjectImpl::nativeLookup(JSContext *cx, jsid idArg)
 {
     MOZ_ASSERT(isNative());
     Shape **spp;
+    RootedId id(cx, idArg);
     return Shape::search(cx, lastProperty(), id, &spp);
 }
 
-Shape *
+UnrootedShape
 js::ObjectImpl::nativeLookupNoAllocation(jsid id)
 {
     MOZ_ASSERT(isNative());
     return Shape::searchNoAllocation(lastProperty(), id);
 }
 
 void
 js::ObjectImpl::markChildren(JSTracer *trc)
@@ -498,18 +499,21 @@ js::GetOwnProperty(JSContext *cx, Handle
 
     Rooted<PropertyId> pid(cx, pid_);
 
     if (static_cast<JSObject *>(obj.get())->isProxy()) {
         MOZ_NOT_REACHED("NYI: proxy [[GetOwnProperty]]");
         return false;
     }
 
-    Shape *shape = obj->nativeLookup(cx, pid);
+    /* |shape| is always set /after/ a GC. */
+    UnrootedShape shape = obj->nativeLookup(cx, pid);
     if (!shape) {
+        DropUnrooted(shape);
+
         /* Not found: attempt to resolve it. */
         Class *clasp = obj->getClass();
         JSResolveOp resolve = clasp->resolve;
         if (resolve != JS_ResolveStub) {
             Rooted<jsid> id(cx, pid.get().asId());
             Rooted<JSObject*> robj(cx, static_cast<JSObject*>(obj.get()));
             if (clasp->flags & JSCLASS_NEW_RESOLVE) {
                 Rooted<JSObject*> obj2(cx, NULL);
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -19,16 +19,17 @@
 #include "gc/Heap.h"
 #include "vm/NumericConversions.h"
 #include "vm/String.h"
 
 namespace js {
 
 class Debugger;
 class ObjectImpl;
+ForwardDeclare(Shape);
 
 class AutoPropDescArrayRooter;
 
 static inline PropertyOp
 CastAsPropertyOp(JSObject *object)
 {
     return JS_DATA_TO_FUNC_PTR(PropertyOp, object);
 }
@@ -353,17 +354,17 @@ class ElementsHeader
     union {
         class {
             friend class DenseElementsHeader;
             uint32_t initializedLength;
             uint32_t capacity;
         } dense;
         class {
             friend class SparseElementsHeader;
-            Shape * shape;
+            RawShape shape;
         } sparse;
         class {
             friend class ArrayBufferElementsHeader;
             JSObject * views;
         } buffer;
     };
 
     void staticAsserts() {
@@ -444,17 +445,17 @@ class DenseElementsHeader : public Eleme
 
     DenseElementsHeader(const DenseElementsHeader &other) MOZ_DELETE;
     void operator=(const DenseElementsHeader &other) MOZ_DELETE;
 };
 
 class SparseElementsHeader : public ElementsHeader
 {
   public:
-    Shape * shape() {
+    UnrootedShape shape() {
         MOZ_ASSERT(ElementsHeader::isSparseElements());
         return sparse.shape;
     }
 
     uint32_t length() const {
         MOZ_ASSERT(ElementsHeader::isSparseElements());
         return ElementsHeader::length;
     }
@@ -1150,23 +1151,23 @@ class ObjectImpl : public gc::Cell
      */
     bool hasLazyType() const { return type_->lazy(); }
 
     inline uint32_t slotSpan() const;
 
     /* Compute dynamicSlotsCount() for this object. */
     inline uint32_t numDynamicSlots() const;
 
-    Shape * nativeLookup(JSContext *cx, jsid id);
-    inline Shape * nativeLookup(JSContext *cx, PropertyId pid);
-    inline Shape * nativeLookup(JSContext *cx, PropertyName *name);
+    UnrootedShape nativeLookup(JSContext *cx, jsid id);
+    inline UnrootedShape nativeLookup(JSContext *cx, PropertyId pid);
+    inline UnrootedShape nativeLookup(JSContext *cx, PropertyName *name);
 
-    Shape * nativeLookupNoAllocation(jsid id);
-    inline Shape * nativeLookupNoAllocation(PropertyId pid);
-    inline Shape * nativeLookupNoAllocation(PropertyName *name);
+    UnrootedShape nativeLookupNoAllocation(jsid id);
+    inline UnrootedShape nativeLookupNoAllocation(PropertyId pid);
+    inline UnrootedShape nativeLookupNoAllocation(PropertyName *name);
 
     inline bool nativeContains(JSContext *cx, Handle<jsid> id);
     inline bool nativeContains(JSContext *cx, Handle<PropertyName*> name);
     inline bool nativeContains(JSContext *cx, Handle<Shape*> shape);
 
     inline bool nativeContainsNoAllocation(jsid id);
     inline bool nativeContainsNoAllocation(PropertyName *name);
     inline bool nativeContainsNoAllocation(Shape &shape);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -317,69 +317,63 @@ RegExpObject::createShared(JSContext *cx
     JS_ASSERT(!maybeShared());
     if (!cx->compartment->regExps.get(cx, getSource(), getFlags(), g))
         return false;
 
     self->setShared(cx, **g);
     return true;
 }
 
-Shape *
+UnrootedShape
 RegExpObject::assignInitialShape(JSContext *cx)
 {
     JS_ASSERT(isRegExp());
     JS_ASSERT(nativeEmpty());
 
     JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
     JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
     JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
     JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
     JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
     JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
 
     RootedObject self(cx, this);
 
     /* The lastIndex property alone is writable but non-configurable. */
-    if (!addDataProperty(cx, NameToId(cx->names().lastIndex),
-                         LAST_INDEX_SLOT, JSPROP_PERMANENT))
-    {
-        return NULL;
-    }
+    if (!addDataProperty(cx, NameToId(cx->names().lastIndex), LAST_INDEX_SLOT, JSPROP_PERMANENT))
+        return UnrootedShape(NULL);
 
     /* Remaining instance properties are non-writable and non-configurable. */
-    if (!self->addDataProperty(cx, NameToId(cx->names().source),
-                               SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !self->addDataProperty(cx, NameToId(cx->names().global),
-                               GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !self->addDataProperty(cx, NameToId(cx->names().ignoreCase),
-                               IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
-        !self->addDataProperty(cx, NameToId(cx->names().multiline),
-                               MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY))
-    {
-        return NULL;
-    }
-
-    return self->addDataProperty(cx, NameToId(cx->names().sticky),
-                                 STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
+    unsigned attrs = JSPROP_PERMANENT | JSPROP_READONLY;
+    if (!self->addDataProperty(cx, NameToId(cx->names().source), SOURCE_SLOT, attrs))
+        return UnrootedShape(NULL);
+    if (!self->addDataProperty(cx, NameToId(cx->names().global), GLOBAL_FLAG_SLOT, attrs))
+        return UnrootedShape(NULL);
+    if (!self->addDataProperty(cx, NameToId(cx->names().ignoreCase), IGNORE_CASE_FLAG_SLOT, attrs))
+        return UnrootedShape(NULL);
+    if (!self->addDataProperty(cx, NameToId(cx->names().multiline), MULTILINE_FLAG_SLOT, attrs))
+        return UnrootedShape(NULL);
+    return self->addDataProperty(cx, NameToId(cx->names().sticky), STICKY_FLAG_SLOT, attrs);
 }
 
 inline bool
 RegExpObject::init(JSContext *cx, HandleAtom source, RegExpFlag flags)
 {
     Rooted<RegExpObject *> self(cx, this);
 
     if (nativeEmpty()) {
         if (isDelegate()) {
             if (!assignInitialShape(cx))
                 return false;
         } else {
-            Shape *shape = assignInitialShape(cx);
+            RootedShape shape(cx, assignInitialShape(cx));
             if (!shape)
                 return false;
-            EmptyShape::insertInitialShape(cx, shape, self->getProto());
+            RootedObject proto(cx, self->getProto());
+            EmptyShape::insertInitialShape(cx, shape, proto);
         }
         JS_ASSERT(!self->nativeEmpty());
     }
 
     JS_ASSERT(self->nativeLookupNoAllocation(NameToId(cx->names().lastIndex))->slot() ==
               LAST_INDEX_SLOT);
     JS_ASSERT(self->nativeLookupNoAllocation(NameToId(cx->names().source))->slot() ==
               SOURCE_SLOT);
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -400,17 +400,17 @@ class RegExpObject : public JSObject
   private:
     friend class RegExpObjectBuilder;
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
      * changing this regular expression object's last property to it.
      */
-    Shape *assignInitialShape(JSContext *cx);
+    UnrootedShape assignInitialShape(JSContext *cx);
 
     inline bool init(JSContext *cx, HandleAtom source, RegExpFlag flags);
 
     /*
      * Precondition: the syntax for |source| has already been validated.
      * Side effect: sets the private field.
      */
     bool createShared(JSContext *cx, RegExpGuard *g);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -53,22 +53,24 @@ StaticScopeIter::operator++(int)
 bool
 StaticScopeIter::hasDynamicScopeObject() const
 {
     return obj->isStaticBlock()
            ? obj->asStaticBlock().needsClone()
            : obj->toFunction()->isHeavyweight();
 }
 
-Shape *
+UnrootedShape
 StaticScopeIter::scopeShape() const
 {
     JS_ASSERT(hasDynamicScopeObject());
     JS_ASSERT(type() != NAMED_LAMBDA);
-    return type() == BLOCK ? block().lastProperty() : funScript()->bindings.callObjShape();
+    return type() == BLOCK
+           ? UnrootedShape(block().lastProperty())
+           : funScript()->bindings.callObjShape();
 }
 
 StaticScopeIter::Type
 StaticScopeIter::type() const
 {
     if (onNamedLambda)
         return NAMED_LAMBDA;
     return obj->isStaticBlock() ? BLOCK : FUNCTION;
@@ -298,18 +300,18 @@ DeclEnvObject::createTemplateObject(JSCo
     RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL));
     if (!obj)
         return NULL;
 
     // Assign a fixed slot to a property with the same name as the lambda.
     Rooted<jsid> id(cx, AtomToId(fun->atom()));
     Class *clasp = obj->getClass();
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
-    if (!obj->putProperty(cx, id, clasp->getProperty, clasp->setProperty,
-                          lambdaSlot(), attrs, 0, 0))
+    if (!JSObject::putProperty(cx, obj, id, clasp->getProperty, clasp->setProperty,
+                               lambdaSlot(), attrs, 0, 0))
     {
         return NULL;
     }
 
     JS_ASSERT(!obj->hasDynamicSlots());
     return &obj->asDeclEnv();
 }
 
@@ -684,40 +686,40 @@ StaticBlockObject::create(JSContext *cx)
 
     JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyBlockShape, type, NULL);
     if (!obj)
         return NULL;
 
     return &obj->asStaticBlock();
 }
 
-/* static */ Shape *
+/* static */ UnrootedShape
 StaticBlockObject::addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id,
                           int index, bool *redeclared)
 {
     JS_ASSERT(JSID_IS_ATOM(id) || (JSID_IS_INT(id) && JSID_TO_INT(id) == index));
 
     *redeclared = false;
 
     /* Inline JSObject::addProperty in order to trap the redefinition case. */
     Shape **spp;
     if (Shape::search(cx, block->lastProperty(), id, &spp, true)) {
         *redeclared = true;
-        return NULL;
+        return UnrootedShape(NULL);
     }
 
     /*
      * Don't convert this object to dictionary mode so that we can clone the
      * block's shape later.
      */
     uint32_t slot = JSSLOT_FREE(&BlockClass) + index;
-    return block->addPropertyInternal(cx, id, /* getter = */ NULL, /* setter = */ NULL,
-                                      slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
-                                      Shape::HAS_SHORTID, index, spp,
-                                      /* allowDictionary = */ false);
+    return JSObject::addPropertyInternal(cx, block, id, /* getter = */ NULL, /* setter = */ NULL,
+                                         slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
+                                         Shape::HAS_SHORTID, index, spp,
+                                         /* allowDictionary = */ false);
 }
 
 Class js::BlockClass = {
     "Block",
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,         /* addProperty */
@@ -795,36 +797,39 @@ js::XDRStaticBlockObject(XDRState<mode> 
             obj->setAliased(i, !!aliased);
         }
     } else {
         AutoShapeVector shapes(cx);
         if (!shapes.growBy(count))
             return false;
 
         for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
-            Shape *shape = &r.front();
+            UnrootedShape shape = &r.front();
             shapes[shape->shortid()] = shape;
         }
 
         /*
          * XDR the block object's properties. We know that there are 'count'
          * properties to XDR, stored as id/shortid pairs.
          */
+        RootedShape shape(cx);
+        RootedId propid(cx);
+        RootedAtom atom(cx);
         for (unsigned i = 0; i < count; i++) {
-            Shape *shape = shapes[i];
+            shape = shapes[i];
             JS_ASSERT(shape->hasDefaultGetter());
             JS_ASSERT(unsigned(shape->shortid()) == i);
 
-            jsid propid = shape->propid();
+            propid = shape->propid();
             JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid));
 
             /* The empty string indicates an int id. */
-            RootedAtom atom(cx, JSID_IS_ATOM(propid)
-                                ? JSID_TO_ATOM(propid)
-                                : cx->runtime->emptyString);
+            atom = JSID_IS_ATOM(propid)
+                   ? JSID_TO_ATOM(propid)
+                   : cx->runtime->emptyString;
             if (!XDRAtom(xdr, &atom))
                 return false;
 
             uint32_t aliased = obj->isAliased(i);
             if (!xdr->codeUint32(&aliased))
                 return false;
         }
     }
@@ -1217,17 +1222,17 @@ class DebugScopeProxy : public BaseProxy
             }
 
             return true;
         }
 
         /* Handle unaliased let and catch bindings at block scope. */
         if (scope->isClonedBlock()) {
             ClonedBlockObject &block = scope->asClonedBlock();
-            Shape *shape = block.lastProperty()->search(cx, id);
+            UnrootedShape shape = block.lastProperty()->search(cx, id);
             if (!shape)
                 return false;
 
             AutoAssertNoGC nogc;
             unsigned i = shape->shortid();
             if (block.staticBlock().isAliased(i))
                 return false;
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -60,17 +60,17 @@ class StaticScopeIter
   public:
     explicit StaticScopeIter(JSObject *obj);
 
     bool done() const;
     void operator++(int);
 
     /* Return whether this static scope will be on the dynamic scope chain. */
     bool hasDynamicScopeObject() const;
-    Shape *scopeShape() const;
+    UnrootedShape scopeShape() const;
 
     enum Type { BLOCK, FUNCTION, NAMED_LAMBDA };
     Type type() const;
 
     StaticBlockObject &block() const;
     UnrootedScript funScript() const;
 };
 
@@ -341,18 +341,18 @@ class StaticBlockObject : public BlockOb
      * The parser uses 'enclosingBlock' as the prev-link in the pc->blockChain
      * stack. Note: in the case of hoisting, this prev-link will not ultimately
      * be the same as enclosingBlock, initEnclosingStaticScope must be called
      * separately in the emitter. 'reset' is just for asserting stackiness.
      */
     void initPrevBlockChainFromParser(StaticBlockObject *prev);
     void resetPrevBlockChainFromParser();
 
-    static Shape *addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id,
-                         int index, bool *redeclared);
+    static UnrootedShape addVar(JSContext *cx, Handle<StaticBlockObject*> block, HandleId id,
+                                int index, bool *redeclared);
 };
 
 class ClonedBlockObject : public BlockObject
 {
   public:
     static ClonedBlockObject *create(JSContext *cx, Handle<StaticBlockObject *> block,
                                      StackFrame *fp);
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -850,50 +850,55 @@ class AutoNameVector : public AutoVector
 
 } /* namespace js */
 
 /* Avoid requiring vm/String-inl.h just to call getChars. */
 
 JS_ALWAYS_INLINE const jschar *
 JSString::getChars(JSContext *cx)
 {
+    JS::AutoAssertNoGC nogc;
     if (JSLinearString *str = ensureLinear(cx))
         return str->chars();
     return NULL;
 }
 
 JS_ALWAYS_INLINE const jschar *
 JSString::getCharsZ(JSContext *cx)
 {
+    JS::AutoAssertNoGC nogc;
     if (JSFlatString *str = ensureFlat(cx))
         return str->chars();
     return NULL;
 }
 
 JS_ALWAYS_INLINE JSLinearString *
 JSString::ensureLinear(JSContext *cx)
 {
+    JS::AutoAssertNoGC nogc;
     return isLinear()
            ? &asLinear()
            : asRope().flatten(cx);
 }
 
 JS_ALWAYS_INLINE JSFlatString *
 JSString::ensureFlat(JSContext *cx)
 {
+    JS::AutoAssertNoGC nogc;
     return isFlat()
            ? &asFlat()
            : isDependent()
              ? asDependent().undepend(cx)
              : asRope().flatten(cx);
 }
 
 JS_ALWAYS_INLINE JSStableString *
 JSString::ensureStable(JSContext *maybecx)
 {
+    JS::AutoAssertNoGC nogc;
     if (isRope()) {
         JSFlatString *flat = asRope().flatten(maybecx);
         if (!flat)
             return NULL;
         JS_ASSERT(!flat->isInline());
         return &flat->asStable();
     }
 
--- a/js/src/vm/StringObject-inl.h
+++ b/js/src/vm/StringObject-inl.h
@@ -17,29 +17,31 @@ JSObject::asString()
     return *static_cast<js::StringObject *>(this);
 }
 
 namespace js {
 
 inline bool
 StringObject::init(JSContext *cx, HandleString str)
 {
+    AssertCanGC();
     JS_ASSERT(gc::GetGCKindSlots(getAllocKind()) == 2);
 
     Rooted<StringObject *> self(cx, this);
 
     if (nativeEmpty()) {
         if (isDelegate()) {
             if (!assignInitialShape(cx))
                 return false;
         } else {
-            Shape *shape = assignInitialShape(cx);
+            RootedShape shape(cx, assignInitialShape(cx));
             if (!shape)
                 return false;
-            EmptyShape::insertInitialShape(cx, shape, self->getProto());
+            RootedObject proto(cx, self->getProto());
+            EmptyShape::insertInitialShape(cx, shape, proto);
         }
     }
 
     JS_ASSERT(self->nativeLookupNoAllocation(NameToId(cx->names().length))->slot() == LENGTH_SLOT);
 
     self->setStringThis(str);
 
     return true;
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -61,14 +61,14 @@ class StringObject : public JSObject
     friend JSObject *
     ::js_InitStringClass(JSContext *cx, js::HandleObject global);
 
     /*
      * Compute the initial shape to associate with fresh String objects, which
      * encodes the initial length property. Return the shape after changing
      * this String object's last property to it.
      */
-    Shape *assignInitialShape(JSContext *cx);
+    UnrootedShape assignInitialShape(JSContext *cx);
 };
 
 } // namespace js
 
 #endif /* StringObject_h__ */