Bug 1536439 - [Part 1] Removing Shaped Objects r=tcampbell
authorMatthew Gaudet <mgaudet@mozilla.com>
Thu, 28 Mar 2019 15:02:59 +0000
changeset 466591 3a5f3266614a8cfdbb606274d1d748907c9cc898
parent 466590 2c42dfe47d1a0cefcc31a4828477b2d016830282
child 466592 a2cba7a15840df9b48c83d498242714b64088328
push id112592
push userncsoregi@mozilla.com
push dateFri, 29 Mar 2019 05:30:21 +0000
treeherdermozilla-inbound@7404e19d2534 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1536439
milestone68.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 1536439 - [Part 1] Removing Shaped Objects r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D25072
js/src/builtin/TypedObject.h
js/src/gdb/mozilla/JSObject.py
js/src/jit/CacheIR.cpp
js/src/jit/CacheIRCompiler.cpp
js/src/jit/MacroAssembler-inl.h
js/src/jit/MacroAssembler.cpp
js/src/jit/TemplateObject-inl.h
js/src/shell/js.cpp
js/src/vm/JSObject-inl.h
js/src/vm/JSObject.cpp
js/src/vm/JSObject.h
js/src/vm/NativeObject.h
js/src/vm/ProxyObject.h
js/src/vm/ReceiverGuard-inl.h
js/src/vm/Shape.cpp
js/src/vm/ShapedObject-inl.h
js/src/vm/ShapedObject.h
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -9,17 +9,16 @@
 
 #include "mozilla/CheckedInt.h"
 
 #include "builtin/TypedObjectConstants.h"
 #include "gc/WeakMap.h"
 #include "js/Conversions.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/JSObject.h"
-#include "vm/ShapedObject.h"
 
 /*
  * -------------
  * [SMDOC] Typed Objects
  * -------------
  *
  * Typed objects are a special kind of JS object where the data is
  * given well-structured form. To use a typed object, users first
@@ -518,17 +517,17 @@ class TypedObjectModuleObject : public N
     WasmAnyRefDesc,
     SlotCount
   };
 
   static const Class class_;
 };
 
 /* Base type for transparent and opaque typed objects. */
-class TypedObject : public ShapedObject {
+class TypedObject : public JSObject {
   static const bool IsTypedObjectClass = true;
 
   static MOZ_MUST_USE bool obj_getArrayElement(JSContext* cx,
                                                Handle<TypedObject*> typedObj,
                                                Handle<TypeDescr*> typeDescr,
                                                uint32_t index,
                                                MutableHandleValue vp);
 
--- a/js/src/gdb/mozilla/JSObject.py
+++ b/js/src/gdb/mozilla/JSObject.py
@@ -45,17 +45,17 @@ class JSObjectPtrOrRef(prettyprinters.Po
         m = gdb_string_regexp.match(class_name)
         if m:
             class_name = m.group(1)
 
         if non_native:
             return '[object {}]'.format(class_name)
         else:
             native = self.value.cast(self.otc.NativeObject_ptr_t)
-            shape = native['shapeOrExpando_'].cast(self.otc.Shape_ptr_t)
+            shape = native['shape_'].cast(self.otc.Shape_ptr_t)
             baseshape = deref(shape['base_'])
             flags = baseshape['flags']
             is_delegate = bool(flags & self.otc.flag_DELEGATE)
             name = None
             if class_name == 'Function':
                 function = self.value
                 concrete_type = function.type.strip_typedefs()
                 if concrete_type.code == gdb.TYPE_CODE_REF:
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4394,19 +4394,18 @@ bool SetPropIRGenerator::tryAttachAddSlo
     return false;
   }
 
   ObjOperandId objId = writer.guardIsObject(objValId);
   maybeEmitIdGuard(id);
 
   // In addition to guarding for type barrier, we need this group guard (or
   // shape guard below) to ensure class is unchanged. This group guard may also
-  // implay maybeInterpretedFunction() for the special-case of function
+  // imply maybeInterpretedFunction() for the special-case of function
   // prototype property set.
-  MOZ_ASSERT(obj->is<ShapedObject>());
   writer.guardGroup(objId, oldGroup);
 
   // If we are adding a property to an object for which the new script
   // properties analysis hasn't been performed yet, make sure the stub fails
   // after we run the analysis as a group change may be required here. The
   // group change is not required for correctness but improves type
   // information elsewhere.
   AutoSweepObjectGroup sweep(oldGroup);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1667,17 +1667,17 @@ bool CacheIRCompiler::emitGuardIsExtensi
   Register obj = allocator.useRegister(masm, reader.objOperandId());
   AutoScratchRegister scratch(allocator, masm);
 
   FailurePath* failure;
   if (!addFailurePath(&failure)) {
     return false;
   }
 
-  Address shape(obj, ShapedObject::offsetOfShape());
+  Address shape(obj, JSObject::offsetOfShape());
   masm.loadPtr(shape, scratch);
 
   Address baseShape(scratch, Shape::offsetOfBaseShape());
   masm.loadPtr(baseShape, scratch);
 
   Address baseShapeFlags(scratch, BaseShape::offsetOfFlags());
   masm.loadPtr(baseShapeFlags, scratch);
 
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -464,56 +464,56 @@ void MacroAssembler::branchTestObjShape(
                                         Label* label) {
   MOZ_ASSERT(obj != scratch);
   MOZ_ASSERT(spectreRegToZero != scratch);
 
   if (JitOptions.spectreObjectMitigationsMisc) {
     move32(Imm32(0), scratch);
   }
 
-  branchPtr(cond, Address(obj, ShapedObject::offsetOfShape()), ImmGCPtr(shape),
+  branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape),
             label);
 
   if (JitOptions.spectreObjectMitigationsMisc) {
     spectreMovePtr(cond, scratch, spectreRegToZero);
   }
 }
 
 void MacroAssembler::branchTestObjShapeNoSpectreMitigations(Condition cond,
                                                             Register obj,
                                                             const Shape* shape,
                                                             Label* label) {
-  branchPtr(cond, Address(obj, ShapedObject::offsetOfShape()), ImmGCPtr(shape),
+  branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape),
             label);
 }
 
 void MacroAssembler::branchTestObjShape(Condition cond, Register obj,
                                         Register shape, Register scratch,
                                         Register spectreRegToZero,
                                         Label* label) {
   MOZ_ASSERT(obj != scratch);
   MOZ_ASSERT(obj != shape);
   MOZ_ASSERT(spectreRegToZero != scratch);
 
   if (JitOptions.spectreObjectMitigationsMisc) {
     move32(Imm32(0), scratch);
   }
 
-  branchPtr(cond, Address(obj, ShapedObject::offsetOfShape()), shape, label);
+  branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
 
   if (JitOptions.spectreObjectMitigationsMisc) {
     spectreMovePtr(cond, scratch, spectreRegToZero);
   }
 }
 
 void MacroAssembler::branchTestObjShapeNoSpectreMitigations(Condition cond,
                                                             Register obj,
                                                             Register shape,
                                                             Label* label) {
-  branchPtr(cond, Address(obj, ShapedObject::offsetOfShape()), shape, label);
+  branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
 }
 
 void MacroAssembler::branchTestObjShapeUnsafe(Condition cond, Register obj,
                                               Register shape, Label* label) {
   branchTestObjShapeNoSpectreMitigations(cond, obj, shape, label);
 }
 
 void MacroAssembler::branchTestObjGroup(Condition cond, Register obj,
@@ -803,25 +803,25 @@ void MacroAssembler::storeObjGroup(Objec
   emitPreBarrier(*this, groupAddr);
   storePtr(ImmGCPtr(group), groupAddr);
 }
 
 template <typename EmitPreBarrier>
 void MacroAssembler::storeObjShape(Register shape, Register obj,
                                    EmitPreBarrier emitPreBarrier) {
   MOZ_ASSERT(shape != obj);
-  Address shapeAddr(obj, ShapedObject::offsetOfShape());
+  Address shapeAddr(obj, JSObject::offsetOfShape());
   emitPreBarrier(*this, shapeAddr);
   storePtr(shape, shapeAddr);
 }
 
 template <typename EmitPreBarrier>
 void MacroAssembler::storeObjShape(Shape* shape, Register obj,
                                    EmitPreBarrier emitPreBarrier) {
-  Address shapeAddr(obj, ShapedObject::offsetOfShape());
+  Address shapeAddr(obj, JSObject::offsetOfShape());
   emitPreBarrier(*this, shapeAddr);
   storePtr(ImmGCPtr(shape), shapeAddr);
 }
 
 template <typename T>
 void MacroAssembler::storeObjectOrNull(Register src, const T& dest) {
   Label notNull, done;
   branchTestPtr(Assembler::NonZero, src, src, &notNull);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1095,17 +1095,17 @@ void MacroAssembler::initGCThing(Registe
                                  const TemplateObject& templateObj,
                                  bool initContents) {
   // Fast initialization of an empty object returned by allocateObject().
 
   storePtr(ImmGCPtr(templateObj.group()),
            Address(obj, JSObject::offsetOfGroup()));
 
   if (gc::Cell* shape = templateObj.maybeShape()) {
-    storePtr(ImmGCPtr(shape), Address(obj, ShapedObject::offsetOfShape()));
+    storePtr(ImmGCPtr(shape), Address(obj, JSObject::offsetOfShape()));
   }
 
   if (templateObj.isNative()) {
     const NativeTemplateObject& ntemplate =
         templateObj.asNativeTemplateObject();
     MOZ_ASSERT_IF(!ntemplate.denseElementsAreCopyOnWrite(),
                   !ntemplate.hasDynamicElements());
     MOZ_ASSERT_IF(ntemplate.convertDoubleElements(), ntemplate.isArrayObject());
@@ -3476,17 +3476,17 @@ void MacroAssembler::debugAssertIsObject
   bind(&ok);
 #endif
 }
 
 void MacroAssembler::debugAssertObjHasFixedSlots(Register obj,
                                                  Register scratch) {
 #ifdef DEBUG
   Label hasFixedSlots;
-  loadPtr(Address(obj, ShapedObject::offsetOfShape()), scratch);
+  loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
   branchTest32(Assembler::NonZero,
                Address(scratch, Shape::offsetOfImmutableFlags()),
                Imm32(Shape::fixedSlotsMask()), &hasFixedSlots);
   assumeUnreachable("Expected a fixed slot");
   bind(&hasFixedSlots);
 #endif
 }
 
--- a/js/src/jit/TemplateObject-inl.h
+++ b/js/src/jit/TemplateObject-inl.h
@@ -6,18 +6,16 @@
 
 #ifndef jit_TemplateObject_inl_h
 #define jit_TemplateObject_inl_h
 
 #include "jit/TemplateObject.h"
 
 #include "vm/RegExpObject.h"
 
-#include "vm/ShapedObject-inl.h"
-
 namespace js {
 namespace jit {
 
 inline gc::AllocKind TemplateObject::getAllocKind() const {
   return obj_->asTenured().getAllocKind();
 }
 
 inline bool TemplateObject::isNative() const { return obj_->isNative(); }
@@ -51,22 +49,19 @@ inline bool TemplateObject::isPlainObjec
 }
 
 inline gc::Cell* TemplateObject::group() const {
   MOZ_ASSERT(!obj_->hasLazyGroup());
   return obj_->group();
 }
 
 inline gc::Cell* TemplateObject::maybeShape() const {
-  if (obj_->is<ShapedObject>()) {
-    Shape* shape = obj_->maybeShape();
-    MOZ_ASSERT(!shape->inDictionary());
-    return shape;
-  }
-  return nullptr;
+  Shape* shape = obj_->maybeShape();
+  MOZ_ASSERT(!shape->inDictionary());
+  return shape;
 }
 
 inline uint32_t TemplateObject::getInlineTypedObjectSize() const {
   return obj_->as<InlineTypedObject>().size();
 }
 
 inline uint8_t* TemplateObject::getInlineTypedObjectMem(
     const JS::AutoRequireNoGC& nogc) const {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4154,23 +4154,17 @@ static bool UnwrappedObjectsHaveSameShap
   if (!args.get(0).isObject() || !args.get(1).isObject()) {
     JS_ReportErrorASCII(cx, "2 objects expected");
     return false;
   }
 
   RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
   RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
 
-  if (!obj1->is<ShapedObject>() || !obj2->is<ShapedObject>()) {
-    JS_ReportErrorASCII(cx, "object does not have a Shape");
-    return false;
-  }
-
-  args.rval().setBoolean(obj1->as<ShapedObject>().shape() ==
-                         obj2->as<ShapedObject>().shape());
+  args.rval().setBoolean(obj1->shape() == obj2->shape());
   return true;
 }
 
 static bool Sleep_fn(JSContext* cx, unsigned argc, Value* vp) {
   ShellContext* sc = GetShellContext(cx);
   CallArgs args = CallArgsFromVp(argc, vp);
 
   TimeDuration duration = TimeDuration::FromSeconds(0.0);
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -26,26 +26,19 @@
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
 
 #include "gc/Marking-inl.h"
 #include "gc/ObjectKind-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/ObjectOperations-inl.h"  // js::MaybeHasInterestingSymbolProperty
 #include "vm/Realm-inl.h"
-#include "vm/ShapedObject-inl.h"
 #include "vm/TypeInference-inl.h"
 
-inline js::Shape* JSObject::maybeShape() const {
-  if (!is<js::ShapedObject>()) {
-    return nullptr;
-  }
-
-  return as<js::ShapedObject>().shape();
-}
+inline js::Shape* JSObject::maybeShape() const { return shape(); }
 
 inline js::Shape* JSObject::ensureShape(JSContext* cx) {
   js::Shape* shape = maybeShape();
   MOZ_ASSERT(shape);
   return shape;
 }
 
 inline void JSObject::finalize(js::FreeOp* fop) {
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -4014,19 +4014,17 @@ JS::ubi::Node::Size JS::ubi::Concrete<JS
   return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
 }
 
 const char16_t JS::ubi::Concrete<JSObject>::concreteTypeName[] = u"JSObject";
 
 void JSObject::traceChildren(JSTracer* trc) {
   TraceEdge(trc, &group_, "group");
 
-  if (is<ShapedObject>()) {
-    as<ShapedObject>().traceShape(trc);
-  }
+  traceShape(trc);
 
   const Class* clasp = group_->clasp();
   if (clasp->isNative()) {
     NativeObject* nobj = &as<NativeObject>();
 
     {
       GetObjectSlotNameFunctor func(nobj);
       JS::AutoTracingDetails ctx(trc, func);
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -28,16 +28,20 @@ namespace js {
 using PropertyDescriptorVector = JS::GCVector<JS::PropertyDescriptor>;
 class GCMarker;
 class Nursery;
 
 namespace gc {
 class RelocationOverlay;
 }  // namespace gc
 
+namespace jit {
+class CacheIRCompiler;
+}
+
 /****************************************************************************/
 
 class GlobalObject;
 class NewObjectCache;
 
 enum class IntegrityLevel { Sealed, Frozen };
 
 // Forward declarations, required for later friend declarations.
@@ -58,41 +62,33 @@ bool SetImmutablePrototype(JSContext* cx
  * and execution semantics. The runtime class of an arbitrary JSObject is
  * identified by JSObject::getClass().
  *
  * The members common to all objects are as follows:
  *
  * - The |group_| member stores the group of the object, which contains its
  *   prototype object, its class and the possible types of its properties.
  *
- * - The |shapeOrExpando_| member points to (an optional) guard object that JIT
- *   may use to optimize. The pointed-to object dictates the constraints
- *   imposed on the JSObject:
- *      nullptr
- *          - Safe value if this field is not needed.
- *      js::Shape
- *          - All objects that might point |shapeOrExpando_| to a js::Shape
- *            must follow the rules specified on js::ShapedObject.
- *      JSObject
- *          - Implies nothing about the current object or target object. Either
- *            of which may mutate in place. Store a JSObject* only to save
- *            space, not to guard on.
+ * - The |shape_| member stores the current 'shape' of the object, which
+ *   describes the current layout and set of property keys of the object. The
+ *   |shape_| field must be non-null.
  *
- * NOTE: The JIT may check |shapeOrExpando_| pointer value without ever
- *       inspecting |group_| or the class.
+ * NOTE: shape()->getObjectClass() must equal getClass().
+ *
+ * NOTE: The JIT may check |shape_| pointer value without ever inspecting
+ *       |group_| or the class.
  *
  * NOTE: Some operations can change the contents of an object (including class)
  *       in-place so avoid assuming an object with same pointer has same class
- *       as before.
- *       - JSObject::swap()
+ *       as before. - JSObject::swap()
  */
 class JSObject : public js::gc::Cell {
  protected:
   js::GCPtrObjectGroup group_;
-  void* shapeOrExpando_;
+  void* shape_;
 
  private:
   friend class js::Shape;
   friend class js::GCMarker;
   friend class js::NewObjectCache;
   friend class js::Nursery;
   friend class js::gc::RelocationOverlay;
   friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj,
@@ -160,16 +156,34 @@ class JSObject : public js::gc::Cell {
   bool hasLazyGroup() const { return group_->lazy(); }
 
   JS::Compartment* compartment() const { return group_->compartment(); }
   JS::Compartment* maybeCompartment() const { return compartment(); }
 
   inline js::Shape* maybeShape() const;
   inline js::Shape* ensureShape(JSContext* cx);
 
+  void initShape(js::Shape* shape) {
+    // Note: JSObject::zone() uses the group and we require it to be
+    // initialized before the shape.
+    MOZ_ASSERT(zone() == shape->zone());
+    shapeRef().init(shape);
+  }
+  void setShape(js::Shape* shape) {
+    MOZ_ASSERT(zone() == shape->zone());
+    shapeRef() = shape;
+  }
+  js::Shape* shape() const { return shapeRef(); }
+
+  void traceShape(JSTracer* trc) { TraceEdge(trc, shapePtr(), "shape"); }
+
+  static JSObject* fromShapeFieldPointer(uintptr_t p) {
+    return reinterpret_cast<JSObject*>(p - JSObject::offsetOfShape());
+  }
+
   enum GenerateShape { GENERATE_NONE, GENERATE_SHAPE };
 
   static bool setFlags(JSContext* cx, JS::HandleObject obj,
                        js::BaseShape::Flag flags,
                        GenerateShape generateShape = GENERATE_NONE);
   inline bool hasAllFlags(js::BaseShape::Flag flags) const;
 
   // An object is a delegate if it is on another object's prototype or
@@ -550,26 +564,40 @@ class JSObject : public js::gc::Cell {
   void dump() const;
 #endif
 
   // Maximum size in bytes of a JSObject.
   static const size_t MAX_BYTE_SIZE =
       4 * sizeof(void*) + 16 * sizeof(JS::Value);
 
  protected:
+  // ShapedObjects treat the |shapeOrExpando_| field as a GCPtrShape to
+  // ensure barriers are called. Use these instead of accessing
+  // |shapeOrExpando_| directly.
+  MOZ_ALWAYS_INLINE const js::GCPtrShape& shapeRef() const {
+    return *reinterpret_cast<const js::GCPtrShape*>(&(this->shape_));
+  }
+  MOZ_ALWAYS_INLINE js::GCPtrShape& shapeRef() {
+    return *reinterpret_cast<js::GCPtrShape*>(&(this->shape_));
+  }
+
+  // Used for GC tracing and Shape::listp
+  MOZ_ALWAYS_INLINE js::GCPtrShape* shapePtr() {
+    return reinterpret_cast<js::GCPtrShape*>(&(this->shape_));
+  }
+
   // JIT Accessors.
   //
   // To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
   // to call the method below.
   friend class js::jit::MacroAssembler;
+  friend class js::jit::CacheIRCompiler;
 
   static constexpr size_t offsetOfGroup() { return offsetof(JSObject, group_); }
-  static constexpr size_t offsetOfShapeOrExpando() {
-    return offsetof(JSObject, shapeOrExpando_);
-  }
+  static constexpr size_t offsetOfShape() { return offsetof(JSObject, shape_); }
 
  private:
   JSObject() = delete;
   JSObject(const JSObject& other) = delete;
   void operator=(const JSObject& other) = delete;
 };
 
 template <>
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -16,17 +16,16 @@
 #include "NamespaceImports.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/JSObject.h"
 #include "vm/Shape.h"
-#include "vm/ShapedObject.h"
 #include "vm/StringType.h"
 
 namespace js {
 
 class Shape;
 class TenuringTracer;
 
 /*
@@ -454,17 +453,17 @@ enum class ShouldUpdateTypes { Update, D
  * either the shared emptyObjectElements and emptyObjectElementsShared
  * singletons, into the inline value array (the address of the third value, to
  * leave room for a ObjectElements header;in this case numFixedSlots() is zero)
  * or to a dynamically allocated array.
  *
  * Slots and elements may both be non-empty. The slots may be either names or
  * indexes; no indexed property will be in both the slots and elements.
  */
-class NativeObject : public ShapedObject {
+class NativeObject : public JSObject {
  protected:
   /* Slots for object properties. */
   js::HeapSlot* slots_;
 
   /* Slots for object dense elements. */
   js::HeapSlot* elements_;
 
   friend class ::JSObject;
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -3,32 +3,32 @@
  * 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 vm_ProxyObject_h
 #define vm_ProxyObject_h
 
 #include "js/Proxy.h"
-#include "vm/ShapedObject.h"
+#include "vm/JSObject.h"
 
 namespace js {
 
 /**
  * This is the base class for the various kinds of proxy objects.  It's never
  * instantiated.
  *
- * Proxy objects use ShapedObject::shape_ primarily to record flags.  Property
+ * Proxy objects use JSObject::shape_ primarily to record flags.  Property
  * information, &c. is all dynamically computed.
  *
  * There is no class_ member to force specialization of JSObject::is<T>().
  * The implementation in JSObject is incorrect for proxies since it doesn't
  * take account of the handler type.
  */
-class ProxyObject : public ShapedObject {
+class ProxyObject : public JSObject {
   // GetProxyDataLayout computes the address of this field.
   detail::ProxyDataLayout data;
 
   void static_asserts() {
     static_assert(sizeof(ProxyObject) == sizeof(JSObject_Slots0),
                   "proxy object size must match GC thing size");
     static_assert(offsetof(ProxyObject, data) == detail::ProxyDataOffset,
                   "proxy object layout must match shadow interface");
--- a/js/src/vm/ReceiverGuard-inl.h
+++ b/js/src/vm/ReceiverGuard-inl.h
@@ -6,29 +6,28 @@
 
 #ifndef vm_ReceiverGuard_inl_h
 #define vm_ReceiverGuard_inl_h
 
 #include "vm/ReceiverGuard.h"
 
 #include "builtin/TypedObject.h"
 #include "vm/JSObject.h"
-#include "vm/ShapedObject.h"
 
 namespace js {
 
 MOZ_ALWAYS_INLINE
 ReceiverGuard::ReceiverGuard(JSObject* obj) : group(nullptr), shape(nullptr) {
   if (!obj->isNative()) {
     if (obj->is<TypedObject>()) {
       group = obj->group();
       return;
     }
   }
-  shape = obj->as<ShapedObject>().shape();
+  shape = obj->as<JSObject>().shape();
 }
 
 MOZ_ALWAYS_INLINE
 ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
     : group(group), shape(shape) {
   if (group) {
     const Class* clasp = group->clasp();
     if (IsTypedObjectClass(clasp)) {
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1496,17 +1496,17 @@ bool JSObject::setFlags(JSContext* cx, H
   Shape* newShape =
       Shape::setObjectFlags(cx, flags, obj->taggedProto(), existingShape);
   if (!newShape) {
     return false;
   }
 
   // The success of the |JSObject::ensureShape| call above means that |obj|
   // can be assumed to have a shape.
-  obj->as<ShapedObject>().setShape(newShape);
+  obj->as<JSObject>().setShape(newShape);
 
   return true;
 }
 
 /* static */
 bool NativeObject::clearFlag(JSContext* cx, HandleNativeObject obj,
                              BaseShape::Flag flag) {
   MOZ_ASSERT(obj->lastProperty()->getObjectFlags() & flag);
@@ -1908,17 +1908,17 @@ void Shape::fixupDictionaryShapeAfterMov
   if (listpPointsIntoShape) {
     // listp points to the parent field of the next shape.
     Shape* next = Shape::fromParentFieldPointer(uintptr_t(listp));
     if (gc::IsForwarded(next)) {
       listp = &gc::Forwarded(next)->parent;
     }
   } else {
     // listp points to the shape_ field of an object.
-    JSObject* last = ShapedObject::fromShapeFieldPointer(uintptr_t(listp));
+    JSObject* last = JSObject::fromShapeFieldPointer(uintptr_t(listp));
     if (gc::IsForwarded(last)) {
       listp = gc::Forwarded(last)->as<NativeObject>().shapePtr();
     }
   }
 }
 
 void Shape::fixupShapeTreeAfterMovingGC() {
   if (kids.isNull()) {
deleted file mode 100644
--- a/js/src/vm/ShapedObject-inl.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: set ts=8 sts=2 et sw=2 tw=80:
- * 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 vm_ShapedObject_inl_h
-#define vm_ShapedObject_inl_h
-
-#include "vm/ShapedObject.h"
-
-#include "jsfriendapi.h"
-
-#include "js/Proxy.h"
-
-template <>
-inline bool JSObject::is<js::ShapedObject>() const {
-  return isNative() || is<js::ProxyObject>() || is<js::TypedObject>();
-}
-
-#endif /* vm_ShapedObject_inl_h */
deleted file mode 100644
--- a/js/src/vm/ShapedObject.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: set ts=8 sts=2 et sw=2 tw=80:
- * 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 vm_ShapedObject_h
-#define vm_ShapedObject_h
-
-#include "vm/JSObject.h"
-
-namespace js {
-
-namespace jit {
-class CacheIRCompiler;
-}
-
-/*
- * Shaped objects are a variant of JSObject that use a GCPtrShape for their
- * |shapeOrExpando_| field. All objects that point to a js::Shape as their
- * |shapeOrExpando_| field should use this as their subclass.
- *
- * NOTE: shape()->getObjectClass() must equal getClass().
- */
-class ShapedObject : public JSObject {
- protected:
-  // ShapedObjects treat the |shapeOrExpando_| field as a GCPtrShape to
-  // ensure barriers are called. Use these instead of accessing
-  // |shapeOrExpando_| directly.
-  MOZ_ALWAYS_INLINE const GCPtrShape& shapeRef() const {
-    return *reinterpret_cast<const GCPtrShape*>(&(this->shapeOrExpando_));
-  }
-  MOZ_ALWAYS_INLINE GCPtrShape& shapeRef() {
-    return *reinterpret_cast<GCPtrShape*>(&(this->shapeOrExpando_));
-  }
-
-  // Used for GC tracing and Shape::listp
-  MOZ_ALWAYS_INLINE GCPtrShape* shapePtr() {
-    return reinterpret_cast<GCPtrShape*>(&(this->shapeOrExpando_));
-  }
-
- public:
-  // Set the shape of an object. This pointer is valid for native objects and
-  // some non-native objects. After creating an object, the objects for which
-  // the shape pointer is invalid need to overwrite this pointer before a GC
-  // can occur.
-  void initShape(Shape* shape) {
-    // Note: JSObject::zone() uses the group and we require it to be
-    // initialized before the shape.
-    MOZ_ASSERT(zone() == shape->zone());
-    shapeRef().init(shape);
-  }
-  void setShape(Shape* shape) {
-    MOZ_ASSERT(zone() == shape->zone());
-    shapeRef() = shape;
-  }
-  Shape* shape() const { return shapeRef(); }
-
-  void traceShape(JSTracer* trc) { TraceEdge(trc, shapePtr(), "shape"); }
-
-  static JSObject* fromShapeFieldPointer(uintptr_t p) {
-    return reinterpret_cast<JSObject*>(p - ShapedObject::offsetOfShape());
-  }
-
- private:
-  // See JSObject::offsetOfGroup() comment.
-  friend class js::jit::MacroAssembler;
-
-  friend class js::jit::CacheIRCompiler;
-
-  static constexpr size_t offsetOfShape() {
-    static_assert(offsetOfShapeOrExpando() == offsetof(shadow::Object, shape),
-                  "shadow shape must match actual shape");
-    return offsetOfShapeOrExpando();
-  }
-};
-
-}  // namespace js
-
-#endif /* vm_ShapedObject_h */