Bug 1133254 - Dehandlify shape-updating object methods, allow setting multiple flags on an object at once, r=terrence.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 18 Feb 2015 08:08:36 -0700
changeset 229698 b1cc2dd3e35c8c8e074ac15e3e618520dc07edbc
parent 229697 4ef497dc6f9b7fd560ac6f167c1c8d483d07fb3f
child 229699 acf46fe8b0547a5051d9be1629c901ac93844729
push id28294
push userryanvm@gmail.com
push dateThu, 19 Feb 2015 01:30:38 +0000
treeherderautoland@360b5f211180 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1133254
milestone38.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 1133254 - Dehandlify shape-updating object methods, allow setting multiple flags on an object at once, r=terrence.
js/src/jsfun.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/vm/GlobalObject.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/NativeObject.cpp
js/src/vm/NativeObject.h
js/src/vm/ObjectGroup.cpp
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/vm/UnboxedObject.cpp
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1342,20 +1342,20 @@ JSFunction::initBoundFunction(JSContext 
     /*
      * Convert to a dictionary to set the BOUND_FUNCTION flag and increase
      * the slot span to cover the arguments and additional slots for the 'this'
      * value and arguments count.
      */
     if (!self->toDictionaryMode(cx))
         return false;
 
-    if (!self->setFlag(cx, BaseShape::BOUND_FUNCTION))
+    if (!self->JSObject::setFlags(cx, BaseShape::BOUND_FUNCTION))
         return false;
 
-    if (!NativeObject::setSlotSpan(cx, self, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
+    if (!self->setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
         return false;
 
     self->setSlot(JSSLOT_BOUND_FUNCTION_TARGET, ObjectValue(*target));
     self->setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
     self->setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
 
     self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1033,17 +1033,17 @@ js::SetIntegrityLevel(JSContext *cx, Han
                 MarkTypePropertyNonWritable(cx, nobj, child->propid);
 
             last = cx->compartment()->propertyTree.getChild(cx, last, *child);
             if (!last)
                 return false;
         }
 
         MOZ_ASSERT(nobj->lastProperty()->slotSpan() == last->slotSpan());
-        JS_ALWAYS_TRUE(NativeObject::setLastProperty(cx, nobj, last));
+        JS_ALWAYS_TRUE(nobj->setLastProperty(cx, last));
     } else {
         RootedId id(cx);
         Rooted<PropertyDescriptor> desc(cx);
 
         const unsigned AllowConfigure = JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
                                         JSPROP_IGNORE_VALUE;
         const unsigned AllowConfigureAndWritable = AllowConfigure & ~JSPROP_IGNORE_READONLY;
 
@@ -1840,17 +1840,17 @@ js::DeepCloneObjectLiteral(JSContext *cx
         clone->setDenseInitializedLength(i + 1);
         clone->initDenseElement(i, v);
     }
 
     MOZ_ASSERT(obj->compartment() == clone->compartment());
     MOZ_ASSERT(!obj->hasPrivate());
     RootedShape shape(cx, obj->lastProperty());
     size_t span = shape->slotSpan();
-    clone->setLastProperty(cx, clone, shape);
+    clone->setLastProperty(cx, shape);
     for (size_t i = 0; i < span; i++) {
         v = obj->getSlot(i);
         if (v.isObject()) {
             deepObj = &v.toObject().as<NativeObject>();
             deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
             if (!deepObj)
                 return nullptr;
             v.setObject(*deepObj);
@@ -2132,17 +2132,17 @@ js::CloneObjectLiteral(JSContext *cx, Ha
 
         RootedPlainObject res(cx, NewObjectWithGroup<PlainObject>(cx, group, parent, kind,
                                                                   MaybeSingletonObject));
         if (!res)
             return nullptr;
 
         RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->lastProperty(),
                                                               TaggedProto(proto), parent, kind));
-        if (!newShape || !NativeObject::setLastProperty(cx, res, newShape))
+        if (!newShape || !res->setLastProperty(cx, newShape))
             return nullptr;
 
         return res;
     }
 
     RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
     MOZ_ASSERT(srcArray->denseElementsAreCopyOnWrite());
     MOZ_ASSERT(srcArray->getElementsHeader()->ownerObject() == srcObj);
@@ -3134,17 +3134,17 @@ js::PreventExtensions(JSContext *cx, Han
      * initialized length and capacity of the object to zero and ensure that no
      * new dense elements can be added without calling growElements(), which
      * checks isExtensible().
      */
     if (obj->isNative() && !NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
         return false;
 
     *succeeded = true;
-    return obj->setFlag(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE);
+    return obj->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE);
 }
 
 bool
 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                              MutableHandle<PropertyDescriptor> desc)
 {
     if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor)
         return op(cx, obj, id, desc);
@@ -3229,17 +3229,17 @@ bool
 js::SetImmutablePrototype(ExclusiveContext *cx, HandleObject obj, bool *succeeded)
 {
     if (obj->hasLazyPrototype()) {
         if (!cx->shouldBeJSContext())
             return false;
         return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded);
     }
 
-    if (!obj->setFlag(cx, BaseShape::IMMUTABLE_PROTOTYPE))
+    if (!obj->setFlags(cx, BaseShape::IMMUTABLE_PROTOTYPE))
         return false;
     *succeeded = true;
     return true;
 }
 
 bool
 js::GetPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
                           MutableHandle<PropertyDescriptor> desc)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -184,89 +184,87 @@ class JSObject : public js::gc::Cell
 
     // Set the initial slots and elements of an object. These pointers are only
     // valid for native objects, but during initialization are set for all
     // objects. For non-native objects, these must not be dynamically allocated
     // pointers which leak when the non-native object finishes initialization.
     inline void setInitialSlotsMaybeNonNative(js::HeapSlot *slots);
     inline void setInitialElementsMaybeNonNative(js::HeapSlot *elements);
 
-  protected:
     enum GenerateShape {
         GENERATE_NONE,
         GENERATE_SHAPE
     };
 
-    bool setFlag(js::ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag,
-                 GenerateShape generateShape = GENERATE_NONE);
+    bool setFlags(js::ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flags,
+                  GenerateShape generateShape = GENERATE_NONE);
 
-  public:
     /*
      * An object is a delegate if it is on another object's prototype or scope
      * chain, and therefore the delegate might be asked implicitly to get or
      * set a property on behalf of another object. Delegates may be accessed
      * directly too, as may any object, but only those objects linked after the
      * head of any prototype or scope chain are flagged as delegates. This
      * definition helps to optimize shape-based property cache invalidation
      * (see Purge{Scope,Proto}Chain in jsobj.cpp).
      */
     bool isDelegate() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::DELEGATE);
     }
 
     bool setDelegate(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
+        return setFlags(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
     }
 
     bool isBoundFunction() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION);
     }
 
     inline bool hasSpecialEquality() const;
 
     bool watched() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED);
     }
     bool setWatched(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::WATCHED, GENERATE_SHAPE);
+        return setFlags(cx, js::BaseShape::WATCHED, GENERATE_SHAPE);
     }
 
     /* See InterpreterFrame::varObj. */
     inline bool isQualifiedVarObj();
     bool setQualifiedVarObj(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::QUALIFIED_VAROBJ);
+        return setFlags(cx, js::BaseShape::QUALIFIED_VAROBJ);
     }
 
     inline bool isUnqualifiedVarObj();
     bool setUnqualifiedVarObj(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::UNQUALIFIED_VAROBJ);
+        return setFlags(cx, js::BaseShape::UNQUALIFIED_VAROBJ);
     }
 
     /*
      * Objects with an uncacheable proto can have their prototype mutated
      * without inducing a shape change on the object. Property cache entries
      * and JIT inline caches should not be filled for lookups across prototype
      * lookups on the object.
      */
     bool hasUncacheableProto() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::UNCACHEABLE_PROTO);
     }
     bool setUncacheableProto(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
+        return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
     }
 
     /*
      * Whether SETLELEM was used to access this object. See also the comment near
      * PropertyTree::MAX_HEIGHT.
      */
     bool hadElementsAccess() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::HAD_ELEMENTS_ACCESS);
     }
     bool setHadElementsAccess(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::HAD_ELEMENTS_ACCESS);
+        return setFlags(cx, js::BaseShape::HAD_ELEMENTS_ACCESS);
     }
 
     /*
      * Whether there may be indexed properties on this object, excluding any in
      * the object's elements.
      */
     bool isIndexed() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED);
@@ -392,34 +390,34 @@ class JSObject : public js::gc::Cell
      * Mark an object that has been iterated over and is a singleton. We need
      * to recover this information in the object's type information after it
      * is purged on GC.
      */
     bool isIteratedSingleton() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::ITERATED_SINGLETON);
     }
     bool setIteratedSingleton(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::ITERATED_SINGLETON);
+        return setFlags(cx, js::BaseShape::ITERATED_SINGLETON);
     }
 
     /*
      * Mark an object as requiring its default 'new' type to have unknown
      * properties.
      */
     bool isNewGroupUnknown() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::NEW_GROUP_UNKNOWN);
     }
     static bool setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj);
 
     // Mark an object as having its 'new' script information cleared.
     bool wasNewScriptCleared() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::NEW_SCRIPT_CLEARED);
     }
     bool setNewScriptCleared(js::ExclusiveContext *cx) {
-        return setFlag(cx, js::BaseShape::NEW_SCRIPT_CLEARED);
+        return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED);
     }
 
     /* Set a new prototype for an object with a singleton type. */
     bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle<js::TaggedProto> proto);
 
     /*
      * For bootstrapping, whether to splice a prototype for Function.prototype
      * or the global object.
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -609,18 +609,18 @@ GlobalObject::addIntrinsicValue(JSContex
 {
     RootedNativeObject holder(cx, intrinsicsHolder());
 
     uint32_t slot = holder->slotSpan();
     RootedShape last(cx, holder->lastProperty());
     Rooted<UnownedBaseShape*> base(cx, last->base()->unowned());
 
     StackShape child(base, id, slot, 0, 0);
-    RootedShape shape(cx, cx->compartment()->propertyTree.getChild(cx, last, child));
+    Shape *shape = cx->compartment()->propertyTree.getChild(cx, last, child);
     if (!shape)
         return false;
 
-    if (!NativeObject::setLastProperty(cx, holder, shape))
+    if (!holder->setLastProperty(cx, shape))
         return false;
 
     holder->setSlot(shape->slot(), value);
     return true;
 }
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -34,19 +34,17 @@ NativeObject::changePropertyAttributes(J
 {
     return !!changeProperty(cx, obj, shape, attrs, 0, shape->getter(), shape->setter());
 }
 
 inline void
 NativeObject::removeLastProperty(ExclusiveContext *cx)
 {
     MOZ_ASSERT(canRemoveLastProperty());
-    RootedNativeObject self(cx, this);
-    RootedShape prev(cx, lastProperty()->previous());
-    JS_ALWAYS_TRUE(setLastProperty(cx, self, prev));
+    JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->previous()));
 }
 
 inline bool
 NativeObject::canRemoveLastProperty()
 {
     /*
      * Check that the information about the object stored in the last
      * property's base shape is consistent with that stored in the previous
@@ -330,18 +328,17 @@ CopyInitializerObject(JSContext *cx, Han
     gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
     allocKind = gc::GetBackgroundAllocKind(allocKind);
     MOZ_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->asTenured().getAllocKind());
     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
     if (!obj)
         return nullptr;
 
     RootedObject metadata(cx, obj->getMetadata());
-    RootedShape lastProp(cx, baseobj->lastProperty());
-    if (!NativeObject::setLastProperty(cx, obj, lastProp))
+    if (!obj->setLastProperty(cx, baseobj->lastProperty()))
         return nullptr;
     if (metadata && !JSObject::setMetadata(cx, obj, metadata))
         return nullptr;
 
     return obj;
 }
 
 inline NativeObject *
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -291,66 +291,65 @@ js::NativeObject::dynamicSlotsCount(uint
 void
 PropDesc::trace(JSTracer *trc)
 {
     gc::MarkValueRoot(trc, &value_, "PropDesc value");
     gc::MarkValueRoot(trc, &get_, "PropDesc get");
     gc::MarkValueRoot(trc, &set_, "PropDesc set");
 }
 
-/* static */ inline bool
-NativeObject::updateSlotsForSpan(ExclusiveContext *cx,
-                                 HandleNativeObject obj, size_t oldSpan, size_t newSpan)
+inline bool
+NativeObject::updateSlotsForSpan(ExclusiveContext *cx, size_t oldSpan, size_t newSpan)
 {
     MOZ_ASSERT(oldSpan != newSpan);
 
-    size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
-    size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
+    size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan, getClass());
+    size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan, getClass());
 
     if (oldSpan < newSpan) {
-        if (oldCount < newCount && !growSlots(cx, obj, oldCount, newCount))
+        if (oldCount < newCount && !growSlots(cx, oldCount, newCount))
             return false;
 
         if (newSpan == oldSpan + 1)
-            obj->initSlotUnchecked(oldSpan, UndefinedValue());
+            initSlotUnchecked(oldSpan, UndefinedValue());
         else
-            obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
+            initializeSlotRange(oldSpan, newSpan - oldSpan);
     } else {
         /* Trigger write barriers on the old slots before reallocating. */
-        obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
-        obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
+        prepareSlotRangeForOverwrite(newSpan, oldSpan);
+        invalidateSlotRange(newSpan, oldSpan - newSpan);
 
         if (oldCount > newCount)
-            shrinkSlots(cx, obj, oldCount, newCount);
+            shrinkSlots(cx, oldCount, newCount);
     }
 
     return true;
 }
 
-/* static */ bool
-NativeObject::setLastProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleShape shape)
+bool
+NativeObject::setLastProperty(ExclusiveContext *cx, Shape *shape)
 {
-    MOZ_ASSERT(!obj->inDictionaryMode());
+    MOZ_ASSERT(!inDictionaryMode());
     MOZ_ASSERT(!shape->inDictionary());
-    MOZ_ASSERT(shape->compartment() == obj->compartment());
-    MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
-    MOZ_ASSERT(shape->getObjectClass() == obj->getClass());
+    MOZ_ASSERT(shape->compartment() == compartment());
+    MOZ_ASSERT(shape->numFixedSlots() == numFixedSlots());
+    MOZ_ASSERT(shape->getObjectClass() == getClass());
 
-    size_t oldSpan = obj->lastProperty()->slotSpan();
+    size_t oldSpan = lastProperty()->slotSpan();
     size_t newSpan = shape->slotSpan();
 
     if (oldSpan == newSpan) {
-        obj->shape_ = shape;
+        shape_ = shape;
         return true;
     }
 
-    if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
+    if (!updateSlotsForSpan(cx, oldSpan, newSpan))
         return false;
 
-    obj->shape_ = shape;
+    shape_ = shape;
     return true;
 }
 
 void
 NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
 {
     MOZ_ASSERT(!inDictionaryMode());
     MOZ_ASSERT(!shape->inDictionary());
@@ -378,53 +377,52 @@ NativeObject::setLastPropertyMakeNonNati
     MOZ_ASSERT(shape->slotSpan() == 0);
     MOZ_ASSERT(shape->numFixedSlots() == 0);
     MOZ_ASSERT(!hasDynamicElements());
     MOZ_ASSERT(!hasDynamicSlots());
 
     shape_ = shape;
 }
 
-/* static */ void
-NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj,
-                                        HandleShape shape)
+void
+NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, Shape *shape)
 {
-    MOZ_ASSERT(obj->getClass()->isNative());
-    MOZ_ASSERT(!obj->lastProperty()->isNative());
+    MOZ_ASSERT(getClass()->isNative());
+    MOZ_ASSERT(!lastProperty()->isNative());
     MOZ_ASSERT(shape->isNative());
-    MOZ_ASSERT(!obj->inDictionaryMode());
+    MOZ_ASSERT(!inDictionaryMode());
     MOZ_ASSERT(!shape->inDictionary());
-    MOZ_ASSERT(shape->compartment() == obj->compartment());
+    MOZ_ASSERT(shape->compartment() == compartment());
 
-    obj->shape_ = shape;
-    obj->slots_ = nullptr;
-    obj->elements_ = emptyObjectElements;
+    shape_ = shape;
+    slots_ = nullptr;
+    elements_ = emptyObjectElements;
 
     size_t oldSpan = shape->numFixedSlots();
     size_t newSpan = shape->slotSpan();
 
-    // A failures at this point will leave the object as a mutant, and we
+    // A failure at this point will leave the object as a mutant, and we
     // can't recover.
-    if (oldSpan != newSpan && !updateSlotsForSpan(cx, obj, oldSpan, newSpan))
+    if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan))
         CrashAtUnhandlableOOM("NativeObject::setLastPropertyMakeNative");
 }
 
-/* static */ bool
-NativeObject::setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span)
+bool
+NativeObject::setSlotSpan(ExclusiveContext *cx, uint32_t span)
 {
-    MOZ_ASSERT(obj->inDictionaryMode());
+    MOZ_ASSERT(inDictionaryMode());
 
-    size_t oldSpan = obj->lastProperty()->base()->slotSpan();
+    size_t oldSpan = lastProperty()->base()->slotSpan();
     if (oldSpan == span)
         return true;
 
-    if (!updateSlotsForSpan(cx, obj, oldSpan, span))
+    if (!updateSlotsForSpan(cx, oldSpan, span))
         return false;
 
-    obj->lastProperty()->base()->setSlotSpan(span);
+    lastProperty()->base()->setSlotSpan(span);
     return true;
 }
 
 // This will not run the garbage collector.  If a nursery cannot accomodate the slot array
 // an attempt will be made to place the array in the tenured area.
 static HeapSlot *
 AllocateSlots(ExclusiveContext *cx, JSObject *obj, uint32_t nslots)
 {
@@ -443,77 +441,76 @@ ReallocateSlots(ExclusiveContext *cx, JS
 {
     if (cx->isJSContext()) {
         return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
                                                                         oldCount, newCount);
     }
     return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
 }
 
-/* static */ bool
-NativeObject::growSlots(ExclusiveContext *cx, HandleNativeObject obj, uint32_t oldCount, uint32_t newCount)
+bool
+NativeObject::growSlots(ExclusiveContext *cx, uint32_t oldCount, uint32_t newCount)
 {
     MOZ_ASSERT(newCount > oldCount);
-    MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
+    MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
 
     /*
      * Slot capacities are determined by the span of allocated objects. Due to
      * the limited number of bits to store shape slots, object growth is
      * throttled well before the slot capacity can overflow.
      */
     NativeObject::slotsSizeMustNotOverflow();
     MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
 
     if (!oldCount) {
-        obj->slots_ = AllocateSlots(cx, obj, newCount);
-        if (!obj->slots_)
+        slots_ = AllocateSlots(cx, this, newCount);
+        if (!slots_)
             return false;
-        Debug_SetSlotRangeToCrashOnTouch(obj->slots_, newCount);
+        Debug_SetSlotRangeToCrashOnTouch(slots_, newCount);
         return true;
     }
 
-    HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots_, oldCount, newCount);
+    HeapSlot *newslots = ReallocateSlots(cx, this, slots_, oldCount, newCount);
     if (!newslots)
         return false;  /* Leave slots at its old size. */
 
-    obj->slots_ = newslots;
+    slots_ = newslots;
 
-    Debug_SetSlotRangeToCrashOnTouch(obj->slots_ + oldCount, newCount - oldCount);
+    Debug_SetSlotRangeToCrashOnTouch(slots_ + oldCount, newCount - oldCount);
 
     return true;
 }
 
 static void
 FreeSlots(ExclusiveContext *cx, HeapSlot *slots)
 {
     // Note: threads without a JSContext do not have access to GGC nursery allocated things.
     if (cx->isJSContext())
         return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
     js_free(slots);
 }
 
-/* static */ void
-NativeObject::shrinkSlots(ExclusiveContext *cx, HandleNativeObject obj,
-                          uint32_t oldCount, uint32_t newCount)
+void
+NativeObject::shrinkSlots(ExclusiveContext *cx, uint32_t oldCount, uint32_t newCount)
 {
     MOZ_ASSERT(newCount < oldCount);
 
     if (newCount == 0) {
-        FreeSlots(cx, obj->slots_);
-        obj->slots_ = nullptr;
+        FreeSlots(cx, slots_);
+        slots_ = nullptr;
         return;
     }
 
-    MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
+    MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
 
-    HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots_, oldCount, newCount);
+    HeapSlot *newslots = ReallocateSlots(cx, this, slots_, oldCount, newCount);
     if (!newslots)
         return;  /* Leave slots at its old size. */
 
-    obj->slots_ = newslots;
+    slots_ = newslots;
 }
 
 /* static */ bool
 NativeObject::sparsifyDenseElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index)
 {
     if (!obj->maybeCopyElementsForWrite(cx))
         return false;
 
@@ -983,17 +980,17 @@ NativeObject::allocSlot(ExclusiveContext
 
     if (slot >= SHAPE_MAXIMUM_SLOT) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     *slotp = slot;
 
-    if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
+    if (obj->inDictionaryMode() && !obj->setSlotSpan(cx, slot + 1))
         return false;
 
     return true;
 }
 
 void
 NativeObject::freeSlot(uint32_t slot)
 {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -394,39 +394,35 @@ class NativeObject : public JSObject
     }
     uint32_t getDenseInitializedLength() {
         return getElementsHeader()->initializedLength;
     }
     uint32_t getDenseCapacity() {
         return getElementsHeader()->capacity;
     }
 
-    /*
-     * Update the last property, keeping the number of allocated slots in sync
-     * with the object's new slot span.
-     */
-    static bool setLastProperty(ExclusiveContext *cx,
-                                HandleNativeObject obj, HandleShape shape);
+    // Update the last property, keeping the number of allocated slots in sync
+    // with the object's new slot span.
+    bool setLastProperty(ExclusiveContext *cx, Shape *shape);
 
     // As for setLastProperty(), but allows the number of fixed slots to
     // change. This can only be used when fixed slots are being erased from the
     // object, and only when the object will not require dynamic slots to cover
     // the new properties.
     void setLastPropertyShrinkFixedSlots(Shape *shape);
 
     // As for setLastProperty(), but changes the class associated with the
     // object to a non-native one. This leaves the object with a type and shape
     // that are (temporarily) inconsistent.
     void setLastPropertyMakeNonNative(Shape *shape);
 
     // As for setLastProperty(), but changes the class associated with the
     // object to a native one. The object's type has already been changed, and
     // this brings the shape into sync with it.
-    static void setLastPropertyMakeNative(ExclusiveContext *cx, HandleNativeObject obj,
-                                          HandleShape shape);
+    void setLastPropertyMakeNative(ExclusiveContext *cx, Shape *shape);
 
   protected:
 #ifdef DEBUG
     void checkShapeConsistency();
 #else
     void checkShapeConsistency() { }
 #endif
 
@@ -442,17 +438,17 @@ class NativeObject : public JSObject
      */
     inline void removeLastProperty(ExclusiveContext *cx);
     inline bool canRemoveLastProperty();
 
     /*
      * Update the slot span directly for a dictionary object, and allocate
      * slots to cover the new span if necessary.
      */
-    static bool setSlotSpan(ExclusiveContext *cx, HandleNativeObject obj, uint32_t span);
+    bool setSlotSpan(ExclusiveContext *cx, uint32_t span);
 
     bool toDictionaryMode(ExclusiveContext *cx);
 
   private:
     friend class Nursery;
 
     /*
      * Get internal pointers to the range of values starting at start and
@@ -585,20 +581,18 @@ class NativeObject : public JSObject
         return slot - numFixedSlots();
     }
 
     /*
      * Grow or shrink slots immediately before changing the slot span.
      * The number of allocated slots is not stored explicitly, and changes to
      * the slots must track changes in the slot span.
      */
-    static bool growSlots(ExclusiveContext *cx, HandleNativeObject obj, uint32_t oldCount,
-                          uint32_t newCount);
-    static void shrinkSlots(ExclusiveContext *cx, HandleNativeObject obj, uint32_t oldCount,
-                            uint32_t newCount);
+    bool growSlots(ExclusiveContext *cx, uint32_t oldCount, uint32_t newCount);
+    void shrinkSlots(ExclusiveContext *cx, uint32_t oldCount, uint32_t newCount);
 
     bool hasDynamicSlots() const { return !!slots_; }
 
     /* Compute dynamicSlotsCount() for this object. */
     uint32_t numDynamicSlots() const {
         return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
     }
 
@@ -786,18 +780,17 @@ class NativeObject : public JSObject
         getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
     }
 
     // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
     // size classes will give an object.
     static const uint32_t MAX_FIXED_SLOTS = 16;
 
   protected:
-    static inline bool updateSlotsForSpan(ExclusiveContext *cx,
-                                          HandleNativeObject obj, size_t oldSpan, size_t newSpan);
+    inline bool updateSlotsForSpan(ExclusiveContext *cx, size_t oldSpan, size_t newSpan);
 
   public:
     /*
      * Trigger the write barrier on a range of slots that will no longer be
      * reachable.
      */
     void prepareSlotRangeForOverwrite(size_t start, size_t end) {
         for (size_t i = start; i < end; i++)
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -329,17 +329,17 @@ JSObject::makeLazyGroup(JSContext *cx, H
 
     return group;
 }
 
 /* static */ bool
 JSObject::setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj)
 {
     ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj);
-    return obj->setFlag(cx, BaseShape::NEW_GROUP_UNKNOWN);
+    return obj->setFlags(cx, BaseShape::NEW_GROUP_UNKNOWN);
 }
 
 /////////////////////////////////////////////////////////////////////
 // ObjectGroupCompartment NewTable
 /////////////////////////////////////////////////////////////////////
 
 /*
  * Entries for the per-compartment set of groups which are the default
@@ -1052,18 +1052,17 @@ ObjectGroup::newPlainObject(JSContext *c
 
     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
     if (!obj) {
         cx->clearPendingException();
         return nullptr;
     }
     MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject());
 
-    RootedShape shape(cx, p->value().shape);
-    if (!NativeObject::setLastProperty(cx, obj, shape)) {
+    if (!obj->setLastProperty(cx, p->value().shape)) {
         cx->clearPendingException();
         return nullptr;
     }
 
     ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), properties, nproperties);
 
     for (size_t i = 0; i < nproperties; i++)
         obj->setSlot(i, properties[i].value);
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -382,39 +382,39 @@ NativeObject::getChildPropertyOnDictiona
 
     if (obj->inDictionaryMode()) {
         MOZ_ASSERT(parent == obj->lastProperty());
         RootedGeneric<StackShape*> childRoot(cx, &child);
         shape = childRoot->isAccessorShape() ? NewGCAccessorShape(cx) : NewGCShape(cx);
         if (!shape)
             return nullptr;
         if (childRoot->hasSlot() && childRoot->slot() >= obj->lastProperty()->base()->slotSpan()) {
-            if (!setSlotSpan(cx, obj, childRoot->slot() + 1))
+            if (!obj->setSlotSpan(cx, childRoot->slot() + 1))
                 return nullptr;
         }
         shape->initDictionaryShape(*childRoot, obj->numFixedSlots(), &obj->shape_);
     }
 
     return shape;
 }
 
 /* static */ Shape *
 NativeObject::getChildProperty(ExclusiveContext *cx,
                                HandleNativeObject obj, HandleShape parent, StackShape &unrootedChild)
 {
     RootedGeneric<StackShape*> child(cx, &unrootedChild);
-    RootedShape shape(cx, getChildPropertyOnDictionary(cx, obj, parent, *child));
+    Shape *shape = getChildPropertyOnDictionary(cx, obj, parent, *child);
 
     if (!obj->inDictionaryMode()) {
         shape = cx->compartment()->propertyTree.getChild(cx, parent, *child);
         if (!shape)
             return nullptr;
         //MOZ_ASSERT(shape->parent == parent);
         //MOZ_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
-        if (!setLastProperty(cx, obj, shape))
+        if (!obj->setLastProperty(cx, shape))
             return nullptr;
     }
 
     return shape;
 }
 
 bool
 js::NativeObject::toDictionaryMode(ExclusiveContext *cx)
@@ -1021,29 +1021,29 @@ NativeObject::removeProperty(ExclusiveCo
 
     self->checkShapeConsistency();
     return true;
 }
 
 /* static */ void
 NativeObject::clear(JSContext *cx, HandleNativeObject obj)
 {
-    RootedShape shape(cx, obj->lastProperty());
+    Shape *shape = obj->lastProperty();
     MOZ_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
 
     while (shape->parent) {
         shape = shape->parent;
         MOZ_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
     }
     MOZ_ASSERT(shape->isEmptyShape());
 
     if (obj->inDictionaryMode())
         shape->listp = &obj->shape_;
 
-    JS_ALWAYS_TRUE(setLastProperty(cx, obj, shape));
+    JS_ALWAYS_TRUE(obj->setLastProperty(cx, shape));
 
     ++cx->runtime()->propertyRemovals;
     obj->checkShapeConsistency();
 }
 
 /* static */ bool
 NativeObject::rollbackProperties(ExclusiveContext *cx, HandleNativeObject obj, uint32_t slotSpan)
 {
@@ -1200,41 +1200,41 @@ Shape::setObjectMetadata(JSContext *cx, 
     StackBaseShape base(last);
     base.metadata = metadata;
 
     RootedShape lastRoot(cx, last);
     return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
 bool
-JSObject::setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag_,
-                  GenerateShape generateShape)
+JSObject::setFlags(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flags_,
+                   GenerateShape generateShape)
 {
-    BaseShape::Flag flag = (BaseShape::Flag) flag_;
+    BaseShape::Flag flags = (BaseShape::Flag) flags_;
 
-    if (lastProperty()->getObjectFlags() & flag)
+    if ((lastProperty()->getObjectFlags() & flags) == flags)
         return true;
 
     RootedObject self(cx, this);
 
     if (isNative() && as<NativeObject>().inDictionaryMode()) {
         if (generateShape == GENERATE_SHAPE && !as<NativeObject>().generateOwnShape(cx))
             return false;
         StackBaseShape base(self->lastProperty());
-        base.flags |= flag;
+        base.flags |= flags;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
         self->lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
     Shape *newShape =
-        Shape::setObjectFlag(cx, flag, self->getTaggedProto(), self->lastProperty());
+        Shape::setObjectFlags(cx, flags, self->getTaggedProto(), self->lastProperty());
     if (!newShape)
         return false;
 
     self->shape_ = newShape;
     return true;
 }
 
 bool
@@ -1251,23 +1251,23 @@ NativeObject::clearFlag(ExclusiveContext
     if (!nbase)
         return false;
 
     self->lastProperty()->base()->adoptUnowned(nbase);
     return true;
 }
 
 /* static */ Shape *
-Shape::setObjectFlag(ExclusiveContext *cx, BaseShape::Flag flag, TaggedProto proto, Shape *last)
+Shape::setObjectFlags(ExclusiveContext *cx, BaseShape::Flag flags, TaggedProto proto, Shape *last)
 {
-    if (last->getObjectFlags() & flag)
+    if ((last->getObjectFlags() & flags) == flags)
         return last;
 
     StackBaseShape base(last);
-    base.flags |= flag;
+    base.flags |= flags;
 
     RootedShape lastRoot(cx, last);
     return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
 /* static */ inline HashNumber
 StackBaseShape::hash(const Lookup& lookup)
 {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -829,18 +829,18 @@ class Shape : public gc::TenuredCell
     }
     JSObject *getObjectParent() const { return base()->parent; }
     JSObject *getObjectMetadata() const { return base()->metadata; }
 
     static Shape *setObjectParent(ExclusiveContext *cx,
                                   JSObject *obj, TaggedProto proto, Shape *last);
     static Shape *setObjectMetadata(JSContext *cx,
                                     JSObject *metadata, TaggedProto proto, Shape *last);
-    static Shape *setObjectFlag(ExclusiveContext *cx,
-                                BaseShape::Flag flag, TaggedProto proto, Shape *last);
+    static Shape *setObjectFlags(ExclusiveContext *cx,
+                                 BaseShape::Flag flag, TaggedProto proto, Shape *last);
 
     uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
     bool hasObjectFlag(BaseShape::Flag flag) const {
         MOZ_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
         return !!(base()->flags & flag);
     }
 
   protected:
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -192,17 +192,17 @@ UnboxedPlainObject::convertToNative(JSCo
         if (!shape)
             return false;
     }
 
     if (!SetClassAndProto(cx, obj, &PlainObject::class_, proto))
         return false;
 
     RootedNativeObject nobj(cx, &obj->as<PlainObject>());
-    NativeObject::setLastPropertyMakeNative(cx, nobj, shape);
+    nobj->setLastPropertyMakeNative(cx, shape);
 
     for (size_t i = 0; i < values.length(); i++)
         nobj->initSlotUnchecked(i, values[i]);
 
     return true;
 }
 
 /* static */