Bug 1191117 - Remove RootedGeneric and replace with normal Rooted usage; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Tue, 04 Aug 2015 14:32:27 -0700
changeset 289971 3a580b48d1adca56f74b2a7491b468af3e70bee8
parent 289970 4b635fb0b9d43efb9034e6550ae2ad840de5e14c
child 289972 bd7d66caa7930c815db5deee4d8d7878ad68510a
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1191117
milestone43.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 1191117 - Remove RootedGeneric and replace with normal Rooted usage; r=jonco
js/src/NamespaceImports.h
js/src/jsapi.h
js/src/jsobj.cpp
js/src/jspropertytree.cpp
js/src/jspropertytree.h
js/src/jsscript.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/NativeObject.h
js/src/vm/Shape-inl.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/vm/UnboxedObject.cpp
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -36,17 +36,16 @@ typedef AutoVectorRooter<jsid> AutoIdVec
 typedef AutoVectorRooter<JSObject*> AutoObjectVector;
 typedef AutoVectorRooter<JSScript*> AutoVector;
 
 class AutoIdArray;
 
 template <typename T> class AutoVectorRooter;
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
-template<typename T> class RootedGeneric;
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
 class ObjectOpResult;
 } // namespace JS
 
@@ -86,17 +85,16 @@ typedef AutoVectorRooter<JSScript*> Auto
 using ValueVector = TraceableVector<JS::Value>;
 using IdVector = TraceableVector<jsid>;
 using ScriptVector = TraceableVector<JSScript*>;
 
 using JS::AutoIdArray;
 
 using JS::AutoHashMapRooter;
 using JS::AutoHashSetRooter;
-using JS::RootedGeneric;
 
 using JS::CallArgs;
 using JS::CallNonGenericMethod;
 using JS::CallReceiver;
 using JS::CompileOptions;
 using JS::IsAcceptableThis;
 using JS::NativeImpl;
 using JS::OwningCompileOptions;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -473,53 +473,16 @@ class JS_PUBLIC_API(CustomAutoRooter) : 
   protected:
     /* Supplied by derived class to trace roots. */
     virtual void trace(JSTracer* trc) = 0;
 
   private:
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-/*
- * RootedGeneric<T> allows a class to instantiate its own Rooted type by
- * including the method:
- *
- *    void trace(JSTracer* trc);
- *
- * The trace() method must trace all of the class's fields.
- */
-template <class T>
-class RootedGeneric : private CustomAutoRooter
-{
-  public:
-    template <typename CX>
-    explicit RootedGeneric(CX* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : CustomAutoRooter(cx)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    template <typename CX>
-    explicit RootedGeneric(CX* cx, const T& initial MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : CustomAutoRooter(cx), value(initial)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    operator const T&() const { return value; }
-    T operator->() const { return value; }
-
-  private:
-    virtual void trace(JSTracer* trc) { value->trace(trc); }
-
-    T value;
-
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 /* A handle to an array of rooted values. */
 class HandleValueArray
 {
     const size_t length_;
     const Value * const elements_;
 
     HandleValueArray(size_t len, const Value* elements) : length_(len), elements_(elements) {}
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -523,24 +523,23 @@ js::SetIntegrityLevel(JSContext* cx, Han
         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
         for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
             if (!shapes.append(&r.front()))
                 return false;
         }
         Reverse(shapes.begin(), shapes.end());
 
         for (Shape* shape : shapes) {
-            StackShape unrootedChild(shape);
-            RootedGeneric<StackShape*> child(cx, &unrootedChild);
-            child->attrs |= GetSealedOrFrozenAttributes(child->attrs, level);
-
-            if (!JSID_IS_EMPTY(child->propid) && level == IntegrityLevel::Frozen)
-                MarkTypePropertyNonWritable(cx, nobj, child->propid);
-
-            last = cx->compartment()->propertyTree.getChild(cx, last, *child);
+            Rooted<StackShape> child(cx, StackShape(shape));
+            child.setAttrs(child.attrs() | GetSealedOrFrozenAttributes(child.attrs(), level));
+
+            if (!JSID_IS_EMPTY(child.get().propid) && level == IntegrityLevel::Frozen)
+                MarkTypePropertyNonWritable(cx, nobj, child.get().propid);
+
+            last = cx->compartment()->propertyTree.getChild(cx, last, child);
             if (!last)
                 return false;
         }
 
         MOZ_ASSERT(nobj->lastProperty()->slotSpan() == last->slotSpan());
         JS_ALWAYS_TRUE(nobj->setLastProperty(cx, last));
 
         // Ordinarily ArraySetLength handles this, but we're going behind its back
@@ -1355,19 +1354,18 @@ InitializePropertiesFromCompatibleNative
         Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
         for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) {
             if (!shapes.append(&r.front()))
                 return false;
         }
         Reverse(shapes.begin(), shapes.end());
 
         for (Shape* shape : shapes) {
-            StackShape unrootedChild(shape);
-            RootedGeneric<StackShape*> child(cx, &unrootedChild);
-            shape = cx->compartment()->propertyTree.getChild(cx, shape, *child);
+            Rooted<StackShape> child(cx, StackShape(shape));
+            shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
             if (!shape)
                 return false;
         }
     }
     size_t span = shape->slotSpan();
     if (!dst->setLastProperty(cx, shape))
         return false;
     for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++)
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -121,17 +121,17 @@ Shape::removeChild(Shape* child)
         Shape* otherChild = r.front();
         MOZ_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
         kidp->setShape(otherChild);
         js_delete(hash);
     }
 }
 
 Shape*
-PropertyTree::getChild(ExclusiveContext* cx, Shape* parentArg, StackShape& unrootedChild)
+PropertyTree::getChild(ExclusiveContext* cx, Shape* parentArg, Handle<StackShape> child)
 {
     RootedShape parent(cx, parentArg);
     MOZ_ASSERT(parent);
 
     Shape* existingShape = nullptr;
 
     /*
      * The property tree has extremely low fan-out below its root in
@@ -139,20 +139,20 @@ PropertyTree::getChild(ExclusiveContext*
      * 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(unrootedChild))
+        if (kid->matches(child))
             existingShape = kid;
     } else if (kidp->isHash()) {
-        if (KidsHash::Ptr p = kidp->toHash()->lookup(unrootedChild))
+        if (KidsHash::Ptr p = kidp->toHash()->lookup(child))
             existingShape = *p;
     } else {
         /* If kidp->isNull(), we always insert. */
     }
 
     if (existingShape) {
         JS::Zone* zone = existingShape->zone();
         if (zone->needsIncrementalBarrier()) {
@@ -176,17 +176,17 @@ PropertyTree::getChild(ExclusiveContext*
         } else if (existingShape->isMarked(gc::GRAY)) {
             UnmarkGrayShapeRecursively(existingShape);
         }
     }
 
     if (existingShape)
         return existingShape;
 
-    Shape* shape = Shape::new_(cx, unrootedChild, parent->numFixedSlots());
+    Shape* shape = Shape::new_(cx, child, parent->numFixedSlots());
     if (!shape)
         return nullptr;
 
     if (!insertChild(cx, parent, shape))
         return nullptr;
 
     return shape;
 }
--- a/js/src/jspropertytree.h
+++ b/js/src/jspropertytree.h
@@ -91,14 +91,14 @@ class PropertyTree
 
     explicit PropertyTree(JSCompartment* comp)
         : compartment_(comp)
     {
     }
 
     JSCompartment* compartment() { return compartment_; }
 
-    Shape* getChild(ExclusiveContext* cx, Shape* parent, StackShape& child);
+    Shape* getChild(ExclusiveContext* cx, Shape* parent, Handle<StackShape> child);
 };
 
 } /* namespace js */
 
 #endif /* jspropertytree_h */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -174,17 +174,17 @@ Bindings::initWithTemporaryStorage(Exclu
 
         UnownedBaseShape* base = BaseShape::getUnowned(cx, stackBase);
         if (!base)
             return false;
 
         unsigned attrs = JSPROP_PERMANENT |
                          JSPROP_ENUMERATE |
                          (bi->kind() == Binding::CONSTANT ? JSPROP_READONLY : 0);
-        StackShape child(base, NameToId(bi->name()), slot, attrs, 0);
+        Rooted<StackShape> child(cx, StackShape(base, NameToId(bi->name()), slot, attrs, 0));
 
         shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
         if (!shape)
             return false;
 
         MOZ_ASSERT(slot < nslots);
         slot++;
     }
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -664,17 +664,17 @@ GlobalObject::addIntrinsicValue(JSContex
     if (!holder)
         return false;
 
     uint32_t slot = holder->slotSpan();
     RootedShape last(cx, holder->lastProperty());
     Rooted<UnownedBaseShape*> base(cx, last->base()->unowned());
 
     RootedId id(cx, NameToId(name));
-    StackShape child(base, id, slot, 0, 0);
+    Rooted<StackShape> child(cx, StackShape(base, id, slot, 0, 0));
     Shape* shape = cx->compartment()->propertyTree.getChild(cx, last, child);
     if (!shape)
         return false;
 
     if (!holder->setLastProperty(cx, shape))
         return false;
 
     holder->setSlot(shape->slot(), value);
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -655,19 +655,19 @@ class NativeObject : public JSObject
      * after calling object-parameter-free shape methods, avoiding coupling
      * logic across the object vs. shape module wall.
      */
     static bool allocSlot(ExclusiveContext* cx, HandleNativeObject obj, uint32_t* slotp);
     void freeSlot(uint32_t slot);
 
   private:
     static Shape* getChildPropertyOnDictionary(ExclusiveContext* cx, HandleNativeObject obj,
-                                               HandleShape parent, StackShape& child);
+                                               HandleShape parent, MutableHandle<StackShape> child);
     static Shape* getChildProperty(ExclusiveContext* cx, HandleNativeObject obj,
-                                   HandleShape parent, StackShape& child);
+                                   HandleShape parent, MutableHandle<StackShape> child);
 
   public:
     /* Add a property whose id is not yet in this scope. */
     static Shape* addProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
                               JSGetterOp getter, JSSetterOp setter,
                               uint32_t slot, unsigned attrs, unsigned flags,
                               bool allowDictionary = true);
 
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -74,31 +74,30 @@ Shape::search(ExclusiveContext* cx, Shap
         if (shape->propidRef() == id)
             return shape;
     }
 
     return nullptr;
 }
 
 inline Shape*
-Shape::new_(ExclusiveContext* cx, StackShape& unrootedOther, uint32_t nfixed)
+Shape::new_(ExclusiveContext* cx, Handle<StackShape> other, uint32_t nfixed)
 {
-    RootedGeneric<StackShape*> other(cx, &unrootedOther);
-    Shape* shape = other->isAccessorShape()
+    Shape* shape = other.isAccessorShape()
                    ? js::Allocate<AccessorShape>(cx)
                    : js::Allocate<Shape>(cx);
     if (!shape) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    if (other->isAccessorShape())
-        new (shape) AccessorShape(*other, nfixed);
+    if (other.isAccessorShape())
+        new (shape) AccessorShape(other, nfixed);
     else
-        new (shape) Shape(*other, nfixed);
+        new (shape) Shape(other, nfixed);
 
     return shape;
 }
 
 template<class ObjectSubclass>
 /* static */ inline bool
 EmptyShape::ensureInitialCustomShape(ExclusiveContext* cx, Handle<ObjectSubclass*> obj)
 {
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -330,30 +330,30 @@ Shape::replaceLastProperty(ExclusiveCont
         return EmptyShape::getInitialShape(cx, base.clasp, proto, kind,
                                            base.flags & BaseShape::OBJECT_FLAG_MASK);
     }
 
     UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return nullptr;
 
-    StackShape child(shape);
-    child.base = nbase;
+    Rooted<StackShape> child(cx, StackShape(shape));
+    child.setBase(nbase);
 
     return cx->compartment()->propertyTree.getChild(cx, shape->parent, 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.
  */
 /* static */ Shape*
 NativeObject::getChildPropertyOnDictionary(ExclusiveContext* cx, HandleNativeObject obj,
-                                           HandleShape parent, StackShape& child)
+                                           HandleShape parent, MutableHandle<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());
@@ -380,39 +380,38 @@ NativeObject::getChildPropertyOnDictiona
                         child.slot() == JSSLOT_FREE(obj->getClass())));
         }
     }
 
     RootedShape shape(cx);
 
     if (obj->inDictionaryMode()) {
         MOZ_ASSERT(parent == obj->lastProperty());
-        RootedGeneric<StackShape*> childRoot(cx, &child);
-        shape = childRoot->isAccessorShape() ? Allocate<AccessorShape>(cx) : Allocate<Shape>(cx);
+        shape = child.isAccessorShape() ? Allocate<AccessorShape>(cx) : Allocate<Shape>(cx);
         if (!shape)
             return nullptr;
-        if (childRoot->hasSlot() && childRoot->slot() >= obj->lastProperty()->base()->slotSpan()) {
-            if (!obj->setSlotSpan(cx, childRoot->slot() + 1))
+        if (child.hasSlot() && child.slot() >= obj->lastProperty()->base()->slotSpan()) {
+            if (!obj->setSlotSpan(cx, child.slot() + 1))
                 return nullptr;
         }
-        shape->initDictionaryShape(*childRoot, obj->numFixedSlots(), &obj->shape_);
+        shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
     }
 
     return shape;
 }
 
 /* static */ Shape*
 NativeObject::getChildProperty(ExclusiveContext* cx,
-                               HandleNativeObject obj, HandleShape parent, StackShape& unrootedChild)
+                               HandleNativeObject obj, HandleShape parent,
+                               MutableHandle<StackShape> child)
 {
-    RootedGeneric<StackShape*> child(cx, &unrootedChild);
-    Shape* shape = getChildPropertyOnDictionary(cx, obj, parent, *child);
+    Shape* shape = getChildPropertyOnDictionary(cx, obj, parent, child);
 
     if (!obj->inDictionaryMode()) {
-        shape = cx->compartment()->propertyTree.getChild(cx, parent, *child);
+        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 (!obj->setLastProperty(cx, shape))
             return nullptr;
     }
 
@@ -572,19 +571,19 @@ NativeObject::addPropertyInternal(Exclus
         } else {
             StackBaseShape base(last->base());
             base.flags |= BaseShape::INDEXED;
             nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return nullptr;
         }
 
-        StackShape child(nbase, id, slot, attrs, flags);
+        Rooted<StackShape> child(cx, StackShape(nbase, id, slot, attrs, flags));
         child.updateGetterSetter(getter, setter);
-        shape = getChildProperty(cx, obj, last, child);
+        shape = getChildProperty(cx, obj, last, &child);
     }
 
     if (shape) {
         MOZ_ASSERT(shape == obj->lastProperty());
 
         if (table) {
             /* Store the tree node pointer in the table entry for id. */
             entry->setPreservingCollision(shape);
@@ -638,17 +637,17 @@ js::ReshapeForAllocKind(JSContext* cx, S
         if (indexed) {
             StackBaseShape base(nbase);
             base.flags |= BaseShape::INDEXED;
             nbase = BaseShape::getUnowned(cx, base);
             if (!nbase)
                 return nullptr;
         }
 
-        StackShape child(nbase, id, i, JSPROP_ENUMERATE, 0);
+        Rooted<StackShape> child(cx, StackShape(nbase, id, i, JSPROP_ENUMERATE, 0));
         newShape = cx->compartment()->propertyTree.getChild(cx, newShape, child);
         if (!newShape)
             return nullptr;
     }
 
     return newShape;
 }
 
@@ -831,20 +830,20 @@ NativeObject::putProperty(ExclusiveConte
 
         UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return nullptr;
 
         MOZ_ASSERT(shape == obj->lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
-        StackShape child(nbase, id, slot, attrs, flags);
+        Rooted<StackShape> child(cx, StackShape(nbase, id, slot, attrs, flags));
         child.updateGetterSetter(getter, setter);
         RootedShape parent(cx, shape->parent);
-        Shape* newShape = getChildProperty(cx, obj, parent, child);
+        Shape* newShape = getChildProperty(cx, obj, parent, &child);
 
         if (!newShape) {
             obj->checkShapeConsistency();
             return nullptr;
         }
 
         shape = newShape;
     }
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -730,17 +730,17 @@ class Shape : public gc::TenuredCell
 
     /* Used by EmptyShape (see jsscopeinlines.h). */
     inline Shape(UnownedBaseShape* base, uint32_t nfixed);
 
     /* Copy constructor disabled, to avoid misuse of the above form. */
     Shape(const Shape& other) = delete;
 
     /* Allocate a new shape based on the given StackShape. */
-    static inline Shape* new_(ExclusiveContext* cx, StackShape& unrootedOther, uint32_t nfixed);
+    static inline Shape* new_(ExclusiveContext* cx, Handle<StackShape> other, uint32_t nfixed);
 
     /*
      * Whether this shape has a valid slot value. This may be true even if
      * !hasSlot() (see SlotInfo comment above), and may be false even if
      * hasSlot() if the shape is being constructed and has not had a slot
      * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
      */
     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
@@ -1093,17 +1093,17 @@ struct InitialShapeEntry
 
     static inline HashNumber hash(const Lookup& lookup);
     static inline bool match(const InitialShapeEntry& key, const Lookup& lookup);
     static void rekey(InitialShapeEntry& k, const InitialShapeEntry& newKey) { k = newKey; }
 };
 
 typedef HashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy> InitialShapeSet;
 
-struct StackShape
+struct StackShape : public JS::Traceable
 {
     /* For performance, StackShape only roots when absolutely necessary. */
     UnownedBaseShape* base;
     jsid propid;
     GetterOp rawGetter;
     SetterOp rawSetter;
     uint32_t slot_;
     uint8_t attrs;
@@ -1172,20 +1172,61 @@ struct StackShape
         hash = mozilla::RotateLeft(hash, 4) ^ attrs;
         hash = mozilla::RotateLeft(hash, 4) ^ slot_;
         hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid);
         hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawGetter);
         hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawSetter);
         return hash;
     }
 
-    // For RootedGeneric<StackShape*>
+    // Traceable implementation.
+    static void trace(StackShape* stackShape, JSTracer* trc) { stackShape->trace(trc); }
     void trace(JSTracer* trc);
 };
 
+template <typename Outer>
+class StackShapeOperations {
+    const StackShape& ss() const { return static_cast<const Outer*>(this)->get(); }
+
+  public:
+    bool hasSlot() const { return ss().hasSlot(); }
+    bool hasMissingSlot() const { return ss().hasMissingSlot(); }
+    uint32_t slot() const { return ss().slot(); }
+    uint32_t maybeSlot() const { return ss().maybeSlot(); }
+    uint32_t slotSpan() const { return ss().slotSpan(); }
+    bool isAccessorShape() const { return ss().isAccessorShape(); }
+    uint8_t attrs() const { return ss().attrs; }
+};
+
+template <typename Outer>
+class MutableStackShapeOperations : public StackShapeOperations<Outer> {
+    StackShape& ss() { return static_cast<Outer*>(this)->get(); }
+
+  public:
+    void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
+        ss().updateGetterSetter(rawGetter, rawSetter);
+    }
+    void setSlot(uint32_t slot) { ss().setSlot(slot); }
+    void setBase(UnownedBaseShape* base) { ss().base = base; }
+    void setAttrs(uint8_t attrs) { ss().attrs = attrs; }
+};
+
+template <>
+class RootedBase<StackShape> : public MutableStackShapeOperations<JS::Rooted<StackShape>>
+{};
+
+template <>
+class HandleBase<StackShape> : public StackShapeOperations<JS::Handle<StackShape>>
+{};
+
+template <>
+class MutableHandleBase<StackShape>
+  : public MutableStackShapeOperations<JS::MutableHandle<StackShape>>
+{};
+
 inline
 Shape::Shape(const StackShape& other, uint32_t nfixed)
   : base_(other.base),
     propid_(other.propid),
     slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(other.attrs),
     flags(other.flags),
     parent(nullptr)
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -491,20 +491,19 @@ UnboxedLayout::makeNativeGroup(JSContext
         return false;
 
     MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0);
 
     // Add shapes for each property, if this is for a plain object.
     for (size_t i = 0; i < layout.properties().length(); i++) {
         const UnboxedLayout::Property& property = layout.properties()[i];
 
-        StackShape unrootedChild(shape->base()->unowned(), NameToId(property.name), i,
-                                 JSPROP_ENUMERATE, 0);
-        RootedGeneric<StackShape*> child(cx, &unrootedChild);
-        shape = cx->compartment()->propertyTree.getChild(cx, shape, *child);
+        Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name),
+                                                i, JSPROP_ENUMERATE, 0));
+        shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
         if (!shape)
             return false;
     }
 
     ObjectGroup* nativeGroup =
         ObjectGroupCompartment::makeGroup(cx, clasp, proto,
                                           group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
     if (!nativeGroup)