Bug 1073842 - Add NativeObject subclass of JSObject, remove ObjectImpl, r=luke.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 02 Oct 2014 19:32:39 -0700
changeset 208503 2095011ec061a02a94c47c881f6a2fc83bddbcd6
parent 208502 1adbc85fcfceea47103d831328d437d835a62867
child 208504 7151ebc290753ea29fa7ba53988d22e0d11ae61d
push id49942
push userbhackett@mozilla.com
push dateFri, 03 Oct 2014 02:32:52 +0000
treeherdermozilla-inbound@2095011ec061 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1073842
milestone35.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 1073842 - Add NativeObject subclass of JSObject, remove ObjectImpl, r=luke.
js/public/RootingAPI.h
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/MapObject.h
js/src/builtin/RegExp.cpp
js/src/builtin/SymbolObject.h
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/WeakSetObject.cpp
js/src/builtin/WeakSetObject.h
js/src/ctypes/CTypes.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/gc/Barrier.cpp
js/src/gc/Barrier.h
js/src/gc/ForkJoinNursery.cpp
js/src/gc/Heap.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/gc/Rooting.h
js/src/gc/StoreBuffer.cpp
js/src/gc/Tracer.cpp
js/src/gc/Tracer.h
js/src/gc/Verifier.cpp
js/src/gc/Zone.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineInspector.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/ParallelFunctions.cpp
js/src/jit/ParallelFunctions.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jit/Recover.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
js/src/jsapi-tests/testLookup.cpp
js/src/jsapi-tests/testResolveRecursion.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsbool.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdate.cpp
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsonparser.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jswatchpoint.cpp
js/src/perf/jsperf.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/ScriptedIndirectProxyHandler.cpp
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/ArgumentsObject.h
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
js/src/vm/ArrayObject-inl.h
js/src/vm/ArrayObject.h
js/src/vm/BooleanObject.h
js/src/vm/DateObject.h
js/src/vm/Debugger-inl.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/DebuggerMemory.cpp
js/src/vm/DebuggerMemory.h
js/src/vm/ErrorObject.cpp
js/src/vm/ErrorObject.h
js/src/vm/GeneratorObject.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/NumberObject.h
js/src/vm/ObjectImpl-inl.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/PIC.cpp
js/src/vm/PIC.h
js/src/vm/ProxyObject.cpp
js/src/vm/ProxyObject.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
js/src/vm/RegExpStaticsObject.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
js/src/vm/ScopeObject-inl.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/Shape-inl.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedTypedArrayObject.cpp
js/src/vm/SharedTypedArrayObject.h
js/src/vm/Stack.cpp
js/src/vm/StringObject-inl.h
js/src/vm/StringObject.h
js/src/vm/TypedArrayCommon.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
js/src/vm/UbiNode.cpp
js/src/vm/WeakMapObject.h
js/src/vm/WrapperObject.h
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -880,16 +880,34 @@ namespace js {
 template <>
 class RootedBase<JSObject*>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
+/*
+ * Augment the generic Handle<T> interface when T = JSObject* with
+ * downcasting operations.
+ *
+ * Given a Handle<JSObject*> obj, one can view
+ *   Handle<StringObject*> h = obj.as<StringObject*>();
+ * as an optimization of
+ *   Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
+ *   Handle<StringObject*> h = rooted;
+ */
+template <>
+class HandleBase<JSObject*>
+{
+  public:
+    template <class U>
+    JS::Handle<U*> as() const;
+};
+
 /* Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
 class FakeRooted : public RootedBase<T>
 {
   public:
     template <typename CX>
     FakeRooted(CX *cx
                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -998,32 +1016,42 @@ template <typename T> class MaybeRooted<
 
     static inline JS::Handle<T> toHandle(HandleType v) {
         return v;
     }
 
     static inline JS::MutableHandle<T> toMutableHandle(MutableHandleType v) {
         return v;
     }
+
+    template <typename T2>
+    static inline JS::Handle<T2*> downcastHandle(HandleType v) {
+        return v.template as<T2>();
+    }
 };
 
 template <typename T> class MaybeRooted<T, NoGC>
 {
   public:
     typedef T HandleType;
     typedef FakeRooted<T> RootType;
     typedef FakeMutableHandle<T> MutableHandleType;
 
     static JS::Handle<T> toHandle(HandleType v) {
         MOZ_CRASH("Bad conversion");
     }
 
     static JS::MutableHandle<T> toMutableHandle(MutableHandleType v) {
         MOZ_CRASH("Bad conversion");
     }
+
+    template <typename T2>
+    static inline T2* downcastHandle(HandleType v) {
+        return &v->template as<T2>();
+    }
 };
 
 } /* namespace js */
 
 namespace JS {
 
 template <typename T> template <typename S>
 inline
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -39,16 +39,17 @@
 #endif
 #include "vm/ArrayBufferObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/ArrayBufferObject-inl.h"
+#include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsNaN;
 using mozilla::PodZero;
 
 static bool
@@ -112,17 +113,17 @@ HasPureCoercion(JSContext *cx, HandleVal
     // breaking all the code that contains this bug, we make an exception for
     // functions that don't have user-defined valueOf or toString, for their
     // coercions are not observable and coercion via ToNumber/ToInt32
     // definitely produces NaN/0. We should remove this special case later once
     // most apps have been built with newer Emscripten.
     jsid toString = NameToId(cx->names().toString);
     if (v.toObject().is<JSFunction>() &&
         HasObjectValueOf(&v.toObject(), cx) &&
-        ClassMethodIsNative(cx, &v.toObject(), &JSFunction::class_, toString, fun_toString))
+        ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
     {
         return true;
     }
 
     return false;
 }
 
 static bool
@@ -892,17 +893,17 @@ CreateExportObject(JSContext *cx, Handle
 
     if (module.numExportedFunctions() == 1) {
         const AsmJSModule::ExportedFunction &func = module.exportedFunction(0);
         if (!func.maybeFieldName())
             return NewExportedFunction(cx, func, moduleObj, 0);
     }
 
     gc::AllocKind allocKind = gc::GetGCObjectKind(module.numExportedFunctions());
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind));
+    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_, allocKind));
     if (!obj)
         return nullptr;
 
     for (unsigned i = 0; i < module.numExportedFunctions(); i++) {
         const AsmJSModule::ExportedFunction &func = module.exportedFunction(i);
 
         RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i));
         if (!fun)
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -863,19 +863,20 @@ const Class AsmJSModuleObject::class_ = 
 };
 
 AsmJSModuleObject *
 AsmJSModuleObject::create(ExclusiveContext *cx, ScopedJSDeletePtr<AsmJSModule> *module)
 {
     JSObject *obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr, nullptr);
     if (!obj)
         return nullptr;
+    AsmJSModuleObject *nobj = &obj->as<AsmJSModuleObject>();
 
-    obj->setReservedSlot(MODULE_SLOT, PrivateValue(module->forget()));
-    return &obj->as<AsmJSModuleObject>();
+    nobj->setReservedSlot(MODULE_SLOT, PrivateValue(module->forget()));
+    return nobj;
 }
 
 AsmJSModule &
 AsmJSModuleObject::module() const
 {
     MOZ_ASSERT(is<AsmJSModuleObject>());
     return *(AsmJSModule *)getReservedSlot(MODULE_SLOT).toPrivate();
 }
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -1432,17 +1432,17 @@ LookupAsmJSModuleInCache(ExclusiveContex
                          AsmJSParser &parser,
                          ScopedJSDeletePtr<AsmJSModule> *module,
                          ScopedJSFreePtr<char> *compilationTimeReport);
 
 // An AsmJSModuleObject is an internal implementation object (i.e., not exposed
 // directly to user script) which manages the lifetime of an AsmJSModule. A
 // JSObject is necessary since we want LinkAsmJS/CallAsmJS JSFunctions to be
 // able to point to their module via their extended slots.
-class AsmJSModuleObject : public JSObject
+class AsmJSModuleObject : public NativeObject
 {
     static const unsigned MODULE_SLOT = 0;
 
   public:
     static const unsigned RESERVED_SLOTS = 1;
 
     // On success, return an AsmJSModuleClass JSObject that has taken ownership
     // (and release()ed) the given module.
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -628,17 +628,17 @@ Collator(JSContext *cx, CallArgs args, b
         // 10.1.3.1 paragraph 2
         RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx));
         if (!proto)
             return false;
         obj = NewObjectWithGivenProto(cx, &CollatorClass, proto, cx->global());
         if (!obj)
             return false;
 
-        obj->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
+        obj->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
     }
 
     // 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2
     RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
     RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
 
     // 10.1.2.1 step 6; 10.1.3.1 step 3
     if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options))
@@ -664,17 +664,17 @@ js::intl_Collator(JSContext *cx, unsigne
     // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
     // be used with "new", but it still has to be treated as a constructor.
     return Collator(cx, args, true);
 }
 
 static void
 collator_finalize(FreeOp *fop, JSObject *obj)
 {
-    UCollator *coll = static_cast<UCollator*>(obj->getReservedSlot(UCOLLATOR_SLOT).toPrivate());
+    UCollator *coll = static_cast<UCollator*>(obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate());
     if (coll)
         ucol_close(coll);
 }
 
 static JSObject *
 InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0));
@@ -727,17 +727,17 @@ InitCollatorClass(JSContext *cx, HandleO
     }
 
     return ctor;
 }
 
 bool
 GlobalObject::initCollatorProto(JSContext *cx, Handle<GlobalObject*> global)
 {
-    RootedObject proto(cx, global->createBlankPrototype(cx, &CollatorClass));
+    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &CollatorClass));
     if (!proto)
         return false;
     proto->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
     global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*proto));
     return true;
 }
 
 bool
@@ -1001,22 +1001,22 @@ js::intl_CompareStrings(JSContext *cx, u
 
     RootedObject collator(cx, &args[0].toObject());
 
     // Obtain a UCollator object, cached if possible.
     // XXX Does this handle Collator instances from other globals correctly?
     bool isCollatorInstance = collator->getClass() == &CollatorClass;
     UCollator *coll;
     if (isCollatorInstance) {
-        coll = static_cast<UCollator *>(collator->getReservedSlot(UCOLLATOR_SLOT).toPrivate());
+        coll = static_cast<UCollator *>(collator->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate());
         if (!coll) {
             coll = NewUCollator(cx, collator);
             if (!coll)
                 return false;
-            collator->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(coll));
+            collator->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(coll));
         }
     } else {
         // There's no good place to cache the ICU collator for an object
         // that has been initialized as a Collator but is not a Collator
         // instance. One possibility might be to add a Collator instance as an
         // internal property to each such object.
         coll = NewUCollator(cx, collator);
         if (!coll)
@@ -1117,17 +1117,17 @@ NumberFormat(JSContext *cx, CallArgs arg
         // 11.1.3.1 paragraph 2
         RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx));
         if (!proto)
             return false;
         obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto, cx->global());
         if (!obj)
             return false;
 
-        obj->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
+        obj->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
     }
 
     // 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2
     RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
     RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
 
     // 11.1.2.1 step 6; 11.1.3.1 step 3
     if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options))
@@ -1155,17 +1155,17 @@ js::intl_NumberFormat(JSContext *cx, uns
     // constructor.
     return NumberFormat(cx, args, true);
 }
 
 static void
 numberFormat_finalize(FreeOp *fop, JSObject *obj)
 {
     UNumberFormat *nf =
-        static_cast<UNumberFormat*>(obj->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
+        static_cast<UNumberFormat*>(obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
     if (nf)
         unum_close(nf);
 }
 
 static JSObject *
 InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0));
@@ -1218,17 +1218,17 @@ InitNumberFormatClass(JSContext *cx, Han
     }
 
     return ctor;
 }
 
 bool
 GlobalObject::initNumberFormatProto(JSContext *cx, Handle<GlobalObject*> global)
 {
-    RootedObject proto(cx, global->createBlankPrototype(cx, &NumberFormatClass));
+    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &NumberFormatClass));
     if (!proto)
         return false;
     proto->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
     global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*proto));
     return true;
 }
 
 bool
@@ -1460,22 +1460,22 @@ js::intl_FormatNumber(JSContext *cx, uns
     MOZ_ASSERT(args[1].isNumber());
 
     RootedObject numberFormat(cx, &args[0].toObject());
 
     // Obtain a UNumberFormat object, cached if possible.
     bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatClass;
     UNumberFormat *nf;
     if (isNumberFormatInstance) {
-        nf = static_cast<UNumberFormat*>(numberFormat->getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
+        nf = static_cast<UNumberFormat*>(numberFormat->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
         if (!nf) {
             nf = NewUNumberFormat(cx, numberFormat);
             if (!nf)
                 return false;
-            numberFormat->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nf));
+            numberFormat->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nf));
         }
     } else {
         // There's no good place to cache the ICU number format for an object
         // that has been initialized as a NumberFormat but is not a
         // NumberFormat instance. One possibility might be to add a
         // NumberFormat instance as an internal property to each such object.
         nf = NewUNumberFormat(cx, numberFormat);
         if (!nf)
@@ -1574,17 +1574,17 @@ DateTimeFormat(JSContext *cx, CallArgs a
         // 12.1.3.1 paragraph 2
         RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx));
         if (!proto)
             return false;
         obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto, cx->global());
         if (!obj)
             return false;
 
-        obj->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
+        obj->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
     }
 
     // 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2
     RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
     RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
 
     // 12.1.2.1 step 6; 12.1.3.1 step 3
     if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
@@ -1611,17 +1611,17 @@ js::intl_DateTimeFormat(JSContext *cx, u
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return DateTimeFormat(cx, args, true);
 }
 
 static void
 dateTimeFormat_finalize(FreeOp *fop, JSObject *obj)
 {
-    UDateFormat *df = static_cast<UDateFormat*>(obj->getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
+    UDateFormat *df = static_cast<UDateFormat*>(obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
     if (df)
         udat_close(df);
 }
 
 static JSObject *
 InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0));
@@ -1674,17 +1674,17 @@ InitDateTimeFormatClass(JSContext *cx, H
     }
 
     return ctor;
 }
 
 bool
 GlobalObject::initDateTimeFormatProto(JSContext *cx, Handle<GlobalObject*> global)
 {
-    RootedObject proto(cx, global->createBlankPrototype(cx, &DateTimeFormatClass));
+    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &DateTimeFormatClass));
     if (!proto)
         return false;
     proto->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
     global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*proto));
     return true;
 }
 
 bool
@@ -1961,22 +1961,22 @@ js::intl_FormatDateTime(JSContext *cx, u
     MOZ_ASSERT(args[1].isNumber());
 
     RootedObject dateTimeFormat(cx, &args[0].toObject());
 
     // Obtain a UDateFormat object, cached if possible.
     bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatClass;
     UDateFormat *df;
     if (isDateTimeFormatInstance) {
-        df = static_cast<UDateFormat*>(dateTimeFormat->getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
+        df = static_cast<UDateFormat*>(dateTimeFormat->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
         if (!df) {
             df = NewUDateFormat(cx, dateTimeFormat);
             if (!df)
                 return false;
-            dateTimeFormat->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(df));
+            dateTimeFormat->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(df));
         }
     } else {
         // There's no good place to cache the ICU date-time format for an object
         // that has been initialized as a DateTimeFormat but is not a
         // DateTimeFormat instance. One possibility might be to add a
         // DateTimeFormat instance as an internal property to each such object.
         df = NewUDateFormat(cx, dateTimeFormat);
         if (!df)
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -14,16 +14,18 @@
 
 #include "gc/Marking.h"
 #include "js/Utility.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
+
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::Forward;
 using mozilla::IsNaN;
 using mozilla::Move;
 using mozilla::NumberEqualsInt32;
 
@@ -835,17 +837,17 @@ HashableValue::mark(JSTracer *trc) const
     return hv;
 }
 
 
 /*** MapIterator *********************************************************************************/
 
 namespace {
 
-class MapIteratorObject : public JSObject
+class MapIteratorObject : public NativeObject
 {
   public:
     static const Class class_;
 
     enum { TargetSlot, KindSlot, RangeSlot, SlotCount };
     static const JSFunctionSpec methods[];
     static MapIteratorObject *create(JSContext *cx, HandleObject mapobj, ValueMap *data,
                                      MapObject::IteratorKind kind);
@@ -896,18 +898,18 @@ MapIteratorObject::kind() const
 }
 
 bool
 GlobalObject::initMapIteratorProto(JSContext *cx, Handle<GlobalObject *> global)
 {
     JSObject *base = GlobalObject::getOrCreateIteratorPrototype(cx, global);
     if (!base)
         return false;
-    Rooted<JSObject*> proto(cx,
-        NewObjectWithGivenProto(cx, &MapIteratorObject::class_, base, global));
+    RootedNativeObject proto(cx,
+        NewNativeObjectWithGivenProto(cx, &MapIteratorObject::class_, base, global));
     if (!proto)
         return false;
     proto->setSlot(MapIteratorObject::RangeSlot, PrivateValue(nullptr));
     if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods))
         return false;
     global->setReservedSlot(MAP_ITERATOR_PROTO, ObjectValue(*proto));
     return true;
 }
@@ -920,17 +922,17 @@ MapIteratorObject::create(JSContext *cx,
     Rooted<JSObject*> proto(cx, GlobalObject::getOrCreateMapIteratorPrototype(cx, global));
     if (!proto)
         return nullptr;
 
     ValueMap::Range *range = cx->new_<ValueMap::Range>(data->all());
     if (!range)
         return nullptr;
 
-    JSObject *iterobj = NewObjectWithGivenProto(cx, &class_, proto, global);
+    NativeObject *iterobj = NewNativeObjectWithGivenProto(cx, &class_, proto, global);
     if (!iterobj) {
         js_delete(range);
         return nullptr;
     }
     iterobj->setSlot(TargetSlot, ObjectValue(*mapobj));
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
     iterobj->setSlot(RangeSlot, PrivateValue(range));
     return static_cast<MapIteratorObject *>(iterobj);
@@ -1039,17 +1041,17 @@ const JSFunctionSpec MapObject::methods[
     JS_SELF_HOSTED_FN("forEach", "MapForEach", 2, 0),
     JS_FS_END
 };
 
 static JSObject *
 InitClass(JSContext *cx, Handle<GlobalObject*> global, const Class *clasp, JSProtoKey key, Native construct,
           const JSPropertySpec *properties, const JSFunctionSpec *methods)
 {
-    Rooted<JSObject*> proto(cx, global->createBlankPrototype(cx, clasp));
+    RootedNativeObject proto(cx, global->createBlankPrototype(cx, clasp));
     if (!proto)
         return nullptr;
     proto->setPrivate(nullptr);
 
     Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 1));
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
@@ -1193,17 +1195,17 @@ MapObject::set(JSContext *cx, HandleObje
     }
     WriteBarrierPost(cx->runtime(), map, key.get());
     return true;
 }
 
 MapObject*
 MapObject::create(JSContext *cx)
 {
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
+    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &class_));
     if (!obj)
         return nullptr;
 
     ValueMap *map = cx->new_<ValueMap>(cx->runtime());
     if (!map || !map->init()) {
         js_delete(map);
         js_ReportOutOfMemory(cx);
         return nullptr;
@@ -1273,17 +1275,17 @@ MapObject::construct(JSContext *cx, unsi
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 MapObject::is(HandleValue v)
 {
-    return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().getPrivate();
+    return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().as<MapObject>().getPrivate();
 }
 
 #define ARG0_KEY(cx, args, key)                                               \
     AutoHashableValueRooter key(cx);                                          \
     if (args.length() > 0 && !key.setValue(cx, args[0]))                      \
         return false
 
 ValueMap &
@@ -1484,17 +1486,17 @@ js_InitMapClass(JSContext *cx, HandleObj
     return MapObject::initClass(cx, obj);
 }
 
 
 /*** SetIterator *********************************************************************************/
 
 namespace {
 
-class SetIteratorObject : public JSObject
+class SetIteratorObject : public NativeObject
 {
   public:
     static const Class class_;
 
     enum { TargetSlot, KindSlot, RangeSlot, SlotCount };
     static const JSFunctionSpec methods[];
     static SetIteratorObject *create(JSContext *cx, HandleObject setobj, ValueSet *data,
                                      SetObject::IteratorKind kind);
@@ -1545,17 +1547,18 @@ SetIteratorObject::kind() const
 }
 
 bool
 GlobalObject::initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global)
 {
     JSObject *base = GlobalObject::getOrCreateIteratorPrototype(cx, global);
     if (!base)
         return false;
-    RootedObject proto(cx, NewObjectWithGivenProto(cx, &SetIteratorObject::class_, base, global));
+    RootedNativeObject proto(cx, NewNativeObjectWithGivenProto(cx, &SetIteratorObject::class_,
+                                                               base, global));
     if (!proto)
         return false;
     proto->setSlot(SetIteratorObject::RangeSlot, PrivateValue(nullptr));
     if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods))
         return false;
     global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto));
     return true;
 }
@@ -1568,17 +1571,17 @@ SetIteratorObject::create(JSContext *cx,
     Rooted<JSObject*> proto(cx, GlobalObject::getOrCreateSetIteratorPrototype(cx, global));
     if (!proto)
         return nullptr;
 
     ValueSet::Range *range = cx->new_<ValueSet::Range>(data->all());
     if (!range)
         return nullptr;
 
-    JSObject *iterobj = NewObjectWithGivenProto(cx, &class_, proto, global);
+    NativeObject *iterobj = NewNativeObjectWithGivenProto(cx, &class_, proto, global);
     if (!iterobj) {
         js_delete(range);
         return nullptr;
     }
     iterobj->setSlot(TargetSlot, ObjectValue(*setobj));
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
     iterobj->setSlot(RangeSlot, PrivateValue(range));
     return static_cast<SetIteratorObject *>(iterobj);
@@ -1737,17 +1740,17 @@ SetObject::add(JSContext *cx, HandleObje
     }
     WriteBarrierPost(cx->runtime(), set, key.get());
     return true;
 }
 
 SetObject*
 SetObject::create(JSContext *cx)
 {
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
+    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &class_));
     if (!obj)
         return nullptr;
 
     ValueSet *set = cx->new_<ValueSet>(cx->runtime());
     if (!set || !set->init()) {
         js_delete(set);
         js_ReportOutOfMemory(cx);
         return nullptr;
@@ -1807,17 +1810,17 @@ SetObject::construct(JSContext *cx, unsi
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 SetObject::is(HandleValue v)
 {
-    return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().getPrivate();
+    return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().as<SetObject>().getPrivate();
 }
 
 ValueSet &
 SetObject::extract(CallReceiver call)
 {
     MOZ_ASSERT(call.thisv().isObject());
     MOZ_ASSERT(call.thisv().toObject().hasClass(&SetObject::class_));
     return *static_cast<SetObject&>(call.thisv().toObject()).getData();
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -80,17 +80,17 @@ typedef OrderedHashMap<HashableValue,
                        RelocatableValue,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueMap;
 
 typedef OrderedHashSet<HashableValue,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueSet;
 
-class MapObject : public JSObject {
+class MapObject : public NativeObject {
   public:
     enum IteratorKind { Keys, Values, Entries };
 
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static const Class class_;
 
     static bool getKeysAndValuesInterleaved(JSContext *cx, HandleObject obj,
                                             JS::AutoValueVector *entries);
@@ -125,17 +125,17 @@ class MapObject : public JSObject {
     static bool keys(JSContext *cx, unsigned argc, Value *vp);
     static bool values_impl(JSContext *cx, CallArgs args);
     static bool values(JSContext *cx, unsigned argc, Value *vp);
     static bool entries_impl(JSContext *cx, CallArgs args);
     static bool clear_impl(JSContext *cx, CallArgs args);
     static bool clear(JSContext *cx, unsigned argc, Value *vp);
 };
 
-class SetObject : public JSObject {
+class SetObject : public NativeObject {
   public:
     enum IteratorKind { Values, Entries };
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static const Class class_;
 
     static bool keys(JSContext *cx, HandleObject obj, JS::AutoValueVector *keys);
     static bool values(JSContext *cx, unsigned argc, Value *vp);
     static bool add(JSContext *cx, HandleObject obj, HandleValue key);
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -11,16 +11,18 @@
 #include "jscntxt.h"
 
 #include "irregexp/RegExpParser.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
+
 using namespace js;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
 bool
 js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs &matches,
@@ -41,17 +43,17 @@ js::CreateRegExpMatchResult(JSContext *c
     /* Get the templateObject that defines the shape and type of the output object */
     JSObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return false;
 
     size_t numPairs = matches.length();
     MOZ_ASSERT(numPairs > 0);
 
-    RootedObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
+    RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
     if (!arr)
         return false;
 
     /* Store a Value for each pair. */
     for (size_t i = 0; i < numPairs; i++) {
         const MatchPair &pair = matches[i];
 
         if (pair.isUndefined()) {
@@ -66,31 +68,31 @@ js::CreateRegExpMatchResult(JSContext *c
 
             // We don't have to update type information here, since the match
             // result template is already known to have string elements.
             arr->initDenseElement(i, StringValue(str));
         }
     }
 
     /* Set the |index| property. (TemplateObject positions it in slot 0) */
-    arr->nativeSetSlot(0, Int32Value(matches[0].start));
+    arr->setSlot(0, Int32Value(matches[0].start));
 
     /* Set the |input| property. (TemplateObject positions it in slot 1) */
-    arr->nativeSetSlot(1, StringValue(input));
+    arr->setSlot(1, StringValue(input));
 
 #ifdef DEBUG
     RootedValue test(cx);
     RootedId id(cx, NameToId(cx->names().index));
     if (!baseops::GetProperty(cx, arr, id, &test))
         return false;
-    MOZ_ASSERT(test == arr->nativeGetSlot(0));
+    MOZ_ASSERT(test == arr->getSlot(0));
     id = NameToId(cx->names().input);
     if (!baseops::GetProperty(cx, arr, id, &test))
         return false;
-    MOZ_ASSERT(test == arr->nativeGetSlot(1));
+    MOZ_ASSERT(test == arr->getSlot(1));
 #endif
 
     rval.setObject(*arr);
     return true;
 }
 
 static RegExpRunStatus
 ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, HandleLinearString input,
@@ -503,17 +505,17 @@ static const JSPropertySpec regexp_stati
 
 JSObject *
 js_InitRegExpClass(JSContext *cx, HandleObject obj)
 {
     MOZ_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
-    RootedObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_));
+    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_));
     if (!proto)
         return nullptr;
     proto->setPrivate(nullptr);
 
     HandlePropertyName empty = cx->names().empty;
     RegExpObjectBuilder builder(cx, &proto->as<RegExpObject>());
     if (!builder.build(empty, RegExpFlag(0)))
         return nullptr;
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/SymbolObject.h
@@ -2,23 +2,22 @@
  * vim: set ts=8 sts=4 et sw=4 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 builtin_SymbolObject_h
 #define builtin_SymbolObject_h
 
-#include "jsobj.h"
-
+#include "vm/ObjectImpl.h"
 #include "vm/Symbol.h"
 
 namespace js {
 
-class SymbolObject : public JSObject
+class SymbolObject : public NativeObject
 {
     /* Stores this Symbol object's [[PrimitiveValue]]. */
     static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
   public:
     static const unsigned RESERVED_SLOTS = 1;
 
     static const Class class_;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -31,16 +31,18 @@
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/TraceLogging.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
+
 using namespace js;
 using namespace JS;
 
 using mozilla::ArrayLength;
 using mozilla::Move;
 using mozilla::UniquePtr;
 
 // If fuzzingSafe is set, remove functionality that could cause problems with
@@ -1364,31 +1366,31 @@ static bool
 SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
     args.rval().setUndefined();
     return true;
 }
 
-class CloneBufferObject : public JSObject {
+class CloneBufferObject : public NativeObject {
     static const JSPropertySpec props_[2];
     static const size_t DATA_SLOT   = 0;
     static const size_t LENGTH_SLOT = 1;
     static const size_t NUM_SLOTS   = 2;
 
   public:
     static const Class class_;
 
     static CloneBufferObject *Create(JSContext *cx) {
         RootedObject obj(cx, JS_NewObject(cx, Jsvalify(&class_), JS::NullPtr(), JS::NullPtr()));
         if (!obj)
             return nullptr;
-        obj->setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
-        obj->setReservedSlot(LENGTH_SLOT, Int32Value(0));
+        obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
+        obj->as<CloneBufferObject>().setReservedSlot(LENGTH_SLOT, Int32Value(0));
 
         if (!JS_DefineProperties(cx, obj, props_))
             return nullptr;
 
         return &obj->as<CloneBufferObject>();
     }
 
     static CloneBufferObject *Create(JSContext *cx, JSAutoStructuredCloneBuffer *buffer) {
@@ -1971,17 +1973,17 @@ FindPath(JSContext *cx, unsigned argc, j
     //     edge: <string describing outgoing edge from node>
     //   }
     //
     // or, if the node is some internal thing that isn't a proper JavaScript
     // value:
     //
     //   { node: undefined, edge: <string> }
     size_t length = nodes.length();
-    RootedObject result(cx, NewDenseFullyAllocatedArray(cx, length));
+    RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
     if (!result)
         return false;
     result->ensureDenseInitializedLength(cx, 0, length);
 
     // Walk |nodes| and |edges| in the stored order, and construct the result
     // array in start-to-target order.
     for (size_t i = 0; i < length; i++) {
         // Build an object describing the node and edge.
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -20,16 +20,17 @@
 #include "vm/ObjectImpl.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
 #include "vm/Shape-inl.h"
 
 using mozilla::AssertedCast;
 using mozilla::CheckedInt32;
 using mozilla::DebugOnly;
 
 using namespace js;
 
@@ -1118,81 +1119,74 @@ StructMetaTypeDescr::construct(JSContext
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                          JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
     return false;
 }
 
 size_t
 StructTypeDescr::fieldCount() const
 {
-    return getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject().getDenseInitializedLength();
+    return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseInitializedLength();
 }
 
 size_t
 StructTypeDescr::maybeForwardedFieldCount() const
 {
-    JSObject *fieldNames =
-        MaybeForwarded(&getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject());
-    return fieldNames->getDenseInitializedLength();
+    return maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseInitializedLength();
 }
 
 bool
 StructTypeDescr::fieldIndex(jsid id, size_t *out) const
 {
-    JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
+    NativeObject &fieldNames = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES);
     size_t l = fieldNames.getDenseInitializedLength();
     for (size_t i = 0; i < l; i++) {
         JSAtom &a = fieldNames.getDenseElement(i).toString()->asAtom();
         if (JSID_IS_ATOM(id, &a)) {
             *out = i;
             return true;
         }
     }
     return false;
 }
 
 JSAtom &
 StructTypeDescr::fieldName(size_t index) const
 {
-    JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
-    return fieldNames.getDenseElement(index).toString()->asAtom();
+    return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseElement(index).toString()->asAtom();
 }
 
 size_t
 StructTypeDescr::fieldOffset(size_t index) const
 {
-    JSObject &fieldOffsets =
-        getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject();
+    NativeObject &fieldOffsets = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
     MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
     return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
 }
 
 size_t
 StructTypeDescr::maybeForwardedFieldOffset(size_t index) const
 {
-    JSObject &fieldOffsets =
-        *MaybeForwarded(&getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject());
+    NativeObject &fieldOffsets = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
     MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
     return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
 }
 
 SizedTypeDescr&
 StructTypeDescr::fieldDescr(size_t index) const
 {
-    JSObject &fieldDescrs =
-        getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject();
+    NativeObject &fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
     return fieldDescrs.getDenseElement(index).toObject().as<SizedTypeDescr>();
 }
 
 SizedTypeDescr&
 StructTypeDescr::maybeForwardedFieldDescr(size_t index) const
 {
-    JSObject &fieldDescrs =
-        *MaybeForwarded(&getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject());
+    NativeObject &fieldDescrs = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
     JSObject &descr =
         *MaybeForwarded(&fieldDescrs.getDenseElement(index).toObject());
     return descr.as<SizedTypeDescr>();
 }
 
 /******************************************************************************
  * Creating the TypedObject "module"
@@ -1296,17 +1290,17 @@ DefineSimpleTypeDescr(JSContext *cx,
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
 template<typename T>
 static JSObject *
 DefineMetaTypeDescr(JSContext *cx,
                     Handle<GlobalObject*> global,
-                    HandleObject module,
+                    HandleNativeObject module,
                     TypedObjectModuleObject::Slot protoSlot)
 {
     RootedAtom className(cx, Atomize(cx, T::class_.name,
                                      strlen(T::class_.name)));
     if (!className)
         return nullptr;
 
     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
@@ -1465,17 +1459,17 @@ js_InitTypedObjectDummy(JSContext *cx, H
  * Typed objects
  */
 
 int32_t
 TypedObject::offset() const
 {
     if (is<InlineOpaqueTypedObject>())
         return 0;
-    return getReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET).toInt32();
+    return fakeNativeGetReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET).toInt32();
 }
 
 int32_t
 TypedObject::length() const
 {
     MOZ_ASSERT(typeDescr().kind() == type::SizedArray ||
                typeDescr().kind() == type::UnsizedArray);
 
@@ -1581,36 +1575,36 @@ OutlineTypedObject::createUnattachedWith
     RootedObject proto(cx, PrototypeForTypeDescr(cx, type));
     if (!proto)
         return nullptr;
 
     RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, proto, nullptr));
     if (!obj)
         return nullptr;
 
-    obj->initPrivate(nullptr);
-    obj->initReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(0));
-    obj->initReservedSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(length));
-    obj->initReservedSlot(JS_BUFVIEW_SLOT_OWNER, NullValue());
+    obj->fakeNativeInitPrivate(nullptr);
+    obj->fakeNativeInitReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(0));
+    obj->fakeNativeInitReservedSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(length));
+    obj->fakeNativeInitReservedSlot(JS_BUFVIEW_SLOT_OWNER, NullValue());
 
     return &obj->as<OutlineTypedObject>();
 }
 
 void
 OutlineTypedObject::attach(JSContext *cx, ArrayBufferObject &buffer, int32_t offset)
 {
     MOZ_ASSERT(offset >= 0);
     MOZ_ASSERT((size_t) (offset + size()) <= buffer.byteLength());
 
     if (!buffer.addView(cx, this))
         CrashAtUnhandlableOOM("TypedObject::attach");
 
-    InitArrayBufferViewDataPointer(this, &buffer, offset);
-    setReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
-    setReservedSlot(JS_BUFVIEW_SLOT_OWNER, ObjectValue(buffer));
+    fakeNativeInitPrivate(buffer.dataPointer() + offset);
+    fakeNativeSetReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
+    fakeNativeSetReservedSlot(JS_BUFVIEW_SLOT_OWNER, ObjectValue(buffer));
 }
 
 void
 OutlineTypedObject::attach(JSContext *cx, TypedObject &typedObj, int32_t offset)
 {
     MOZ_ASSERT(typedObj.isAttached());
 
     JSObject *owner = &typedObj;
@@ -1618,21 +1612,21 @@ OutlineTypedObject::attach(JSContext *cx
         owner = &typedObj.as<OutlineTypedObject>().owner();
         offset += typedObj.offset();
     }
 
     if (owner->is<ArrayBufferObject>()) {
         attach(cx, owner->as<ArrayBufferObject>(), offset);
     } else {
         MOZ_ASSERT(owner->is<InlineOpaqueTypedObject>());
-        initPrivate(owner->as<InlineOpaqueTypedObject>().inlineTypedMem() + offset);
+        fakeNativeInitPrivate(owner->as<InlineOpaqueTypedObject>().inlineTypedMem() + offset);
         PostBarrierTypedArrayObject(this);
 
-        setReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
-        setReservedSlot(JS_BUFVIEW_SLOT_OWNER, ObjectValue(*owner));
+        fakeNativeSetReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
+        fakeNativeSetReservedSlot(JS_BUFVIEW_SLOT_OWNER, ObjectValue(*owner));
     }
 }
 
 // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
 // the type `type`. `type` must not be an unsized array.
 static int32_t
 TypedObjLengthFromType(TypeDescr &descr)
 {
@@ -1762,29 +1756,29 @@ OutlineTypedObject::obj_trace(JSTracer *
     // When this is called for compacting GC, the related objects we touch here
     // may not have had their slots updated yet. Note that this does not apply
     // to generational GC because these objects (type descriptors and
     // prototypes) are never allocated in the nursery.
     TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
 
     // Mark the owner, watching in case it is moved by the tracer.
     JSObject *oldOwner = typedObj.maybeOwner();
-    gc::MarkSlot(trc, &typedObj.getFixedSlotRef(JS_BUFVIEW_SLOT_OWNER), "typed object owner");
+    gc::MarkSlot(trc, &typedObj.fakeNativeGetSlotRef(JS_BUFVIEW_SLOT_OWNER), "typed object owner");
     JSObject *owner = typedObj.maybeOwner();
 
     uint8_t *mem = typedObj.outOfLineTypedMem();
 
     // Update the data pointer if the owner moved and the owner's data is
     // inline with it.
     if (owner != oldOwner &&
         (owner->is<InlineOpaqueTypedObject>() ||
          owner->as<ArrayBufferObject>().hasInlineData()))
     {
         mem += reinterpret_cast<uint8_t *>(owner) - reinterpret_cast<uint8_t *>(oldOwner);
-        typedObj.setPrivate(mem);
+        typedObj.fakeNativeSetPrivate(mem);
     }
 
     if (!descr.opaque() || !typedObj.maybeForwardedIsAttached())
         return;
 
     switch (descr.kind()) {
       case type::Scalar:
       case type::Reference:
@@ -2323,46 +2317,46 @@ TypedObject::obj_enumerate(JSContext *cx
     }
 
     return true;
 }
 
 /* static */ size_t
 OutlineTypedObject::offsetOfOwnerSlot()
 {
-    return JSObject::getFixedSlotOffset(JS_BUFVIEW_SLOT_OWNER);
+    return NativeObject::getFixedSlotOffset(JS_BUFVIEW_SLOT_OWNER);
 }
 
 /* static */ size_t
 OutlineTypedObject::offsetOfDataSlot()
 {
 #ifdef DEBUG
     // Compute offset of private data based on TransparentTypedObject;
     // both OpaqueOutlineTypedObject and TransparentTypedObject have the same
     // number of slots, so no problem there.
     gc::AllocKind allocKind = gc::GetGCObjectKind(&TransparentTypedObject::class_);
     size_t nfixed = gc::GetGCKindSlots(allocKind);
     MOZ_ASSERT(DATA_SLOT == nfixed - 1);
 #endif
 
-    return JSObject::getPrivateDataOffset(DATA_SLOT);
+    return NativeObject::getPrivateDataOffset(DATA_SLOT);
 }
 
 /* static */ size_t
 OutlineTypedObject::offsetOfByteOffsetSlot()
 {
-    return JSObject::getFixedSlotOffset(JS_BUFVIEW_SLOT_BYTEOFFSET);
+    return NativeObject::getFixedSlotOffset(JS_BUFVIEW_SLOT_BYTEOFFSET);
 }
 
 void
 OutlineTypedObject::neuter(void *newData)
 {
-    setSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(0));
-    setSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(0));
-    setPrivate(newData);
+    fakeNativeSetSlot(JS_BUFVIEW_SLOT_LENGTH, Int32Value(0));
+    fakeNativeSetSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(0));
+    fakeNativeSetPrivate(newData);
 }
 
 /******************************************************************************
  * Inline typed objects
  */
 
 /* static */ InlineOpaqueTypedObject *
 InlineOpaqueTypedObject::create(JSContext *cx, HandleTypeDescr descr)
@@ -2378,24 +2372,24 @@ InlineOpaqueTypedObject::create(JSContex
         return nullptr;
 
     return &obj->as<InlineOpaqueTypedObject>();
 }
 
 uint8_t *
 InlineOpaqueTypedObject::inlineTypedMem() const
 {
-    return fixedData(0);
+    return fakeNativeFixedData(0);
 }
 
 /* static */
 size_t
 InlineOpaqueTypedObject::offsetOfDataStart()
 {
-    return getFixedSlotOffset(0);
+    return NativeObject::getFixedSlotOffset(0);
 }
 
 /* static */ void
 InlineOpaqueTypedObject::obj_trace(JSTracer *trc, JSObject *object)
 {
     InlineOpaqueTypedObject &typedObj = object->as<InlineOpaqueTypedObject>();
 
     // When this is called for compacting GC, the related objects we touch here
@@ -2838,18 +2832,18 @@ js::SetTypedObjectOffset(ThreadSafeConte
     MOZ_ASSERT(args[1].isInt32());
 
     OutlineTypedObject &typedObj = args[0].toObject().as<OutlineTypedObject>();
     int32_t offset = args[1].toInt32();
 
     MOZ_ASSERT(typedObj.isAttached());
     int32_t oldOffset = typedObj.offset();
 
-    typedObj.setPrivate((typedObj.typedMem() - oldOffset) + offset);
-    typedObj.setReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
+    typedObj.fakeNativeSetPrivate((typedObj.typedMem() - oldOffset) + offset);
+    typedObj.fakeNativeSetReservedSlot(JS_BUFVIEW_SLOT_BYTEOFFSET, Int32Value(offset));
     args.rval().setUndefined();
     return true;
 }
 
 bool
 js::intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp)
 {
     // Do not use JSNativeThreadSafeWrapper<> so that ion can reference
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -139,17 +139,17 @@ class SimdTypeDescr;
 class StructTypeDescr;
 class SizedTypedProto;
 
 /*
  * The prototype for a typed object. Currently, carries a link to the
  * type descriptor. Eventually will carry most of the type information
  * we want.
  */
-class TypedProto : public JSObject
+class TypedProto : public NativeObject
 {
   public:
     static const Class class_;
 
     inline void initTypeDescrSlot(TypeDescr &descr);
 
     TypeDescr &typeDescr() const {
         return getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject().as<TypeDescr>();
@@ -157,17 +157,17 @@ class TypedProto : public JSObject
 
     TypeDescr &maybeForwardedTypeDescr() const {
         return MaybeForwarded(&getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject())->as<TypeDescr>();
     }
 
     inline type::Kind kind() const;
 };
 
-class TypeDescr : public JSObject
+class TypeDescr : public NativeObject
 {
   public:
     // This is *intentionally* not defined so as to produce link
     // errors if a is<FooTypeDescr>() etc goes wrong. Otherwise, the
     // default implementation resolves this to a reference to
     // FooTypeDescr::class_ which resolves to
     // JSObject::class_. Debugging the resulting errors leads to much
     // fun and rejoicing.
@@ -504,38 +504,47 @@ class StructTypeDescr : public ComplexTy
 
     // Return the type descr of the field at index `index`.
     SizedTypeDescr &fieldDescr(size_t index) const;
     SizedTypeDescr &maybeForwardedFieldDescr(size_t index) const;
 
     // Return the offset of the field at index `index`.
     size_t fieldOffset(size_t index) const;
     size_t maybeForwardedFieldOffset(size_t index) const;
+
+  private:
+    NativeObject &fieldInfoObject(size_t slot) const {
+        return getReservedSlot(slot).toObject().as<NativeObject>();
+    }
+
+    NativeObject &maybeForwardedFieldInfoObject(size_t slot) const {
+        return *MaybeForwarded(&fieldInfoObject(slot));
+    }
 };
 
 typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
 
 /*
  * This object exists in order to encapsulate the typed object types
  * somewhat, rather than sticking them all into the global object.
  * Eventually it will go away and become a module.
  */
-class TypedObjectModuleObject : public JSObject {
+class TypedObjectModuleObject : public NativeObject {
   public:
     enum Slot {
         ArrayTypePrototype,
         StructTypePrototype,
         SlotCount
     };
 
     static const Class class_;
 };
 
 /* Base type for transparent and opaque typed objects. */
-class TypedObject : public ArrayBufferViewObject
+class TypedObject : public JSObject
 {
   private:
     static const bool IsTypedObjectClass = true;
 
     template<class T>
     static bool obj_getArrayElement(JSContext *cx,
                                     Handle<TypedObject*> typedObj,
                                     Handle<TypeDescr*> typeDescr,
@@ -685,29 +694,29 @@ class OutlineTypedObject : public TypedO
     // where the `void*` pointer can be found. It is intended for use
     // by the JIT.
     static size_t offsetOfDataSlot();
 
     // Offset of the byte offset slot.
     static size_t offsetOfByteOffsetSlot();
 
     JSObject &owner() const {
-        return getReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObject();
+        return fakeNativeGetReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObject();
     }
 
     JSObject *maybeOwner() const {
-        return getReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObjectOrNull();
+        return fakeNativeGetReservedSlot(JS_BUFVIEW_SLOT_OWNER).toObjectOrNull();
     }
 
     uint8_t *outOfLineTypedMem() const {
-        return static_cast<uint8_t *>(getPrivate(DATA_SLOT));
+        return static_cast<uint8_t *>(fakeNativeGetPrivate(DATA_SLOT));
     }
 
     int32_t length() const {
-        return getReservedSlot(JS_BUFVIEW_SLOT_LENGTH).toInt32();
+        return fakeNativeGetReservedSlot(JS_BUFVIEW_SLOT_LENGTH).toInt32();
     }
 
     // Helper for createUnattached()
     static OutlineTypedObject *createUnattachedWithClass(JSContext *cx,
                                                          const Class *clasp,
                                                          HandleTypeDescr type,
                                                          int32_t length);
 
@@ -758,17 +767,17 @@ class OutlineOpaqueTypedObject : public 
 };
 
 // Class for an opaque typed object whose data is allocated inline.
 class InlineOpaqueTypedObject : public TypedObject
 {
   public:
     static const Class class_;
 
-    static const size_t MaximumSize = JSObject::MAX_FIXED_SLOTS * sizeof(Value);
+    static const size_t MaximumSize = NativeObject::MAX_FIXED_SLOTS * sizeof(Value);
 
     static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
         size_t nbytes = descr->as<SizedTypeDescr>().size();
         MOZ_ASSERT(nbytes <= MaximumSize);
 
         size_t dataSlots = AlignBytes(nbytes, sizeof(Value) / sizeof(Value));
         MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
         return gc::GetGCObjectKind(dataSlots);
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.cpp
@@ -10,16 +10,18 @@
 #include "jscntxt.h"
 #include "jsiter.h"
 
 #include "builtin/SelfHostingDefines.h"
 #include "vm/GlobalObject.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
+
 using namespace js;
 using namespace JS;
 
 const Class WeakSetObject::class_ = {
     "WeakSet",
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet) |
     JSCLASS_HAS_RESERVED_SLOTS(WeakSetObject::RESERVED_SLOTS),
     JS_PropertyStub,         // addProperty
@@ -43,17 +45,17 @@ const JSFunctionSpec WeakSetObject::meth
     JS_FS_END
 };
 
 JSObject *
 WeakSetObject::initClass(JSContext *cx, JSObject *obj)
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     // Todo: WeakSet.prototype should not be a WeakSet!
-    Rooted<JSObject*> proto(cx, global->createBlankPrototype(cx, &class_));
+    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &class_));
     if (!proto)
         return nullptr;
     proto->setReservedSlot(WEAKSET_MAP_SLOT, UndefinedValue());
 
     Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(JSProto_WeakSet, cx), 1));
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
@@ -62,17 +64,17 @@ WeakSetObject::initClass(JSContext *cx, 
         return nullptr;
     }
     return proto;
 }
 
 WeakSetObject*
 WeakSetObject::create(JSContext *cx)
 {
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &class_));
+    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &class_));
     if (!obj)
         return nullptr;
 
     RootedObject map(cx, NewWeakMapObject(cx));
     if (!map)
         return nullptr;
 
     obj->setReservedSlot(WEAKSET_MAP_SLOT, ObjectValue(*map));
--- a/js/src/builtin/WeakSetObject.h
+++ b/js/src/builtin/WeakSetObject.h
@@ -2,21 +2,21 @@
  * vim: set ts=8 sts=4 et sw=4 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 builtin_WeakSetObject_h
 #define builtin_WeakSetObject_h
 
-#include "jsobj.h"
+#include "vm/ObjectImpl.h"
 
 namespace js {
 
-class WeakSetObject : public JSObject
+class WeakSetObject : public NativeObject
 {
   public:
     static const unsigned RESERVED_SLOTS = 1;
 
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static const Class class_;
 
   private:
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -3388,41 +3388,41 @@ CType::Finalize(JSFreeOp *fop, JSObject*
     break;
   }
 }
 
 void
 CType::Trace(JSTracer* trc, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
-  jsval slot = obj->getSlot(SLOT_TYPECODE);
+  jsval slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
   if (slot.isUndefined())
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(slot.toInt32())) {
   case TYPE_struct: {
-    slot = obj->getReservedSlot(SLOT_FIELDINFO);
+    slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
     if (slot.isUndefined())
       return;
 
     FieldInfoHash* fields = static_cast<FieldInfoHash*>(slot.toPrivate());
     for (FieldInfoHash::Enum e(*fields); !e.empty(); e.popFront()) {
       JSString *key = e.front().key();
       JS_CallUnbarrieredStringTracer(trc, &key, "fieldName");
       if (key != e.front().key())
           e.rekeyFront(JS_ASSERT_STRING_IS_FLAT(key));
       JS_CallObjectTracer(trc, &e.front().value().mType, "fieldType");
     }
 
     break;
   }
   case TYPE_function: {
     // Check if we have a FunctionInfo.
-    slot = obj->getReservedSlot(SLOT_FNINFO);
+    slot = obj->as<NativeObject>().getReservedSlot(SLOT_FNINFO);
     if (slot.isUndefined())
       return;
 
     FunctionInfo* fninfo = static_cast<FunctionInfo*>(slot.toPrivate());
     MOZ_ASSERT(fninfo);
 
     // Identify our objects to the tracer.
     JS_CallObjectTracer(trc, &fninfo->mABI, "abi");
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -34,16 +34,17 @@
 #include "vm/Stack.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseMaps-inl.h"
 #include "frontend/ParseNode-inl.h"
+#include "vm/ObjectImpl-inl.h"
 #include "vm/ScopeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::DebugOnly;
 using mozilla::NumberIsInt32;
@@ -2161,19 +2162,19 @@ EmitNewInit(ExclusiveContext *cx, Byteco
     return true;
 }
 
 static bool
 IteratorResultShape(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned *shape)
 {
     MOZ_ASSERT(bce->script->compileAndGo());
 
-    RootedObject obj(cx);
+    RootedNativeObject obj(cx);
     gc::AllocKind kind = GuessObjectGCKind(2);
-    obj = NewBuiltinClassInstance(cx, &JSObject::class_, kind);
+    obj = NewNativeBuiltinClassInstance(cx, &JSObject::class_, kind);
     if (!obj)
         return false;
 
     Rooted<jsid> value_id(cx, AtomToId(cx->names().value));
     Rooted<jsid> done_id(cx, AtomToId(cx->names().done));
     if (!DefineNativeProperty(cx, obj, value_id, UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_ENUMERATE))
         return false;
@@ -4060,17 +4061,17 @@ ParseNode::getConstantValue(ExclusiveCon
             count = pn_count - 1;
             pn = pn_head->pn_next;
         } else {
             MOZ_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
             count = pn_count;
             pn = pn_head;
         }
 
-        RootedObject obj(cx, NewDenseFullyAllocatedArray(cx, count, nullptr, MaybeSingletonObject));
+        RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, count, nullptr, MaybeSingletonObject));
         if (!obj)
             return false;
 
         unsigned idx = 0;
         RootedId id(cx);
         for (; pn; idx++, pn = pn->pn_next) {
             if (!pn->getConstantValue(cx, allowObjects, &value))
                 return false;
@@ -4089,17 +4090,18 @@ ParseNode::getConstantValue(ExclusiveCon
         MOZ_ASSERT(!(pn_xflags & PNX_NONCONST));
 
         if (allowObjects == DontAllowObjects)
             return false;
         if (allowObjects == DontAllowNestedObjects)
             allowObjects = DontAllowObjects;
 
         gc::AllocKind kind = GuessObjectGCKind(pn_count);
-        RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, kind, MaybeSingletonObject));
+        RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_,
+                                                                 kind, MaybeSingletonObject));
         if (!obj)
             return false;
 
         RootedValue value(cx), idvalue(cx);
         for (ParseNode *pn = pn_head; pn; pn = pn->pn_next) {
             if (!pn->pn_right->getConstantValue(cx, allowObjects, &value))
                 return false;
 
@@ -4152,17 +4154,17 @@ ParseNode::getConstantValue(ExclusiveCon
 
 static bool
 EmitSingletonInitialiser(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value))
         return false;
 
-    RootedObject obj(cx, &value.toObject());
+    RootedNativeObject obj(cx, &value.toObject().as<NativeObject>());
     if (!obj->is<ArrayObject>() && !JSObject::setSingletonType(cx, obj))
         return false;
 
     ObjectBox *objbox = bce->parser->newObjectBox(obj);
     if (!objbox)
         return false;
 
     return EmitObjectOp(cx, objbox, JSOP_OBJECT, bce);
@@ -4172,26 +4174,26 @@ static bool
 EmitCallSiteObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox *objbox1 = bce->parser->newObjectBox(&value.toObject());
+    ObjectBox *objbox1 = bce->parser->newObjectBox(&value.toObject().as<NativeObject>());
     if (!objbox1)
         return false;
 
     if (!pn->as<CallSiteNode>().getRawArrayValue(cx, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox *objbox2 = bce->parser->newObjectBox(&value.toObject());
+    ObjectBox *objbox2 = bce->parser->newObjectBox(&value.toObject().as<NativeObject>());
     if (!objbox2)
         return false;
 
     return EmitObjectPairOp(cx, objbox1, objbox2, JSOP_CALLSITEOBJ, bce);
 }
 
 /* See the SRC_FOR source note offsetBias comments later in this file. */
 JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
@@ -6214,20 +6216,20 @@ EmitObject(ExclusiveContext *cx, Bytecod
     ptrdiff_t offset = bce->offset();
     if (!EmitNewInit(cx, bce, JSProto_Object))
         return false;
 
     /*
      * Try to construct the shape of the object as we go, so we can emit a
      * JSOP_NEWOBJECT with the final shape instead.
      */
-    RootedObject obj(cx);
+    RootedNativeObject obj(cx);
     if (bce->script->compileAndGo()) {
         gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
-        obj = NewBuiltinClassInstance(cx, &JSObject::class_, kind, TenuredObject);
+        obj = NewNativeBuiltinClassInstance(cx, &JSObject::class_, kind, TenuredObject);
         if (!obj)
             return false;
     }
 
     for (ParseNode *propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
         if (!UpdateSourceCoordNotes(cx, bce, propdef->pn_pos.begin))
             return false;
 
@@ -6899,17 +6901,17 @@ frontend::EmitTree(ExclusiveContext *cx,
             {
                 // Note: the type of the template object might not yet reflect
                 // that the object has copy on write elements. When the
                 // interpreter or JIT compiler fetches the template, it should
                 // use types::GetOrFixupCopyOnWriteObject to make sure the type
                 // for the template is accurate. We don't do this here as we
                 // want to use types::InitObject, which requires a finished
                 // script.
-                JSObject *obj = &value.toObject();
+                NativeObject *obj = &value.toObject().as<NativeObject>();
                 if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj))
                     return false;
 
                 ObjectBox *objbox = bce->parser->newObjectBox(obj);
                 if (!objbox)
                     return false;
 
                 ok = EmitObjectOp(cx, objbox, JSOP_NEWARRAY_COPYONWRITE, bce);
@@ -7273,17 +7275,17 @@ CGObjectList::indexOf(JSObject *obj)
 }
 
 void
 CGObjectList::finish(ObjectArray *array)
 {
     MOZ_ASSERT(length <= INDEX_LIMIT);
     MOZ_ASSERT(length == array->length);
 
-    js::HeapPtrObject *cursor = array->vector + array->length;
+    js::HeapPtrNativeObject *cursor = array->vector + array->length;
     ObjectBox *objbox = lastbox;
     do {
         --cursor;
         MOZ_ASSERT(!*cursor);
         *cursor = objbox->object;
     } while ((objbox = objbox->emitLink) != nullptr);
     MOZ_ASSERT(cursor == array->vector);
 }
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -167,17 +167,17 @@ class FullParseHandler
     ParseNode *newNullLiteral(const TokenPos &pos) {
         return new_<NullLiteral>(pos);
     }
 
     // The Boxer object here is any object that can allocate ObjectBoxes.
     // Specifically, a Boxer has a .newObjectBox(T) method that accepts a
     // Rooted<RegExpObject*> argument and returns an ObjectBox*.
     template <class Boxer>
-    ParseNode *newRegExp(HandleObject reobj, const TokenPos &pos, Boxer &boxer) {
+    ParseNode *newRegExp(RegExpObject *reobj, const TokenPos &pos, Boxer &boxer) {
         ObjectBox *objbox = boxer.newObjectBox(reobj);
         if (!objbox)
             return null();
         return new_<RegExpLiteral>(objbox, pos);
     }
 
     ParseNode *newConditional(ParseNode *cond, ParseNode *thenExpr, ParseNode *elseExpr) {
         return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -749,17 +749,17 @@ NameNode::dump(int indent)
         fprintf(stderr, "(%s ", name);
         indent += strlen(name) + 2;
         DumpParseTree(expr(), indent);
         fprintf(stderr, ")");
     }
 }
 #endif
 
-ObjectBox::ObjectBox(JSObject *object, ObjectBox* traceLink)
+ObjectBox::ObjectBox(NativeObject *object, ObjectBox* traceLink)
   : object(object),
     traceLink(traceLink),
     emitLink(nullptr)
 {
     MOZ_ASSERT(!object->is<JSFunction>());
 }
 
 ObjectBox::ObjectBox(JSFunction *function, ObjectBox* traceLink)
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1493,19 +1493,19 @@ ParseNode::isConstant()
       default:
         return false;
     }
 }
 
 class ObjectBox
 {
   public:
-    JSObject *object;
+    NativeObject *object;
 
-    ObjectBox(JSObject *object, ObjectBox *traceLink);
+    ObjectBox(NativeObject *object, ObjectBox *traceLink);
     bool isFunctionBox() { return object->is<JSFunction>(); }
     FunctionBox *asFunctionBox();
     void trace(JSTracer *trc);
 
   protected:
     friend struct CGObjectList;
 
     ObjectBox *traceLink;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -532,17 +532,17 @@ Parser<ParseHandler>::~Parser()
     {
         AutoLockForExclusiveAccess lock(context);
         context->perThreadData->removeActiveCompilation();
     }
 }
 
 template <typename ParseHandler>
 ObjectBox *
-Parser<ParseHandler>::newObjectBox(JSObject *obj)
+Parser<ParseHandler>::newObjectBox(NativeObject *obj)
 {
     MOZ_ASSERT(obj && !IsPoisonedPtr(obj));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
@@ -2956,17 +2956,17 @@ LexicalLookup(ContextT *ct, HandleAtom a
         if (stmt->type == STMT_WITH)
             break;
 
         // Skip statements that do not introduce a new scope
         if (!stmt->isBlockScope)
             continue;
 
         StaticBlockObject &blockObj = stmt->staticBlock();
-        Shape *shape = blockObj.nativeLookup(ct->sc->context, id);
+        Shape *shape = blockObj.lookup(ct->sc->context, id);
         if (shape) {
             if (slotp)
                 *slotp = blockObj.shapeToIndex(*shape);
             return stmt;
         }
     }
 
     if (slotp)
@@ -7214,17 +7214,17 @@ Parser<ParseHandler>::arrayInitializer()
         handler.setListFlag(literal, PNX_NONCONST);
     } else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) {
         // ES6 array comprehension.
         return arrayComprehension(begin);
     } else {
         bool spread = false, missingTrailingComma = false;
         uint32_t index = 0;
         for (; ; index++) {
-            if (index == JSObject::NELEMENTS_LIMIT) {
+            if (index == NativeObject::NELEMENTS_LIMIT) {
                 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
             TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
             if (tt == TOK_RB)
                 break;
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -424,17 +424,17 @@ class Parser : private JS::AutoGCRooter,
      * Parse a top-level JS script.
      */
     Node parse(JSObject *chain);
 
     /*
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
-    ObjectBox *newObjectBox(JSObject *obj);
+    ObjectBox *newObjectBox(NativeObject *obj);
     FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
                                 Directives directives, GeneratorKind generatorKind);
 
     /*
      * Create a new function object given parse context (pc) and a name (which
      * is optional if this is a function expression).
      */
     JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind,
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -90,17 +90,17 @@ class SyntaxParseHandler
     bool addToCallSiteObject(Node callSiteObj, Node rawNode, Node cookedNode) {
         return true;
     }
 
     Node newThisLiteral(const TokenPos &pos) { return NodeGeneric; }
     Node newNullLiteral(const TokenPos &pos) { return NodeGeneric; }
 
     template <class Boxer>
-    Node newRegExp(JSObject *reobj, const TokenPos &pos, Boxer &boxer) { return NodeGeneric; }
+    Node newRegExp(RegExpObject *reobj, const TokenPos &pos, Boxer &boxer) { return NodeGeneric; }
 
     Node newConditional(Node cond, Node thenExpr, Node elseExpr) { return NodeGeneric; }
 
     Node newElision() { return NodeGeneric; }
 
     Node newDelete(uint32_t begin, Node expr) { return NodeGeneric; }
 
     Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) {
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -29,35 +29,35 @@ ValueReadBarrier(const Value &value)
         MOZ_ASSERT(!value.isMarkable());
 }
 
 #ifdef DEBUG
 bool
 HeapSlot::preconditionForSet(JSObject *owner, Kind kind, uint32_t slot)
 {
     return kind == Slot
-         ? &owner->getSlotRef(slot) == this
-         : &owner->getDenseElement(slot) == (const Value *)this;
+         ? &owner->fakeNativeGetSlotRef(slot) == this
+         : &owner->fakeNativeGetDenseElement(slot) == (const Value *)this;
 }
 
 bool
 HeapSlot::preconditionForSet(Zone *zone, JSObject *owner, Kind kind, uint32_t slot)
 {
     bool ok = kind == Slot
-            ? &owner->getSlotRef(slot) == this
-            : &owner->getDenseElement(slot) == (const Value *)this;
+            ? &owner->fakeNativeGetSlotRef(slot) == this
+            : &owner->fakeNativeGetDenseElement(slot) == (const Value *)this;
     return ok && owner->zone() == zone;
 }
 
 bool
 HeapSlot::preconditionForWriteBarrierPost(JSObject *obj, Kind kind, uint32_t slot, Value target) const
 {
     return kind == Slot
-         ? obj->getSlotAddressUnchecked(slot)->get() == target
-         : static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target;
+         ? obj->fakeNativeGetSlotAddressUnchecked(slot)->get() == target
+         : static_cast<HeapSlot *>(obj->fakeNativeGetDenseElements() + slot)->get() == target;
 }
 
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
 {
     return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
 }
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -156,29 +156,30 @@ class JSFlatString;
 class JSLinearString;
 
 namespace JS {
 class Symbol;
 }
 
 namespace js {
 
+class NativeObject;
+class ArrayObject;
 class ArgumentsObject;
 class ArrayBufferObjectMaybeShared;
 class ArrayBufferObject;
 class ArrayBufferViewObject;
 class SharedArrayBufferObject;
 class SharedTypedArrayObject;
 class BaseShape;
 class DebugScopeObject;
 class GlobalObject;
 class LazyScript;
 class NestedScopeObject;
 class Nursery;
-class ObjectImpl;
 class PropertyName;
 class SavedFrame;
 class ScopeObject;
 class ScriptSourceObject;
 class Shape;
 class UnownedBaseShape;
 
 namespace types {
@@ -197,16 +198,18 @@ CurrentThreadIsIonCompiling();
 #endif
 
 bool
 StringIsPermanentAtom(JSString *str);
 
 namespace gc {
 
 template <typename T> struct MapTypeToTraceKind {};
+template <> struct MapTypeToTraceKind<NativeObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
+template <> struct MapTypeToTraceKind<ArrayObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArgumentsObject>  { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferObjectMaybeShared>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<BaseShape>        { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<GlobalObject>     { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JS::Symbol>       { static const JSGCTraceKind kind = JSTRACE_SYMBOL; };
@@ -214,17 +217,16 @@ template <> struct MapTypeToTraceKind<JS
 template <> struct MapTypeToTraceKind<JSFlatString>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSFunction>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSLinearString>   { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSObject>         { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSScript>         { static const JSGCTraceKind kind = JSTRACE_SCRIPT; };
 template <> struct MapTypeToTraceKind<JSString>         { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<LazyScript>       { static const JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
 template <> struct MapTypeToTraceKind<NestedScopeObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
-template <> struct MapTypeToTraceKind<ObjectImpl>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<PropertyName>     { static const JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<SavedFrame>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
 template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<SharedTypedArrayObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
@@ -773,16 +775,17 @@ class ReadBarriered
     T operator->() const { return get(); }
 
     T *unsafeGet() { return &value; }
     T const * unsafeGet() const { return &value; }
 
     void set(T v) { value = v; }
 };
 
+class ArrayObject;
 class ArrayBufferObject;
 class NestedScopeObject;
 class DebugScopeObject;
 class GlobalObject;
 class ScriptSourceObject;
 class Shape;
 class BaseShape;
 class UnownedBaseShape;
@@ -796,18 +799,21 @@ class TypeNewScript;
 
 typedef PreBarriered<JSObject*> PreBarrieredObject;
 typedef PreBarriered<JSScript*> PreBarrieredScript;
 typedef PreBarriered<jit::JitCode*> PreBarrieredJitCode;
 typedef PreBarriered<JSAtom*> PreBarrieredAtom;
 
 typedef RelocatablePtr<JSObject*> RelocatablePtrObject;
 typedef RelocatablePtr<JSScript*> RelocatablePtrScript;
+typedef RelocatablePtr<NativeObject*> RelocatablePtrNativeObject;
 typedef RelocatablePtr<NestedScopeObject*> RelocatablePtrNestedScopeObject;
 
+typedef HeapPtr<NativeObject*> HeapPtrNativeObject;
+typedef HeapPtr<ArrayObject*> HeapPtrArrayObject;
 typedef HeapPtr<ArrayBufferObjectMaybeShared*> HeapPtrArrayBufferObjectMaybeShared;
 typedef HeapPtr<ArrayBufferObject*> HeapPtrArrayBufferObject;
 typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
 typedef HeapPtr<JSAtom*> HeapPtrAtom;
 typedef HeapPtr<JSFlatString*> HeapPtrFlatString;
 typedef HeapPtr<JSFunction*> HeapPtrFunction;
 typedef HeapPtr<JSLinearString*> HeapPtrLinearString;
 typedef HeapPtr<JSObject*> HeapPtrObject;
--- a/js/src/gc/ForkJoinNursery.cpp
+++ b/js/src/gc/ForkJoinNursery.cpp
@@ -533,19 +533,19 @@ ForkJoinNursery::allocateObject(size_t b
 
     // Allocate slots contiguously after the object.
     size_t totalSize = baseSize + sizeof(HeapSlot) * numDynamic;
     JSObject *obj = static_cast<JSObject *>(allocate(totalSize));
     if (!obj) {
         tooLarge = false;
         return nullptr;
     }
-    obj->setInitialSlots(numDynamic
-                         ? reinterpret_cast<HeapSlot *>(size_t(obj) + baseSize)
-                         : nullptr);
+    obj->fakeNativeSetInitialSlots(numDynamic
+                                   ? reinterpret_cast<HeapSlot *>(size_t(obj) + baseSize)
+                                   : nullptr);
     return obj;
 }
 
 HeapSlot *
 ForkJoinNursery::allocateSlots(JSObject *obj, uint32_t nslots)
 {
     MOZ_ASSERT(obj);
     MOZ_ASSERT(nslots > 0);
@@ -663,22 +663,23 @@ MOZ_ALWAYS_INLINE void
 ForkJoinNursery::traceObject(ForkJoinNurseryCollectionTracer *trc, JSObject *obj)
 {
     const Class *clasp = obj->getClass();
     if (clasp->trace)
         clasp->trace(trc, obj);
 
     if (!obj->isNative())
         return;
+    NativeObject *nobj = &obj->as<NativeObject>();
 
-    if (!obj->hasEmptyElements())
-        markSlots(obj->getDenseElements(), obj->getDenseInitializedLength());
+    if (!nobj->hasEmptyElements())
+        markSlots(nobj->getDenseElements(), nobj->getDenseInitializedLength());
 
     HeapSlot *fixedStart, *fixedEnd, *dynStart, *dynEnd;
-    obj->getSlotRange(0, obj->slotSpan(), &fixedStart, &fixedEnd, &dynStart, &dynEnd);
+    nobj->getSlotRange(0, nobj->slotSpan(), &fixedStart, &fixedEnd, &dynStart, &dynEnd);
     markSlots(fixedStart, fixedEnd);
     markSlots(dynStart, dynEnd);
 }
 
 MOZ_ALWAYS_INLINE void
 ForkJoinNursery::markSlots(HeapSlot *vp, uint32_t nslots)
 {
     markSlots(vp, vp + nslots);
@@ -709,30 +710,31 @@ ForkJoinNursery::markSlot(HeapSlot *slot
     JSObject *moved = static_cast<JSObject *>(moveObjectToTospace(obj));
     slotp->unsafeGet()->setObject(*moved);
 }
 
 AllocKind
 ForkJoinNursery::getObjectAllocKind(JSObject *obj)
 {
     if (obj->is<ArrayObject>()) {
-        MOZ_ASSERT(obj->numFixedSlots() == 0);
+        ArrayObject *aobj = &obj->as<ArrayObject>();
+        MOZ_ASSERT(aobj->numFixedSlots() == 0);
 
         // Use minimal size object if we are just going to copy the pointer.
-        if (!isInsideFromspace((void *)obj->getElementsHeader()))
+        if (!isInsideFromspace((void *)aobj->getElementsHeader()))
             return FINALIZE_OBJECT0_BACKGROUND;
 
-        size_t nelements = obj->getDenseCapacity();
+        size_t nelements = aobj->getDenseCapacity();
         return GetBackgroundAllocKind(GetGCArrayKind(nelements));
     }
 
     if (obj->is<JSFunction>())
         return obj->as<JSFunction>().getAllocKind();
 
-    AllocKind kind = GetGCObjectFixedSlotsKind(obj->numFixedSlots());
+    AllocKind kind = GetGCObjectFixedSlotsKind(obj->fakeNativeNumFixedSlots());
     MOZ_ASSERT(!IsBackgroundFinalized(kind));
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, obj->getClass()));
     return GetBackgroundAllocKind(kind);
 }
 
 // Nursery allocation will never fail during GC - apart from true OOM - since
 // newspace is at least as large as fromspace, ergo a nullptr return from the
 // allocator means true OOM, which we catch and signal here.
@@ -803,17 +805,17 @@ ForkJoinNursery::copyObjectToTospace(JSO
 {
     size_t srcSize = Arena::thingSize(dstKind);
     size_t movedSize = srcSize;
 
     // Arrays do not necessarily have the same AllocKind between src and dst.
     // We deal with this by copying elements manually, possibly re-inlining
     // them if there is adequate room inline in dst.
     if (src->is<ArrayObject>())
-        srcSize = movedSize = sizeof(ObjectImpl);
+        srcSize = movedSize = sizeof(NativeObject);
 
     js_memcpy(dst, src, srcSize);
     movedSize += copySlotsToTospace(dst, src, dstKind);
     movedSize += copyElementsToTospace(dst, src, dstKind);
 
     // The shape's list head may point into the old object.
     if (&src->shape_ == dst->shape_->listp) {
         MOZ_ASSERT(cx_->isThreadLocal(dst->shape_.get()));
@@ -822,72 +824,72 @@ ForkJoinNursery::copyObjectToTospace(JSO
 
     return movedSize;
 }
 
 size_t
 ForkJoinNursery::copySlotsToTospace(JSObject *dst, JSObject *src, AllocKind dstKind)
 {
     // Fixed slots have already been copied over.
-    if (!src->hasDynamicSlots())
+    if (!src->fakeNativeHasDynamicSlots())
         return 0;
 
-    if (!isInsideFromspace(src->slots)) {
-        hugeSlots[hugeSlotsFrom].remove(src->slots);
+    if (!isInsideFromspace(src->fakeNativeSlots())) {
+        hugeSlots[hugeSlotsFrom].remove(src->fakeNativeSlots());
         if (!isEvacuating_)
-            hugeSlots[hugeSlotsNew].put(src->slots);
+            hugeSlots[hugeSlotsNew].put(src->fakeNativeSlots());
         return 0;
     }
 
-    size_t count = src->numDynamicSlots();
-    dst->slots = allocateInTospace<HeapSlot>(count);
-    if (!dst->slots)
+    size_t count = src->fakeNativeNumDynamicSlots();
+    dst->fakeNativeSlots() = allocateInTospace<HeapSlot>(count);
+    if (!dst->fakeNativeSlots())
         CrashAtUnhandlableOOM("Failed to allocate slots while moving object.");
-    js_memcpy(dst->slots, src->slots, count * sizeof(HeapSlot));
-    setSlotsForwardingPointer(src->slots, dst->slots, count);
+    js_memcpy(dst->fakeNativeSlots(), src->fakeNativeSlots(), count * sizeof(HeapSlot));
+    setSlotsForwardingPointer(src->fakeNativeSlots(), dst->fakeNativeSlots(), count);
     return count * sizeof(HeapSlot);
 }
 
 size_t
 ForkJoinNursery::copyElementsToTospace(JSObject *dst, JSObject *src, AllocKind dstKind)
 {
-    if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite())
+    if (src->fakeNativeHasEmptyElements() || src->fakeNativeDenseElementsAreCopyOnWrite())
         return 0;
 
-    ObjectElements *srcHeader = src->getElementsHeader();
+    ObjectElements *srcHeader = src->fakeNativeGetElementsHeader();
     ObjectElements *dstHeader;
 
     // TODO Bug 874151: Prefer to put element data inline if we have space.
     // (Note, not a correctness issue.)
     if (!isInsideFromspace(srcHeader)) {
-        MOZ_ASSERT(src->elements == dst->elements);
+        MOZ_ASSERT(src->fakeNativeElements() == dst->fakeNativeElements());
         hugeSlots[hugeSlotsFrom].remove(reinterpret_cast<HeapSlot*>(srcHeader));
         if (!isEvacuating_)
             hugeSlots[hugeSlotsNew].put(reinterpret_cast<HeapSlot*>(srcHeader));
         return 0;
     }
 
     size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity;
 
     // Unlike other objects, Arrays can have fixed elements.
     if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
-        dst->setFixedElements();
-        dstHeader = dst->getElementsHeader();
+        dst->as<ArrayObject>().setFixedElements();
+        dstHeader = dst->as<ArrayObject>().getElementsHeader();
         js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
         setElementsForwardingPointer(srcHeader, dstHeader, nslots);
         return nslots * sizeof(HeapSlot);
     }
 
     MOZ_ASSERT(nslots >= 2);
     dstHeader = reinterpret_cast<ObjectElements *>(allocateInTospace<HeapSlot>(nslots));
     if (!dstHeader)
         CrashAtUnhandlableOOM("Failed to allocate elements while moving object.");
     js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
     setElementsForwardingPointer(srcHeader, dstHeader, nslots);
-    dst->elements = dstHeader->elements();
+    dst->fakeNativeElements() = dstHeader->elements();
     return nslots * sizeof(HeapSlot);
 }
 
 void
 ForkJoinNursery::setSlotsForwardingPointer(HeapSlot *oldSlots, HeapSlot *newSlots, uint32_t nslots)
 {
     MOZ_ASSERT(nslots > 0);
     MOZ_ASSERT(isInsideFromspace(oldSlots));
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -178,17 +178,17 @@ class TenuredCell : public Cell
     static MOZ_ALWAYS_INLINE const TenuredCell *fromPointer(const void *ptr);
 
     // Mark bit management.
     MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
     MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const;
     MOZ_ALWAYS_INLINE void unmark(uint32_t color) const;
     MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell *src);
 
-    // Note: this is in TenuredCell because ObjectImpl subclasses are sometimes
+    // Note: this is in TenuredCell because JSObject subclasses are sometimes
     // used tagged.
     static MOZ_ALWAYS_INLINE bool isNullLike(const Cell *thing) { return !thing; }
 
     // Access to the arena header.
     inline ArenaHeader *arenaHeader() const;
     inline AllocKind getAllocKind() const;
     inline JS::Zone *zone() const;
     inline JS::Zone *zoneFromAnyThread() const;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsprf.h"
 
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/ArrayObject.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
@@ -63,17 +64,17 @@ JS_PUBLIC_DATA(void * const) JS::NullPtr
  * call a callback for each object visited. This is a recursive process; the
  * mark stacks are not involved. These callbacks may ask for the outgoing
  * pointers to be visited. Eventually, this leads to the MarkChildren functions
  * being called. These functions duplicate much of the functionality of
  * scanning functions, but they don't push onto an explicit stack.
  */
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing);
+PushMarkStack(GCMarker *gcmarker, JSObject *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSScript *thing);
 
 static inline void
@@ -251,16 +252,23 @@ template<>
 void
 SetMaybeAliveFlag(JSObject *thing)
 {
     thing->compartment()->maybeAlive = true;
 }
 
 template<>
 void
+SetMaybeAliveFlag(NativeObject *thing)
+{
+    thing->compartment()->maybeAlive = true;
+}
+
+template<>
+void
 SetMaybeAliveFlag(JSScript *thing)
 {
     thing->compartment()->maybeAlive = true;
 }
 
 template<typename T>
 static void
 MarkInternal(JSTracer *trc, T **thingp)
@@ -624,26 +632,27 @@ Update##base##IfRelocated(JSRuntime *rt,
 {                                                                                                 \
     return UpdateIfRelocated<type>(rt, thingp);                                                   \
 }
 
 
 DeclMarkerImpl(BaseShape, BaseShape)
 DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(JitCode, jit::JitCode)
+DeclMarkerImpl(Object, NativeObject)
+DeclMarkerImpl(Object, ArrayObject)
 DeclMarkerImpl(Object, ArgumentsObject)
 DeclMarkerImpl(Object, ArrayBufferObject)
 DeclMarkerImpl(Object, ArrayBufferObjectMaybeShared)
 DeclMarkerImpl(Object, ArrayBufferViewObject)
 DeclMarkerImpl(Object, DebugScopeObject)
 DeclMarkerImpl(Object, GlobalObject)
 DeclMarkerImpl(Object, JSObject)
 DeclMarkerImpl(Object, JSFunction)
 DeclMarkerImpl(Object, NestedScopeObject)
-DeclMarkerImpl(Object, ObjectImpl)
 DeclMarkerImpl(Object, SavedFrame)
 DeclMarkerImpl(Object, ScopeObject)
 DeclMarkerImpl(Object, SharedArrayBufferObject)
 DeclMarkerImpl(Object, SharedTypedArrayObject)
 DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(LazyScript, LazyScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
@@ -926,17 +935,17 @@ gc::MarkArraySlots(JSTracer *trc, size_t
 }
 
 void
 gc::MarkObjectSlots(JSTracer *trc, JSObject *obj, uint32_t start, uint32_t nslots)
 {
     MOZ_ASSERT(obj->isNative());
     for (uint32_t i = start; i < (start + nslots); ++i) {
         trc->setTracingDetails(js_GetObjectSlotName, obj, i);
-        MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet());
+        MarkValueInternal(trc, obj->fakeNativeGetSlotRef(i).unsafeGet());
     }
 }
 
 static bool
 ShouldMarkCrossCompartment(JSTracer *trc, JSObject *src, Cell *cell)
 {
     if (!IS_GC_MARKING_TRACER(trc))
         return true;
@@ -1031,17 +1040,17 @@ gc::IsCellAboutToBeFinalized(Cell **thin
     MOZ_ASSERT((thing)->zone()->isGCMarking() ||                        \
                (rt)->isAtomsZone((thing)->zone()));
 
 // Symbols can also be in the atoms zone.
 #define JS_COMPARTMENT_ASSERT_SYM(rt, sym)                              \
     JS_COMPARTMENT_ASSERT_STR(rt, sym)
 
 static void
-PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing)
+PushMarkStack(GCMarker *gcmarker, JSObject *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
     MOZ_ASSERT(!IsInsideNursery(thing));
 
     if (thing->asTenured().markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
@@ -1536,17 +1545,17 @@ struct SlotArrayLayout
     union {
         HeapSlot *end;
         uintptr_t kind;
     };
     union {
         HeapSlot *start;
         uintptr_t index;
     };
-    JSObject *obj;
+    NativeObject *obj;
 
     static void staticAsserts() {
         /* This should have the same layout as three mark stack items. */
         JS_STATIC_ASSERT(sizeof(SlotArrayLayout) == 3 * sizeof(uintptr_t));
     }
 };
 
 /*
@@ -1561,17 +1570,17 @@ void
 GCMarker::saveValueRanges()
 {
     for (uintptr_t *p = stack.tos_; p > stack.stack_; ) {
         uintptr_t tag = *--p & StackTagMask;
         if (tag == ValueArrayTag) {
             *p &= ~StackTagMask;
             p -= 2;
             SlotArrayLayout *arr = reinterpret_cast<SlotArrayLayout *>(p);
-            JSObject *obj = arr->obj;
+            NativeObject *obj = arr->obj;
             MOZ_ASSERT(obj->isNative());
 
             HeapSlot *vp = obj->getDenseElements();
             if (arr->end == vp + obj->getDenseInitializedLength()) {
                 MOZ_ASSERT(arr->start >= vp);
                 arr->index = arr->start - vp;
                 arr->kind = HeapSlot::Element;
             } else {
@@ -1592,17 +1601,17 @@ GCMarker::saveValueRanges()
             p[2] |= SavedValueArrayTag;
         } else if (tag == SavedValueArrayTag) {
             p -= 2;
         }
     }
 }
 
 bool
-GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp)
+GCMarker::restoreValueArray(NativeObject *obj, void **vpp, void **endp)
 {
     uintptr_t start = stack.pop();
     HeapSlot::Kind kind = (HeapSlot::Kind) stack.pop();
 
     if (kind == HeapSlot::Element) {
         if (!obj->is<ArrayObject>())
             return false;
 
@@ -1640,17 +1649,17 @@ GCMarker::restoreValueArray(JSObject *ob
 
 void
 GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
 {
     if (tag == TypeTag) {
         ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
     } else if (tag == SavedValueArrayTag) {
         MOZ_ASSERT(!(addr & CellMask));
-        JSObject *obj = reinterpret_cast<JSObject *>(addr);
+        NativeObject *obj = reinterpret_cast<NativeObject *>(addr);
         HeapValue *vp, *end;
         if (restoreValueArray(obj, (void **)&vp, (void **)&end))
             pushValueArray(obj, vp, end);
         else
             pushObject(obj);
     } else if (tag == JitCodeTag) {
         MarkChildren(this, reinterpret_cast<jit::JitCode *>(addr));
     }
@@ -1754,48 +1763,49 @@ GCMarker::processMarkStackTop(SliceBudge
                              !obj->isOwnGlobal())),
                           clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
             clasp->trace(this, obj);
         }
 
         if (!shape->isNative())
             return;
 
-        unsigned nslots = obj->slotSpan();
+        NativeObject *nobj = &obj->as<NativeObject>();
+        unsigned nslots = nobj->slotSpan();
 
         do {
-            if (obj->hasEmptyElements())
+            if (nobj->hasEmptyElements())
                 break;
 
-            if (obj->denseElementsAreCopyOnWrite()) {
-                JSObject *owner = obj->getElementsHeader()->ownerObject();
-                if (owner != obj) {
+            if (nobj->denseElementsAreCopyOnWrite()) {
+                JSObject *owner = nobj->getElementsHeader()->ownerObject();
+                if (owner != nobj) {
                     PushMarkStack(this, owner);
                     break;
                 }
             }
 
-            vp = obj->getDenseElementsAllowCopyOnWrite();
-            end = vp + obj->getDenseInitializedLength();
+            vp = nobj->getDenseElementsAllowCopyOnWrite();
+            end = vp + nobj->getDenseInitializedLength();
             if (!nslots)
                 goto scan_value_array;
-            pushValueArray(obj, vp, end);
+            pushValueArray(nobj, vp, end);
         } while (false);
 
-        vp = obj->fixedSlots();
-        if (obj->slots) {
-            unsigned nfixed = obj->numFixedSlots();
+        vp = nobj->fixedSlots();
+        if (nobj->slots) {
+            unsigned nfixed = nobj->numFixedSlots();
             if (nslots > nfixed) {
-                pushValueArray(obj, vp, vp + nfixed);
-                vp = obj->slots;
+                pushValueArray(nobj, vp, vp + nfixed);
+                vp = nobj->slots;
                 end = vp + (nslots - nfixed);
                 goto scan_value_array;
             }
         }
-        MOZ_ASSERT(nslots <= obj->numFixedSlots());
+        MOZ_ASSERT(nslots <= nobj->numFixedSlots());
         end = vp + nslots;
         goto scan_value_array;
     }
 }
 
 bool
 GCMarker::drainMarkStack(SliceBudget &budget)
 {
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -100,16 +100,18 @@ bool Is##base##Marked(BarrieredBase<type
 bool Is##base##AboutToBeFinalized(type **thingp);                                                 \
 bool Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp);                                  \
 type *Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp);                     \
 type *Update##base##IfRelocated(JSRuntime *rt, type **thingp);
 
 DeclMarker(BaseShape, BaseShape)
 DeclMarker(BaseShape, UnownedBaseShape)
 DeclMarker(JitCode, jit::JitCode)
+DeclMarker(Object, NativeObject)
+DeclMarker(Object, ArrayObject)
 DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, ArrayBufferObject)
 DeclMarker(Object, ArrayBufferObjectMaybeShared)
 DeclMarker(Object, ArrayBufferViewObject)
 DeclMarker(Object, DebugScopeObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
@@ -300,16 +302,23 @@ Mark(JSTracer *trc, HeapPtrJitCode *code
 
 /* For use by WeakMap's HashKeyRef instantiation. */
 inline void
 Mark(JSTracer *trc, JSObject **objp, const char *name)
 {
     MarkObjectUnbarriered(trc, objp, name);
 }
 
+/* For use by Debugger::WeakMap's missingScopes HashKeyRef instantiation. */
+inline void
+Mark(JSTracer *trc, NativeObject **obj, const char *name)
+{
+    MarkObjectUnbarriered(trc, obj, name);
+}
+
 /* For use by Debugger::WeakMap's proxiedScopes HashKeyRef instantiation. */
 inline void
 Mark(JSTracer *trc, ScopeObject **obj, const char *name)
 {
     MarkObjectUnbarriered(trc, obj, name);
 }
 
 bool
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -166,34 +166,34 @@ js::Nursery::allocateObject(JSContext *c
     /* Ensure there's enough space to replace the contents with a RelocationOverlay. */
     MOZ_ASSERT(size >= sizeof(RelocationOverlay));
 
     /* Attempt to allocate slots contiguously after object, if possible. */
     if (numDynamic && numDynamic <= MaxNurserySlots) {
         size_t totalSize = size + sizeof(HeapSlot) * numDynamic;
         JSObject *obj = static_cast<JSObject *>(allocate(totalSize));
         if (obj) {
-            obj->setInitialSlots(reinterpret_cast<HeapSlot *>(size_t(obj) + size));
+            obj->fakeNativeSetInitialSlots(reinterpret_cast<HeapSlot *>(size_t(obj) + size));
             TraceNurseryAlloc(obj, size);
             return obj;
         }
         /* If we failed to allocate as a block, retry with out-of-line slots. */
     }
 
     HeapSlot *slots = nullptr;
     if (numDynamic) {
         slots = allocateHugeSlots(cx->zone(), numDynamic);
         if (MOZ_UNLIKELY(!slots))
             return nullptr;
     }
 
     JSObject *obj = static_cast<JSObject *>(allocate(size));
 
     if (obj)
-        obj->setInitialSlots(slots);
+        obj->fakeNativeSetInitialSlots(slots);
     else
         freeSlots(slots);
 
     TraceNurseryAlloc(obj, size);
     return obj;
 }
 
 void *
@@ -362,23 +362,24 @@ class MinorCollectionTracer : public JST
 
 } /* namespace gc */
 } /* namespace js */
 
 static AllocKind
 GetObjectAllocKindForCopy(const Nursery &nursery, JSObject *obj)
 {
     if (obj->is<ArrayObject>()) {
-        MOZ_ASSERT(obj->numFixedSlots() == 0);
+        ArrayObject *aobj = &obj->as<ArrayObject>();
+        MOZ_ASSERT(aobj->numFixedSlots() == 0);
 
         /* Use minimal size object if we are just going to copy the pointer. */
-        if (!nursery.isInside(obj->getElementsHeader()))
+        if (!nursery.isInside(aobj->getElementsHeader()))
             return FINALIZE_OBJECT0_BACKGROUND;
 
-        size_t nelements = obj->getDenseCapacity();
+        size_t nelements = aobj->getDenseCapacity();
         return GetBackgroundAllocKind(GetGCArrayKind(nelements));
     }
 
     if (obj->is<JSFunction>())
         return obj->as<JSFunction>().getAllocKind();
 
     /*
      * Typed arrays in the nursery may have a lazily allocated buffer, make
@@ -394,17 +395,17 @@ GetObjectAllocKindForCopy(const Nursery 
     if (obj->is<InlineOpaqueTypedObject>()) {
         // Figure out the size of this object, from the prototype's TypeDescr.
         // The objects we are traversing here are all tenured, so we don't need
         // to check forwarding pointers.
         TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
         return InlineOpaqueTypedObject::allocKindForTypeDescriptor(descr);
     }
 
-    AllocKind kind = GetGCObjectFixedSlotsKind(obj->numFixedSlots());
+    AllocKind kind = GetGCObjectFixedSlotsKind(obj->fakeNativeNumFixedSlots());
     MOZ_ASSERT(!IsBackgroundFinalized(kind));
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, obj->getClass()));
     return GetBackgroundAllocKind(kind);
 }
 
 MOZ_ALWAYS_INLINE void *
 js::Nursery::allocateFromTenured(Zone *zone, AllocKind thingKind)
 {
@@ -513,24 +514,25 @@ js::Nursery::traceObject(MinorCollection
 {
     const Class *clasp = obj->getClass();
     if (clasp->trace)
         clasp->trace(trc, obj);
 
     MOZ_ASSERT(obj->isNative() == clasp->isNative());
     if (!clasp->isNative())
         return;
+    NativeObject *nobj = &obj->as<NativeObject>();
 
     // Note: the contents of copy on write elements pointers are filled in
     // during parsing and cannot contain nursery pointers.
-    if (!obj->hasEmptyElements() && !obj->denseElementsAreCopyOnWrite())
-        markSlots(trc, obj->getDenseElements(), obj->getDenseInitializedLength());
+    if (!nobj->hasEmptyElements() && !nobj->denseElementsAreCopyOnWrite())
+        markSlots(trc, nobj->getDenseElements(), nobj->getDenseInitializedLength());
 
     HeapSlot *fixedStart, *fixedEnd, *dynStart, *dynEnd;
-    obj->getSlotRange(0, obj->slotSpan(), &fixedStart, &fixedEnd, &dynStart, &dynEnd);
+    nobj->getSlotRange(0, nobj->slotSpan(), &fixedStart, &fixedEnd, &dynStart, &dynEnd);
     markSlots(trc, fixedStart, fixedEnd);
     markSlots(trc, dynStart, dynEnd);
 }
 
 MOZ_ALWAYS_INLINE void
 js::Nursery::markSlots(MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots)
 {
     markSlots(trc, vp, vp + nslots);
@@ -592,44 +594,44 @@ js::Nursery::moveObjectToTenured(JSObjec
      * We deal with this by copying elements manually, possibly re-inlining
      * them if there is adequate room inline in dst.
      *
      * For Arrays we're reducing tenuredSize to the smaller srcSize
      * because moveElementsToTenured() accounts for all Array elements,
      * even if they are inlined.
      */
     if (src->is<ArrayObject>())
-        tenuredSize = srcSize = sizeof(ObjectImpl);
+        tenuredSize = srcSize = sizeof(NativeObject);
 
     js_memcpy(dst, src, srcSize);
     tenuredSize += moveSlotsToTenured(dst, src, dstKind);
     tenuredSize += moveElementsToTenured(dst, src, dstKind);
 
     if (src->is<TypedArrayObject>())
-        forwardTypedArrayPointers(dst, src);
+        forwardTypedArrayPointers(&dst->as<TypedArrayObject>(), &src->as<TypedArrayObject>());
 
     /* The shape's list head may point into the old object. */
     if (&src->shape_ == dst->shape_->listp)
         dst->shape_->listp = &dst->shape_;
 
     return tenuredSize;
 }
 
 void
-js::Nursery::forwardTypedArrayPointers(JSObject *dst, JSObject *src)
+js::Nursery::forwardTypedArrayPointers(TypedArrayObject *dst, TypedArrayObject *src)
 {
     /*
      * Typed array data may be stored inline inside the object's fixed slots. If
      * so, we need update the private pointer and leave a forwarding pointer at
      * the start of the data.
      */
-    TypedArrayObject &typedArray = src->as<TypedArrayObject>();
-    MOZ_ASSERT_IF(typedArray.buffer(), !isInside(src->getPrivate()));
-    if (typedArray.buffer())
+    if (src->buffer()) {
+        MOZ_ASSERT(!isInside(src->getPrivate()));
         return;
+    }
 
     void *srcData = src->fixedData(TypedArrayObject::FIXED_DATA_START);
     void *dstData = dst->fixedData(TypedArrayObject::FIXED_DATA_START);
     MOZ_ASSERT(src->getPrivate() == srcData);
     dst->setPrivate(dstData);
 
     /*
      * We don't know the number of slots here, but
@@ -641,69 +643,69 @@ js::Nursery::forwardTypedArrayPointers(J
                               reinterpret_cast<HeapSlot*>(dstData),
                               nslots);
 }
 
 MOZ_ALWAYS_INLINE size_t
 js::Nursery::moveSlotsToTenured(JSObject *dst, JSObject *src, AllocKind dstKind)
 {
     /* Fixed slots have already been copied over. */
-    if (!src->hasDynamicSlots())
+    if (!src->fakeNativeHasDynamicSlots())
         return 0;
 
-    if (!isInside(src->slots)) {
-        hugeSlots.remove(src->slots);
+    if (!isInside(src->fakeNativeSlots())) {
+        hugeSlots.remove(src->fakeNativeSlots());
         return 0;
     }
 
     Zone *zone = src->zone();
-    size_t count = src->numDynamicSlots();
-    dst->slots = zone->pod_malloc<HeapSlot>(count);
-    if (!dst->slots)
+    size_t count = src->fakeNativeNumDynamicSlots();
+    dst->fakeNativeSlots() = zone->pod_malloc<HeapSlot>(count);
+    if (!dst->fakeNativeSlots())
         CrashAtUnhandlableOOM("Failed to allocate slots while tenuring.");
-    PodCopy(dst->slots, src->slots, count);
-    setSlotsForwardingPointer(src->slots, dst->slots, count);
+    PodCopy(dst->fakeNativeSlots(), src->fakeNativeSlots(), count);
+    setSlotsForwardingPointer(src->fakeNativeSlots(), dst->fakeNativeSlots(), count);
     return count * sizeof(HeapSlot);
 }
 
 MOZ_ALWAYS_INLINE size_t
 js::Nursery::moveElementsToTenured(JSObject *dst, JSObject *src, AllocKind dstKind)
 {
-    if (src->hasEmptyElements() || src->denseElementsAreCopyOnWrite())
+    if (src->fakeNativeHasEmptyElements() || src->fakeNativeDenseElementsAreCopyOnWrite())
         return 0;
 
     Zone *zone = src->zone();
-    ObjectElements *srcHeader = src->getElementsHeader();
+    ObjectElements *srcHeader = src->fakeNativeGetElementsHeader();
     ObjectElements *dstHeader;
 
     /* TODO Bug 874151: Prefer to put element data inline if we have space. */
     if (!isInside(srcHeader)) {
-        MOZ_ASSERT(src->elements == dst->elements);
+        MOZ_ASSERT(src->fakeNativeElements() == dst->fakeNativeElements());
         hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader));
         return 0;
     }
 
     size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity;
 
     /* Unlike other objects, Arrays can have fixed elements. */
     if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
-        dst->setFixedElements();
-        dstHeader = dst->getElementsHeader();
+        dst->as<ArrayObject>().setFixedElements();
+        dstHeader = dst->as<ArrayObject>().getElementsHeader();
         js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
         setElementsForwardingPointer(srcHeader, dstHeader, nslots);
         return nslots * sizeof(HeapSlot);
     }
 
     MOZ_ASSERT(nslots >= 2);
     dstHeader = reinterpret_cast<ObjectElements *>(zone->pod_malloc<HeapSlot>(nslots));
     if (!dstHeader)
         CrashAtUnhandlableOOM("Failed to allocate elements while tenuring.");
     js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
     setElementsForwardingPointer(srcHeader, dstHeader, nslots);
-    dst->elements = dstHeader->elements();
+    dst->fakeNativeElements() = dstHeader->elements();
     return nslots * sizeof(HeapSlot);
 }
 
 static bool
 ShouldMoveToTenured(MinorCollectionTracer *trc, void **thingp)
 {
     Cell *cell = static_cast<Cell *>(*thingp);
     Nursery &nursery = *trc->nursery;
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -23,16 +23,17 @@
 #include "js/Vector.h"
 
 namespace JS {
 struct Zone;
 }
 
 namespace js {
 
+class TypedArrayObject;
 class ObjectElements;
 class HeapSlot;
 void SetGCZeal(JSRuntime *, uint8_t, uint32_t);
 
 namespace gc {
 struct Cell;
 class Collector;
 class MinorCollectionTracer;
@@ -281,17 +282,17 @@ class Nursery
     MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src);
     MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots);
     MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, HeapSlot *end);
     MOZ_ALWAYS_INLINE void markSlot(gc::MinorCollectionTracer *trc, HeapSlot *slotp);
     void *moveToTenured(gc::MinorCollectionTracer *trc, JSObject *src);
     size_t moveObjectToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
     size_t moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
     size_t moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind);
-    void forwardTypedArrayPointers(JSObject *dst, JSObject *src);
+    void forwardTypedArrayPointers(TypedArrayObject *dst, TypedArrayObject *src);
 
     /* Handle relocation of slots/elements pointers stored in Ion frames. */
     void setSlotsForwardingPointer(HeapSlot *oldSlots, HeapSlot *newSlots, uint32_t nslots);
     void setElementsForwardingPointer(ObjectElements *oldHeader, ObjectElements *newHeader,
                                       uint32_t nelems);
 
     /* Free malloced pointers owned by freed things in the nursery. */
     void freeHugeSlots();
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -10,35 +10,42 @@
 #include "js/RootingAPI.h"
 
 class JSAtom;
 class JSLinearString;
 
 namespace js {
 
 class PropertyName;
+class NativeObject;
+class ArrayObject;
 class ScriptSourceObject;
 class Shape;
 
 namespace types { struct TypeObject; }
 
 // These are internal counterparts to the public types such as HandleObject.
 
+typedef JS::Handle<NativeObject*>      HandleNativeObject;
 typedef JS::Handle<Shape*>             HandleShape;
 typedef JS::Handle<types::TypeObject*> HandleTypeObject;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<JSLinearString*>    HandleLinearString;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
+typedef JS::Handle<ArrayObject*>       HandleArrayObject;
 typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
+typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
 
+typedef JS::Rooted<NativeObject*>      RootedNativeObject;
 typedef JS::Rooted<Shape*>             RootedShape;
 typedef JS::Rooted<types::TypeObject*> RootedTypeObject;
 typedef JS::Rooted<JSAtom*>            RootedAtom;
 typedef JS::Rooted<JSLinearString*>    RootedLinearString;
 typedef JS::Rooted<PropertyName*>      RootedPropertyName;
+typedef JS::Rooted<ArrayObject*>       RootedArrayObject;
 typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -32,24 +32,24 @@ StoreBuffer::SlotsEdge::mark(JSTracer *t
     if (!obj->isNative()) {
         const Class *clasp = obj->getClass();
         if (clasp)
             clasp->trace(trc, obj);
         return;
     }
 
     if (kind() == ElementKind) {
-        int32_t initLen = obj->getDenseInitializedLength();
+        int32_t initLen = obj->fakeNativeGetDenseInitializedLength();
         int32_t clampedStart = Min(start_, initLen);
         int32_t clampedEnd = Min(start_ + count_, initLen);
         gc::MarkArraySlots(trc, clampedEnd - clampedStart,
-                           obj->getDenseElements() + clampedStart, "element");
+                           obj->fakeNativeGetDenseElements() + clampedStart, "element");
     } else {
-        int32_t start = Min(uint32_t(start_), obj->slotSpan());
-        int32_t end = Min(uint32_t(start_) + count_, obj->slotSpan());
+        int32_t start = Min(uint32_t(start_), obj->fakeNativeSlotSpan());
+        int32_t end = Min(uint32_t(start_) + count_, obj->fakeNativeSlotSpan());
         MOZ_ASSERT(end >= start);
         MarkObjectSlots(trc, obj, start, end - start);
     }
 }
 
 void
 StoreBuffer::WholeCellEdges::mark(JSTracer *trc)
 {
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -199,17 +199,17 @@ JS_GetTraceThingInfo(char *buf, size_t b
             if (obj->is<JSFunction>()) {
                 JSFunction *fun = &obj->as<JSFunction>();
                 if (fun->displayAtom()) {
                     *buf++ = ' ';
                     bufsize--;
                     PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
                 }
             } else if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) {
-                JS_snprintf(buf, bufsize, " %p", obj->getPrivate());
+                JS_snprintf(buf, bufsize, " %p", obj->fakeNativeGetPrivate());
             } else {
                 JS_snprintf(buf, bufsize, " <no private>");
             }
             break;
           }
 
           case JSTRACE_STRING:
           {
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -9,18 +9,18 @@
 
 #include "mozilla/DebugOnly.h"
 
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/TracingAPI.h"
 
 namespace js {
+class NativeObject;
 class GCMarker;
-class ObjectImpl;
 namespace gc {
 struct ArenaHeader;
 }
 namespace jit {
 class JitCode;
 }
 namespace types {
 struct TypeObject;
@@ -134,17 +134,17 @@ class GCMarker : public JSTracer
 
     void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
     size_t maxCapacity() const { return stack.maxCapacity(); }
 
     void start();
     void stop();
     void reset();
 
-    void pushObject(ObjectImpl *obj) {
+    void pushObject(JSObject *obj) {
         pushTaggedPtr(ObjectTag, obj);
     }
 
     void pushType(types::TypeObject *type) {
         pushTaggedPtr(TypeTag, type);
     }
 
     void pushJitCode(jit::JitCode *code) {
@@ -265,17 +265,17 @@ class GCMarker : public JSTracer
         if (!stack.push(endAddr, startAddr, tagged))
             delayMarkingChildren(obj);
     }
 
     bool isMarkStackEmpty() {
         return stack.isEmpty();
     }
 
-    bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
+    bool restoreValueArray(NativeObject *obj, void **vpp, void **endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget &budget);
     void processMarkStackOther(uintptr_t tag, uintptr_t addr);
 
     void appendGrayRoot(void *thing, JSGCTraceKind kind);
 
     /* The color is only applied to objects and functions. */
     uint32_t color;
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -473,17 +473,17 @@ PostVerifierVisitEdge(JSTracer *jstrc, v
     MOZ_ASSERT(!trc->runtime()->gc.nursery.isInside(thingp));
     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
     if (!IsInsideNursery(dst))
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the JS_TraceChildren
-     * below. Since ObjectImpl::markChildren handles this, the real trace
+     * below. Since JSObject::markChildren handles this, the real trace
      * location will be set correctly in these cases.
      */
     void **loc = trc->tracingLocation(thingp);
 
     AssertStoreBufferContainsEdge(trc->edges, loc, dst);
 }
 #endif
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -150,17 +150,17 @@ Zone::sweepBreakpoints(FreeOp *fop)
         for (unsigned i = 0; i < script->length(); i++) {
             BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i));
             if (!site)
                 continue;
 
             Breakpoint *nextbp;
             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
-                HeapPtrObject &dbgobj = bp->debugger->toJSObjectRef();
+                HeapPtrNativeObject &dbgobj = bp->debugger->toJSObjectRef();
                 MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
                               dbgobj->zone()->isGCSweeping());
                 bool dying = scriptGone || IsObjectAboutToBeFinalized(&dbgobj);
                 MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
                 if (dying)
                     bp->destroy(fop);
             }
         }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -18,16 +18,17 @@
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/TraceLogging.h"
 
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
+#include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script)
   : BaselineCompilerSpecific(cx, alloc, script),
     modifiesArguments_(false)
 {
@@ -1253,17 +1254,17 @@ BaselineCompiler::emit_JSOP_DOUBLE()
 
 bool
 BaselineCompiler::emit_JSOP_STRING()
 {
     frame.push(StringValue(script->getAtom(pc)));
     return true;
 }
 
-typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
+typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 bool
 BaselineCompiler::emit_JSOP_OBJECT()
 {
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
@@ -1641,30 +1642,30 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
         if (!type)
             return false;
     }
 
     // Pass length in R0, type in R1.
     masm.move32(Imm32(length), R0.scratchReg());
     masm.movePtr(ImmGCPtr(type), R1.scratchReg());
 
-    JSObject *templateObject = NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject);
+    ArrayObject *templateObject = NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject);
     if (!templateObject)
         return false;
     templateObject->setType(type);
 
     ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     frame.push(R0);
     return true;
 }
 
-typedef JSObject *(*NewArrayCopyOnWriteFn)(JSContext *, HandleObject, gc::InitialHeap);
+typedef JSObject *(*NewArrayCopyOnWriteFn)(JSContext *, HandleNativeObject, gc::InitialHeap);
 const VMFunction jit::NewArrayCopyOnWriteInfo =
     FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray);
 
 bool
 BaselineCompiler::emit_JSOP_NEWARRAY_COPYONWRITE()
 {
     RootedScript scriptRoot(cx, script);
     JSObject *obj = types::GetOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
@@ -1712,18 +1713,18 @@ BaselineCompiler::emit_JSOP_NEWOBJECT()
 
     RootedTypeObject type(cx);
     if (!types::UseNewTypeForInitializer(script, pc, JSProto_Object)) {
         type = types::TypeScript::InitObject(cx, script, pc, JSProto_Object);
         if (!type)
             return false;
     }
 
-    RootedObject baseObject(cx, script->getObject(pc));
-    RootedObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject));
+    RootedNativeObject baseObject(cx, script->getObject(pc));
+    RootedNativeObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject));
     if (!templateObject)
         return false;
 
     if (type) {
         templateObject->setType(type);
     } else {
         if (!JSObject::setSingletonType(cx, templateObject))
             return false;
@@ -1750,29 +1751,29 @@ BaselineCompiler::emit_JSOP_NEWINIT()
             return false;
     }
 
     if (key == JSProto_Array) {
         // Pass length in R0, type in R1.
         masm.move32(Imm32(0), R0.scratchReg());
         masm.movePtr(ImmGCPtr(type), R1.scratchReg());
 
-        JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
+        ArrayObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
         if (!templateObject)
             return false;
         templateObject->setType(type);
 
         ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
         if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
             return false;
     } else {
         MOZ_ASSERT(key == JSProto_Object);
 
-        RootedObject templateObject(cx);
-        templateObject = NewBuiltinClassInstance(cx, &JSObject::class_, TenuredObject);
+        RootedNativeObject templateObject(cx);
+        templateObject = NewNativeBuiltinClassInstance(cx, &JSObject::class_, TenuredObject);
         if (!templateObject)
             return false;
 
         if (type) {
             templateObject->setType(type);
         } else {
             if (!JSObject::setSingletonType(cx, templateObject))
                 return false;
@@ -2098,21 +2099,21 @@ BaselineCompiler::getScopeCoordinateObje
 Address
 BaselineCompiler::getScopeCoordinateAddressFromObject(Register objReg, Register reg)
 {
     ScopeCoordinate sc(pc);
     Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc);
 
     Address addr;
     if (shape->numFixedSlots() <= sc.slot()) {
-        masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), reg);
+        masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), reg);
         return Address(reg, (sc.slot() - shape->numFixedSlots()) * sizeof(Value));
     }
 
-    return Address(objReg, JSObject::getFixedSlotOffset(sc.slot()));
+    return Address(objReg, NativeObject::getFixedSlotOffset(sc.slot()));
 }
 
 Address
 BaselineCompiler::getScopeCoordinateAddress(Register reg)
 {
     getScopeCoordinateObject(reg);
     return getScopeCoordinateAddressFromObject(reg, reg);
 }
@@ -3212,17 +3213,17 @@ BaselineCompiler::emit_JSOP_RUNONCE()
     return callVM(RunOnceScriptPrologueInfo);
 }
 
 bool
 BaselineCompiler::emit_JSOP_REST()
 {
     frame.syncStack(0);
 
-    JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
+    ArrayObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
     if (!templateObject)
         return false;
     types::FixRestArgumentsType(cx, templateObject);
 
     // Call IC.
     ICRest_Fallback::Compiler compiler(cx, templateObject);
     if (!emitOpIC(compiler.getStub(&stubSpace_)))
         return false;
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1743,17 +1743,17 @@ ICNewArray_Fallback::Compiler::generateS
 // NewObject_Fallback
 //
 
 static bool
 DoNewObject(JSContext *cx, ICNewObject_Fallback *stub, MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "NewObject");
 
-    RootedObject templateObject(cx, stub->templateObject());
+    RootedNativeObject templateObject(cx, stub->templateObject());
     JSObject *obj = NewInitObject(cx, templateObject);
     if (!obj)
         return false;
 
     res.setObject(*obj);
     return true;
 }
 
@@ -3184,23 +3184,23 @@ ICUnaryArith_Double::Compiler::generateS
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
 // GetElem_Fallback
 //
 
-static void GetFixedOrDynamicSlotOffset(HandleObject obj, uint32_t slot,
+static void GetFixedOrDynamicSlotOffset(NativeObject *obj, uint32_t slot,
                                         bool *isFixed, uint32_t *offset)
 {
     MOZ_ASSERT(isFixed);
     MOZ_ASSERT(offset);
     *isFixed = obj->isFixedSlot(slot);
-    *offset = *isFixed ? JSObject::getFixedSlotOffset(slot)
+    *offset = *isFixed ? NativeObject::getFixedSlotOffset(slot)
                        : obj->dynamicSlotIndex(slot) * sizeof(Value);
 }
 
 static JSObject *
 GetDOMProxyProto(JSObject *obj)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     return obj->getTaggedProto().toObjectOrNull();
@@ -3216,17 +3216,17 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
                        GeneralRegisterSet &domProxyRegSet,
                        Label *checkFailed)
 {
     // Guard the following:
     //      1. The object is a DOMProxy.
     //      2. The object does not have expando properties, or has an expando
     //          which is known to not have the desired property.
     Address handlerAddr(object, ProxyObject::offsetOfHandler());
-    Address expandoAddr(object, JSObject::getFixedSlotOffset(GetDOMProxyExpandoSlot()));
+    Address expandoAddr(object, NativeObject::getFixedSlotOffset(GetDOMProxyExpandoSlot()));
 
     // Check that object is a DOMProxy.
     masm.loadPtr(checkProxyHandlerAddr, scratch);
     masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, scratch, checkFailed);
 
     // At this point, if not checking for an expando object, just return.
     if (!checkExpandoShapeAddr)
         return;
@@ -3328,17 +3328,17 @@ EffectlesslyLookupProperty(JSContext *cx
     } else if (!obj->isNative()) {
         return true;
     }
 
     if (checkObj->hasIdempotentProtoChain()) {
         if (!JSObject::lookupProperty(cx, checkObj, name, holder, shape))
             return false;
     } else if (checkObj->isNative()) {
-        shape.set(checkObj->nativeLookup(cx, NameToId(name)));
+        shape.set(checkObj->as<NativeObject>().lookup(cx, NameToId(name)));
         if (shape)
             holder.set(checkObj);
     }
     return true;
 }
 
 static bool
 CheckHasNoSuchProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
@@ -3347,17 +3347,17 @@ CheckHasNoSuchProperty(JSContext *cx, Ha
     MOZ_ASSERT(protoChainDepthOut != nullptr);
 
     size_t depth = 0;
     RootedObject curObj(cx, obj);
     while (curObj) {
         if (!curObj->isNative())
             return false;
 
-        Shape *shape = curObj->nativeLookup(cx, NameToId(name));
+        Shape *shape = curObj->as<NativeObject>().lookup(cx, NameToId(name));
         if (shape)
             return false;
 
         JSObject *proto = curObj->getTaggedProto().toObjectOrNull();
         if (!proto)
             break;
 
         curObj = proto;
@@ -3505,30 +3505,30 @@ IsCacheableSetPropAddSlot(JSContext *cx,
     // are native, and that all prototypes have setter defined on the property
     for (JSObject *proto = obj->getProto(); proto; proto = proto->getProto()) {
         chainDepth++;
         // 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
-        Shape *protoShape = proto->nativeLookup(cx, id);
+        Shape *protoShape = proto->as<NativeObject>().lookup(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;
     }
 
     // Only add a IC entry if the dynamic slots didn't change when the shapes
     // changed.  Need to ensure that a shape change for a subsequent object
     // won't involve reallocating the slot array.
-    if (obj->numDynamicSlots() != oldSlots)
+    if (obj->as<NativeObject>().numDynamicSlots() != oldSlots)
         return false;
 
     *protoChainDepth = chainDepth;
     return true;
 }
 
 static bool
 IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape, bool *isScripted)
@@ -3777,17 +3777,18 @@ static bool TryAttachNativeGetElemStub(J
         if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
             return true;
 
         // Remove any existing stubs that may interfere with the new stub being added.
         RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(holder, shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(),
+                                    shape->slot(), &isFixedSlot, &offset);
 
         ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
         ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlot
                                             : ICStub::GetElem_NativePrototypeSlot;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native %s%s slot) stub "
                                     "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
                     (obj == holder) ? "direct" : "prototype",
@@ -4321,17 +4322,17 @@ ICGetElemNativeCompiler::generateStubCod
     if (acctype_ == ICGetElemNativeStub::DynamicSlot ||
         acctype_ == ICGetElemNativeStub::FixedSlot)
     {
         masm.load32(Address(BaselineStubReg, ICGetElemNativeSlotStub::offsetOfOffset()),
                     scratchReg);
 
         // Load from object.
         if (acctype_ == ICGetElemNativeStub::DynamicSlot)
-            masm.addPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
+            masm.addPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
         else
             masm.addPtr(holderReg, scratchReg);
 
         Address valAddr(scratchReg, 0);
 
         // Check if __noSuchMethod__ needs to be called.
 #if JS_HAS_NO_SUCH_METHOD
         if (isCallElem_) {
@@ -4496,17 +4497,17 @@ ICGetElem_Dense::Compiler::generateStubC
     Register scratchReg = regs.takeAny();
 
     // Unbox R0 and shape guard.
     Register obj = masm.extractObject(R0, ExtractTemp0);
     masm.loadPtr(Address(BaselineStubReg, ICGetElem_Dense::offsetOfShape()), scratchReg);
     masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
 
     // Load obj->elements.
-    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), scratchReg);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
 
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     // Bounds check.
     Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
 
@@ -4897,31 +4898,31 @@ RemoveExistingTypedArraySetElemStub(JSCo
         MOZ_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds());
         iter.unlink(cx);
         return true;
     }
     return false;
 }
 
 static bool
-CanOptimizeDenseSetElem(JSContext *cx, HandleObject obj, uint32_t index,
-                        HandleShape oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
+CanOptimizeDenseSetElem(NativeObject *obj, uint32_t index,
+                        Shape *oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
                         bool *isAddingCaseOut, size_t *protoDepthOut)
 {
     uint32_t initLength = obj->getDenseInitializedLength();
     uint32_t capacity = obj->getDenseCapacity();
 
     *isAddingCaseOut = false;
     *protoDepthOut = 0;
 
     // Some initial sanity checks.
     if (initLength < oldInitLength || capacity < oldCapacity)
         return false;
 
-    RootedShape shape(cx, obj->lastProperty());
+    Shape *shape = obj->lastProperty();
 
     // Cannot optimize if the shape changed.
     if (oldShape != shape)
         return false;
 
     // Cannot optimize if the capacity changed.
     if (oldCapacity != capacity)
         return false;
@@ -4945,17 +4946,17 @@ CanOptimizeDenseSetElem(JSContext *cx, H
         return false;
     if (index != oldInitLength)
         return false;
 
     // The checks are not complete.  The object may have a setter definition,
     // either directly, or via a prototype, or via the target object for a prototype
     // which is a proxy, that handles a particular integer write.
     // Scan the prototype and shape chain to make sure that this is not the case.
-    RootedObject curObj(cx, obj);
+    JSObject *curObj = obj;
     while (curObj) {
         // Ensure object is native.
         if (!curObj->isNative())
             return false;
 
         // Ensure all indexed properties are stored in dense elements.
         if (curObj->isIndexed())
             return false;
@@ -4995,18 +4996,18 @@ DoSetElemFallback(JSContext *cx, Baselin
         return false;
 
     RootedShape oldShape(cx, obj->lastProperty());
 
     // Check the old capacity
     uint32_t oldCapacity = 0;
     uint32_t oldInitLength = 0;
     if (obj->isNative() && index.isInt32() && index.toInt32() >= 0) {
-        oldCapacity = obj->getDenseCapacity();
-        oldInitLength = obj->getDenseInitializedLength();
+        oldCapacity = obj->as<NativeObject>().getDenseCapacity();
+        oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
     }
 
     if (op == JSOP_INITELEM) {
         if (!InitElemOperation(cx, obj, index, rhs))
             return false;
     } else if (op == JSOP_INITELEM_ARRAY) {
         MOZ_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
         if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
@@ -5037,17 +5038,18 @@ DoSetElemFallback(JSContext *cx, Baselin
     if (obj->isNative() &&
         !IsAnyTypedArray(obj.get()) &&
         index.isInt32() && index.toInt32() >= 0 &&
         !rhs.isMagic(JS_ELEMENTS_HOLE))
     {
         bool addingCase;
         size_t protoDepth;
 
-        if (CanOptimizeDenseSetElem(cx, obj, index.toInt32(), oldShape, oldCapacity, oldInitLength,
+        if (CanOptimizeDenseSetElem(&obj->as<NativeObject>(), index.toInt32(),
+                                    oldShape, oldCapacity, oldInitLength,
                                     &addingCase, &protoDepth))
         {
             RootedShape shape(cx, obj->lastProperty());
             RootedTypeObject type(cx, obj->getType(cx));
             if (!type)
                 return false;
 
             if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, obj)) {
@@ -5224,17 +5226,17 @@ ICSetElem_Dense::Compiler::generateStubC
     regs = availableGeneralRegs(2);
     scratchReg = regs.takeAny();
 
     // Unbox object and key.
     obj = masm.extractObject(R0, ExtractTemp0);
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     // Load obj->elements in scratchReg.
-    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), scratchReg);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
 
     // Bounds check.
     Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
 
     // Hole check.
     BaseIndex element(scratchReg, key, TimesEight);
     masm.branchTestMagic(Assembler::Equal, element, &failure);
@@ -5407,17 +5409,17 @@ ICSetElemDenseAddCompiler::generateStubC
     regs = availableGeneralRegs(2);
     scratchReg = regs.takeAny();
 
     // Unbox obj and key.
     obj = masm.extractObject(R0, ExtractTemp0);
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
     // Load obj->elements in scratchReg.
-    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), scratchReg);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
 
     // Bounds check (key == initLength)
     Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::NotEqual, initLength, key, &failure);
 
     // Capacity check.
     Address capacity(scratchReg, ObjectElements::offsetOfCapacity());
     masm.branch32(Assembler::BelowOrEqual, capacity, key, &failure);
@@ -5664,29 +5666,29 @@ ICIn_Fallback::Compiler::generateStubCod
     masm.push(BaselineStubReg);
 
     return tailCallVM(DoInFallbackInfo, masm);
 }
 
 // Attach an optimized stub for a GETGNAME/CALLGNAME op.
 static bool
 TryAttachGlobalNameStub(JSContext *cx, HandleScript script, jsbytecode *pc,
-                        ICGetName_Fallback *stub, HandleObject global,
+                        ICGetName_Fallback *stub, Handle<GlobalObject*> global,
                         HandlePropertyName name)
 {
     MOZ_ASSERT(global->is<GlobalObject>());
 
     RootedId id(cx, NameToId(name));
 
     // Instantiate this global property, for use during Ion compilation.
     if (IsIonEnabled(cx))
         types::EnsureTrackPropertyTypes(cx, global, NameToId(name));
 
     // The property must be found, and it must be found as a normal data property.
-    RootedShape shape(cx, global->nativeLookup(cx, id));
+    RootedShape shape(cx, global->lookup(cx, id));
     if (!shape)
         return true;
 
     if (shape->hasDefaultGetter() && shape->hasSlot()) {
 
         MOZ_ASSERT(shape->slot() >= global->numFixedSlots());
         uint32_t slot = shape->slot() - global->numFixedSlots();
 
@@ -5735,45 +5737,46 @@ TryAttachScopeNameStub(JSContext *cx, Ha
     RootedObject scopeChain(cx, initialScopeChain);
 
     Shape *shape = nullptr;
     while (scopeChain) {
         if (!shapes.append(scopeChain->lastProperty()))
             return false;
 
         if (scopeChain->is<GlobalObject>()) {
-            shape = scopeChain->nativeLookup(cx, id);
+            shape = scopeChain->as<GlobalObject>().lookup(cx, id);
             if (shape)
                 break;
             return true;
         }
 
         if (!scopeChain->is<ScopeObject>() || scopeChain->is<DynamicWithObject>())
             return true;
 
         // Check for an 'own' property on the scope. There is no need to
         // check the prototype as non-with scopes do not inherit properties
         // from any prototype.
-        shape = scopeChain->nativeLookup(cx, id);
+        shape = scopeChain->as<NativeObject>().lookup(cx, id);
         if (shape)
             break;
 
         scopeChain = scopeChain->enclosingScope();
     }
 
     // We don't handle getters here. When this changes, we need to make sure
     // IonBuilder::getPropTryCommonGetter (which requires a Baseline stub to
     // work) handles non-outerized this objects correctly.
 
     if (!IsCacheableGetPropReadSlot(scopeChain, scopeChain, shape))
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
-    GetFixedOrDynamicSlotOffset(scopeChain, shape->slot(), &isFixedSlot, &offset);
+    GetFixedOrDynamicSlotOffset(&scopeChain->as<NativeObject>(),
+                                shape->slot(), &isFixedSlot, &offset);
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     ICStub *newStub;
 
     switch (shapes.length()) {
       case 1: {
         ICGetName_Scope<0>::Compiler compiler(cx, monitorStub, &shapes, isFixedSlot, offset);
         newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -5856,17 +5859,17 @@ DoGetNameFallback(JSContext *cx, Baselin
 
     // Attach new stub.
     if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic stub.
         return true;
     }
 
     if (js_CodeSpec[*pc].format & JOF_GNAME) {
-        if (!TryAttachGlobalNameStub(cx, script, pc, stub, scopeChain, name))
+        if (!TryAttachGlobalNameStub(cx, script, pc, stub, scopeChain.as<GlobalObject>(), name))
             return false;
     } else {
         if (!TryAttachScopeNameStub(cx, script, stub, scopeChain, name))
             return false;
     }
 
     return true;
 }
@@ -5896,17 +5899,17 @@ ICGetName_Global::Compiler::generateStub
     Register obj = R0.scratchReg();
     Register scratch = R1.scratchReg();
 
     // Shape guard.
     masm.loadPtr(Address(BaselineStubReg, ICGetName_Global::offsetOfShape()), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, obj, scratch, &failure);
 
     // Load dynamic slot.
-    masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), obj);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
     masm.load32(Address(BaselineStubReg, ICGetName_Global::offsetOfSlot()), scratch);
     masm.loadValue(BaseIndex(obj, scratch, TimesEight), R0);
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
@@ -5936,17 +5939,17 @@ ICGetName_Scope<NumHops>::Compiler::gene
 
         if (index < numHops)
             masm.extractObject(Address(scope, ScopeObject::offsetOfEnclosingScope()), walker);
     }
 
     Register scope = NumHops ? walker : obj;
 
     if (!isFixedSlot_) {
-        masm.loadPtr(Address(scope, JSObject::offsetOfSlots()), walker);
+        masm.loadPtr(Address(scope, NativeObject::offsetOfSlots()), walker);
         scope = walker;
     }
 
     masm.load32(Address(BaselineStubReg, ICGetName_Scope::offsetOfOffset()), scratch);
     masm.loadValue(BaseIndex(scope, scratch, TimesOne), R0);
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
@@ -6180,17 +6183,17 @@ TryAttachLengthStub(JSContext *cx, JSScr
 
     return true;
 }
 
 static bool
 UpdateExistingGenerationalDOMProxyStub(ICGetProp_Fallback *stub,
                                        HandleObject obj)
 {
-    Value expandoSlot = obj->getFixedSlot(GetDOMProxyExpandoSlot());
+    Value expandoSlot = obj->fakeNativeGetReservedSlot(GetDOMProxyExpandoSlot());
     MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
     ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate();
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->isGetProp_CallDOMProxyWithGenerationNative()) {
             ICGetProp_CallDOMProxyWithGenerationNative* updateStub =
                 iter->toGetProp_CallDOMProxyWithGenerationNative();
             if (updateStub->expandoAndGeneration() == expandoAndGeneration) {
                 // Update generation
@@ -6267,17 +6270,17 @@ TryAttachNativeGetPropStub(JSContext *cx
         return true;
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (!isDOMProxy && IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(holder, shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
 
         // Instantiate this property for singleton holders, for use during Ion compilation.
         if (IsIonEnabled(cx))
             types::EnsureTrackPropertyTypes(cx, holder, NameToId(name));
 
         ICStub::Kind kind = (obj == holder) ? ICStub::GetProp_Native
                                             : ICStub::GetProp_NativePrototype;
 
@@ -6412,17 +6415,17 @@ TryAttachNativeGetPropStub(JSContext *cx
 static bool
 TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
                               ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
                               HandleValue res, bool *attached)
 {
     MOZ_ASSERT(!*attached);
 
     JSValueType primitiveType;
-    RootedObject proto(cx);
+    RootedNativeObject proto(cx);
     Rooted<GlobalObject*> global(cx, &script->global());
     if (val.isString()) {
         primitiveType = JSVAL_TYPE_STRING;
         proto = GlobalObject::getOrCreateStringPrototype(cx, global);
     } else if (val.isSymbol()) {
         primitiveType = JSVAL_TYPE_SYMBOL;
         proto = GlobalObject::getOrCreateSymbolPrototype(cx, global);
     } else if (val.isNumber()) {
@@ -6437,17 +6440,17 @@ TryAttachPrimitiveGetPropStub(JSContext 
         return false;
 
     // Instantiate this property, for use during Ion compilation.
     RootedId id(cx, NameToId(name));
     if (IsIonEnabled(cx))
         types::EnsureTrackPropertyTypes(cx, proto, id);
 
     // For now, only look for properties directly set on the prototype.
-    RootedShape shape(cx, proto->nativeLookup(cx, id));
+    RootedShape shape(cx, proto->lookup(cx, id));
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return true;
 
     bool isFixedSlot;
     uint32_t offset;
     GetFixedOrDynamicSlotOffset(proto, shape->slot(), &isFixedSlot, &offset);
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
@@ -6688,17 +6691,17 @@ ICGetProp_ArrayLength::Compiler::generat
 
     Register scratch = R1.scratchReg();
 
     // Unbox R0 and guard it's an array.
     Register obj = masm.extractObject(R0, ExtractTemp0);
     masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, &ArrayObject::class_, &failure);
 
     // Load obj->elements->length.
-    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), scratch);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
     masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch);
 
     // Guard length fits in an int32.
     masm.branchTest32(Assembler::Signed, scratch, scratch, &failure);
 
     masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
     EmitReturnFromIC(masm);
 
@@ -6755,17 +6758,17 @@ ICGetProp_Primitive::Compiler::generateS
     // Verify the shape of the prototype.
     masm.movePtr(ImmGCPtr(prototype_.get()), holderReg);
 
     Address shapeAddr(BaselineStubReg, ICGetProp_Primitive::offsetOfProtoShape());
     masm.loadPtr(Address(holderReg, JSObject::offsetOfShape()), scratchReg);
     masm.branchPtr(Assembler::NotEqual, shapeAddr, scratchReg, &failure);
 
     if (!isFixedSlot_)
-        masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), holderReg);
+        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
 
     masm.load32(Address(BaselineStubReg, ICGetPropNativeStub::offsetOfOffset()), scratchReg);
     masm.loadValue(BaseIndex(holderReg, scratchReg, TimesOne), R0);
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
     // Failure case - jump to next stub
@@ -6802,17 +6805,17 @@ ICGetPropNativeCompiler::generateStubCod
                      scratch);
         masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
     }
 
     if (!isFixedSlot_) {
         // Don't overwrite actual holderReg if we need to load a dynamic slots object.
         // May need to preserve object for noSuchMethod check later.
         Register nextHolder = regs.takeAny();
-        masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), nextHolder);
+        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), nextHolder);
         holderReg = nextHolder;
     }
 
     masm.load32(Address(BaselineStubReg, ICGetPropNativeStub::offsetOfOffset()), scratch);
     BaseIndex result(holderReg, scratch, TimesOne);
 
 #if JS_HAS_NO_SUCH_METHOD
 #ifdef DEBUG
@@ -7236,17 +7239,17 @@ ICGetPropCallDOMProxyNativeCompiler::gen
 }
 
 ICStub *
 ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace *space)
 {
     RootedShape shape(cx, proxy_->lastProperty());
     RootedShape holderShape(cx, holder_->lastProperty());
 
-    Value expandoSlot = proxy_->getFixedSlot(GetDOMProxyExpandoSlot());
+    Value expandoSlot = proxy_->fakeNativeGetReservedSlot(GetDOMProxyExpandoSlot());
     RootedShape expandoShape(cx, nullptr);
     ExpandoAndGeneration *expandoAndGeneration;
     int32_t generation;
     Value expandoVal;
     if (kind == ICStub::GetProp_CallDOMProxyNative) {
         expandoVal = expandoSlot;
     } else {
         MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative);
@@ -7523,17 +7526,17 @@ TryAttachSetPropStub(JSContext *cx, Hand
         // a stub, though, so the access is not marked as unoptimizable.
         if (oldType->newScript() && !oldType->newScript()->analyzed()) {
             *attached = true;
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(obj, shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.ADD) stub");
         ICSetPropNativeAddCompiler compiler(cx, obj, oldShape, oldType,
                                             chainDepth, isFixedSlot, offset);
         ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
@@ -7552,17 +7555,17 @@ TryAttachSetPropStub(JSContext *cx, Hand
         types::EnsureTrackPropertyTypes(cx, obj, id);
         if (!types::PropertyHasBeenMarkedNonConstant(obj, id)) {
             *attached = true;
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
-        GetFixedOrDynamicSlotOffset(obj, shape->slot(), &isFixedSlot, &offset);
+        GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.PROP) stub");
         ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
         ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
@@ -7649,22 +7652,25 @@ DoSetPropFallback(JSContext *cx, Baselin
 
     RootedObject obj(cx, ToObjectFromStack(cx, lhs));
     if (!obj)
         return false;
     RootedShape oldShape(cx, obj->lastProperty());
     RootedTypeObject oldType(cx, obj->getType(cx));
     if (!oldType)
         return false;
-    uint32_t oldSlots = obj->numDynamicSlots();
+    uint32_t oldSlots = obj->fakeNativeNumDynamicSlots();
 
     if (op == JSOP_INITPROP) {
         MOZ_ASSERT(obj->is<JSObject>());
-        if (!DefineNativeProperty(cx, obj, id, rhs, nullptr, nullptr, JSPROP_ENUMERATE))
-            return false;
+        if (!DefineNativeProperty(cx, obj.as<NativeObject>(), id, rhs,
+                                  nullptr, nullptr, JSPROP_ENUMERATE))
+        {
+            return false;
+        }
     } else if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
         if (!SetNameOperation(cx, script, pc, obj, rhs))
             return false;
     } else if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL) {
         obj->as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc), name, rhs);
     } else {
         MOZ_ASSERT(op == JSOP_SETPROP);
         if (script->strict()) {
@@ -7793,17 +7799,17 @@ ICSetProp_Native::Compiler::generateStub
     regs.add(R0);
     regs.takeUnchecked(objReg);
 
     Register holderReg;
     if (isFixedSlot_) {
         holderReg = objReg;
     } else {
         holderReg = regs.takeAny();
-        masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), holderReg);
+        masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
     }
 
     // Perform the store.
     masm.load32(Address(BaselineStubReg, ICSetProp_Native::offsetOfOffset()), scratch);
     EmitPreBarrier(masm, BaseIndex(holderReg, scratch, TimesOne), MIRType_Value);
     masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
     if (holderReg != objReg)
         regs.add(holderReg);
@@ -7937,17 +7943,17 @@ ICSetPropNativeAddCompiler::generateStub
 
     Register holderReg;
     regs.add(R0);
     regs.takeUnchecked(objReg);
     if (isFixedSlot_) {
         holderReg = objReg;
     } else {
         holderReg = regs.takeAny();
-        masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), holderReg);
+        masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
     }
 
     // Perform the store.  No write barrier required since this is a new
     // initialization.
     masm.load32(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfOffset()), scratch);
     masm.storeValue(R1, BaseIndex(holderReg, scratch, TimesOne));
 
     if (holderReg != objReg)
@@ -8251,17 +8257,17 @@ TryAttachFunCallStub(JSContext *cx, ICCa
         return true;
     }
 
     return true;
 }
 
 static bool
 GetTemplateObjectForNative(JSContext *cx, HandleScript script, jsbytecode *pc,
-                           Native native, const CallArgs &args, MutableHandleObject res)
+                           Native native, const CallArgs &args, MutableHandleNativeObject res)
 {
     // Check for natives to which template objects can be attached. This is
     // done to provide templates to Ion for inlining these natives later on.
 
     if (native == js_Array) {
         // Note: the template array won't be used if its length is inaccurately
         // computed here.  (We allocate here because compilation may occur on a
         // separate thread where allocation is impossible.)
@@ -8426,17 +8432,17 @@ TryAttachCallStub(JSContext *cx, ICCall_
 
         // Keep track of the function's |prototype| property in type
         // information, for use during Ion compilation.
         if (IsIonEnabled(cx))
             types::EnsureTrackPropertyTypes(cx, fun, NameToId(cx->names().prototype));
 
         // Remember the template object associated with any script being called
         // as a constructor, for later use during Ion compilation.
-        RootedObject templateObject(cx);
+        RootedNativeObject templateObject(cx);
         if (constructing) {
             templateObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
             if (!templateObject)
                 return false;
 
             // If we are calling a constructor for which the new script
             // properties analysis has not been performed yet, don't attach a
             // stub. After the analysis is performed, CreateThisForFunction may
@@ -8491,17 +8497,17 @@ TryAttachCallStub(JSContext *cx, ICCall_
         }
 
         if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
             JitSpew(JitSpew_BaselineIC,
                     "  Too many Call_Native stubs. TODO: add Call_AnyNative!");
             return true;
         }
 
-        RootedObject templateObject(cx);
+        RootedNativeObject templateObject(cx);
         if (MOZ_LIKELY(!isSpread)) {
             CallArgs args = CallArgsFromVp(argc, vp);
             if (!GetTemplateObjectForNative(cx, script, pc, fun->native(), args, &templateObject))
                 return false;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
                 fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
@@ -8515,27 +8521,27 @@ TryAttachCallStub(JSContext *cx, ICCall_
         stub->addNewStub(newStub);
         return true;
     }
 
     return true;
 }
 
 static bool
-CopyArray(JSContext *cx, HandleObject obj, MutableHandleValue result)
+CopyArray(JSContext *cx, HandleArrayObject obj, MutableHandleValue result)
 {
     MOZ_ASSERT(obj->is<ArrayObject>());
     uint32_t length = obj->as<ArrayObject>().length();
     MOZ_ASSERT(obj->getDenseInitializedLength() == length);
 
     RootedTypeObject type(cx, obj->getType(cx));
     if (!type)
         return false;
 
-    RootedObject newObj(cx, NewDenseArray(cx, length, type, NewArray_FullyAllocating));
+    RootedArrayObject newObj(cx, NewDenseArray(cx, length, type, NewArray_FullyAllocating));
     if (!newObj)
         return false;
 
     newObj->setDenseInitializedLength(length);
     newObj->initDenseElements(0, obj->getDenseElements(), length);
     result.setObject(*newObj);
     return true;
 }
@@ -8549,23 +8555,22 @@ TryAttachStringSplit(JSContext *cx, ICCa
 
     RootedValue callee(cx, vp[0]);
     RootedValue thisv(cx, vp[1]);
     Value *args = vp + 2;
 
     if (!IsOptimizableCallStringSplit(callee, thisv, argc, args))
         return true;
 
-    MOZ_ASSERT(res.toObject().is<ArrayObject>());
     MOZ_ASSERT(callee.isObject());
     MOZ_ASSERT(callee.toObject().is<JSFunction>());
 
     RootedString thisString(cx, thisv.toString());
     RootedString argString(cx, args[0].toString());
-    RootedObject obj(cx, &res.toObject());
+    RootedArrayObject obj(cx, &res.toObject().as<ArrayObject>());
     RootedValue arr(cx);
 
     // Copy the array before storing in stub.
     if (!CopyArray(cx, obj, &arr))
         return false;
 
     ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
                                           script->pcToOffset(pc), thisString, argString,
@@ -8760,17 +8765,17 @@ ICCallStubCompiler::pushCallArguments(Ma
     }
     masm.bind(&done);
 }
 
 void
 ICCallStubCompiler::guardSpreadCall(MacroAssembler &masm, Register argcReg, Label *failure)
 {
     masm.unboxObject(Address(BaselineStackReg, ICStackValueOffset), argcReg);
-    masm.loadPtr(Address(argcReg, JSObject::offsetOfElements()), argcReg);
+    masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg);
     masm.load32(Address(argcReg, ObjectElements::offsetOfLength()), argcReg);
 
     // Limit actual argc to something reasonable (huge number of arguments can
     // blow the stack limit).
     static_assert(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH <= ARGS_LENGTH_MAX,
                   "maximum arguments length for optimized stub should be <= ARGS_LENGTH_MAX");
     masm.branch32(Assembler::Above, argcReg, Imm32(ICCall_Scripted::MAX_ARGS_SPREAD_LENGTH),
                   failure);
@@ -8779,17 +8784,17 @@ ICCallStubCompiler::guardSpreadCall(Macr
 void
 ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler &masm, GeneralRegisterSet regs,
                                             Register argcReg)
 {
     // Push arguments
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.unboxObject(Address(BaselineStackReg, STUB_FRAME_SIZE), startReg);
-    masm.loadPtr(Address(startReg, JSObject::offsetOfElements()), startReg);
+    masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
     masm.mov(argcReg, endReg);
     static_assert(sizeof(Value) == 8, "Value must be 8 bytes");
     masm.lshiftPtr(Imm32(3), endReg);
     masm.addPtr(startReg, endReg);
 
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
@@ -8842,17 +8847,17 @@ ICCallStubCompiler::guardFunApply(MacroA
 
         regsx.add(secondArgVal);
         regsx.takeUnchecked(secondArgObj);
 
         masm.branchTestObjClass(Assembler::NotEqual, secondArgObj, regsx.getAny(),
                                 &ArrayObject::class_, failure);
 
         // Get the array elements and ensure that initializedLength == length
-        masm.loadPtr(Address(secondArgObj, JSObject::offsetOfElements()), secondArgObj);
+        masm.loadPtr(Address(secondArgObj, NativeObject::offsetOfElements()), secondArgObj);
 
         Register lenReg = regsx.takeAny();
         masm.load32(Address(secondArgObj, ObjectElements::offsetOfLength()), lenReg);
 
         masm.branch32(Assembler::NotEqual,
                       Address(secondArgObj, ObjectElements::offsetOfInitializedLength()),
                       lenReg, failure);
 
@@ -8952,17 +8957,17 @@ ICCallStubCompiler::pushArrayArguments(M
                                        GeneralRegisterSet regs)
 {
     // Load start and end address of values to copy.
     // guardFunApply has already gauranteed that the array is packed and contains
     // no holes.
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
     masm.extractObject(arrayVal, startReg);
-    masm.loadPtr(Address(startReg, JSObject::offsetOfElements()), startReg);
+    masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
     masm.load32(Address(startReg, ObjectElements::offsetOfInitializedLength()), endReg);
     JS_STATIC_ASSERT(sizeof(Value) == 8);
     masm.lshiftPtr(Imm32(3), endReg);
     masm.addPtr(startReg, endReg);
 
     // Copying pre-decrements endReg by 8 until startReg is reached
     Label copyDone;
     Label copyStart;
@@ -9350,17 +9355,17 @@ ICCallScriptedCompiler::generateStubCode
     if (argcReg != R0.scratchReg())
         masm.mov(argcReg, R0.scratchReg());
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-typedef bool (*CopyArrayFn)(JSContext *, HandleObject, MutableHandleValue);
+typedef bool (*CopyArrayFn)(JSContext *, HandleArrayObject, MutableHandleValue);
 static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray);
 
 bool
 ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler &masm)
 {
     // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, +ICStackValueOffset+ ]
     GeneralRegisterSet regs = availableGeneralRegs(0);
     Label failureRestoreArgc;
@@ -9612,17 +9617,17 @@ ICCall_ScriptedApplyArray::Compiler::gen
 
     // All pushes after this use Push instead of push to make sure ARM can align
     // stack properly for call.
     Register scratch = regs.takeAny();
     EmitCreateStubFrameDescriptor(masm, scratch);
 
     // Reload argc from length of array.
     masm.extractObject(arrayVal, argcReg);
-    masm.loadPtr(Address(argcReg, JSObject::offsetOfElements()), argcReg);
+    masm.loadPtr(Address(argcReg, NativeObject::offsetOfElements()), argcReg);
     masm.load32(Address(argcReg, ObjectElements::offsetOfInitializedLength()), argcReg);
 
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
@@ -10847,43 +10852,43 @@ ICSetProp_CallNative::Clone(JSContext *c
     RootedShape shape(cx, other.shape_);
     RootedObject holder(cx, other.holder_);
     RootedShape holderShape(cx, other.holderShape_);
     RootedFunction setter(cx, other.setter_);
     return New(space, other.jitCode(), shape, holder, holderShape, setter, other.pcOffset_);
 }
 
 ICCall_Scripted::ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub,
-                                 HandleScript calleeScript, HandleObject templateObject,
+                                 HandleScript calleeScript, HandleNativeObject templateObject,
                                  uint32_t pcOffset)
   : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
     calleeScript_(calleeScript),
     templateObject_(templateObject),
     pcOffset_(pcOffset)
 { }
 
 /* static */ ICCall_Scripted *
 ICCall_Scripted::Clone(JSContext *cx, ICStubSpace *space, ICStub *firstMonitorStub,
                        ICCall_Scripted &other)
 {
     RootedScript calleeScript(cx, other.calleeScript_);
-    RootedObject templateObject(cx, other.templateObject_);
+    RootedNativeObject templateObject(cx, other.templateObject_);
     return New(space, other.jitCode(), firstMonitorStub, calleeScript, templateObject,
                other.pcOffset_);
 }
 
 /* static */ ICCall_AnyScripted *
 ICCall_AnyScripted::Clone(JSContext *, ICStubSpace *space, ICStub *firstMonitorStub,
                           ICCall_AnyScripted &other)
 {
     return New(space, other.jitCode(), firstMonitorStub, other.pcOffset_);
 }
 
 ICCall_Native::ICCall_Native(JitCode *stubCode, ICStub *firstMonitorStub,
-                             HandleFunction callee, HandleObject templateObject,
+                             HandleFunction callee, HandleNativeObject templateObject,
                              uint32_t pcOffset)
   : ICMonitoredStub(ICStub::Call_Native, stubCode, firstMonitorStub),
     callee_(callee),
     templateObject_(templateObject),
     pcOffset_(pcOffset)
 {
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     // The simulator requires VM calls to be redirected to a special swi
@@ -10894,17 +10899,17 @@ ICCall_Native::ICCall_Native(JitCode *st
 #endif
 }
 
 /* static */ ICCall_Native *
 ICCall_Native::Clone(JSContext *cx, ICStubSpace *space, ICStub *firstMonitorStub,
                      ICCall_Native &other)
 {
     RootedFunction callee(cx, other.callee_);
-    RootedObject templateObject(cx, other.templateObject_);
+    RootedNativeObject templateObject(cx, other.templateObject_);
     return New(space, other.jitCode(), firstMonitorStub, callee, templateObject,
                other.pcOffset_);
 }
 
 /* static */ ICCall_ScriptedApplyArray *
 ICCall_ScriptedApplyArray::Clone(JSContext *, ICStubSpace *space, ICStub *firstMonitorStub,
                                  ICCall_ScriptedApplyArray &other)
 {
@@ -11022,17 +11027,17 @@ ICGetProp_DOMProxyShadowed::Clone(JSCont
 static bool DoRestFallback(JSContext *cx, ICRest_Fallback *stub,
                            BaselineFrame *frame, MutableHandleValue res)
 {
     unsigned numFormals = frame->numFormalArgs() - 1;
     unsigned numActuals = frame->numActualArgs();
     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
     Value *rest = frame->argv() + numFormals;
 
-    JSObject *obj = NewDenseCopiedArray(cx, numRest, rest, nullptr);
+    ArrayObject *obj = NewDenseCopiedArray(cx, numRest, rest, nullptr);
     if (!obj)
         return false;
     types::FixRestArgumentsType(cx, obj);
     res.setObject(*obj);
     return true;
 }
 
 typedef bool (*DoRestFallbackFn)(JSContext *, ICRest_Fallback *, BaselineFrame *,
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -11,16 +11,17 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsopcode.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/BaselineRegisters.h"
+#include "vm/ArrayObject.h"
 
 namespace js {
 
 class TypedArrayLayout;
 
 namespace jit {
 
 //
@@ -1873,84 +1874,84 @@ class ICThis_Fallback : public ICFallbac
         }
     };
 };
 
 class ICNewArray_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
-    HeapPtrObject templateObject_;
-
-    ICNewArray_Fallback(JitCode *stubCode, JSObject *templateObject)
+    HeapPtrArrayObject templateObject_;
+
+    ICNewArray_Fallback(JitCode *stubCode, ArrayObject *templateObject)
       : ICFallbackStub(ICStub::NewArray_Fallback, stubCode), templateObject_(templateObject)
     {}
 
   public:
     static inline ICNewArray_Fallback *New(ICStubSpace *space, JitCode *code,
-                                           JSObject *templateObject) {
+                                           ArrayObject *templateObject) {
         if (!code)
             return nullptr;
         return space->allocate<ICNewArray_Fallback>(code, templateObject);
     }
 
     class Compiler : public ICStubCompiler {
-        RootedObject templateObject;
+        RootedArrayObject templateObject;
         bool generateStubCode(MacroAssembler &masm);
 
       public:
-        Compiler(JSContext *cx, JSObject *templateObject)
+        Compiler(JSContext *cx, ArrayObject *templateObject)
           : ICStubCompiler(cx, ICStub::NewArray_Fallback),
             templateObject(cx, templateObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             return ICNewArray_Fallback::New(space, getStubCode(), templateObject);
         }
     };
 
-    HeapPtrObject &templateObject() {
+    HeapPtrArrayObject &templateObject() {
         return templateObject_;
     }
 };
 
 class ICNewObject_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
-    HeapPtrObject templateObject_;
-
-    ICNewObject_Fallback(JitCode *stubCode, JSObject *templateObject)
+    HeapPtrNativeObject templateObject_;
+
+    ICNewObject_Fallback(JitCode *stubCode, NativeObject *templateObject)
       : ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(templateObject)
     {}
 
   public:
     static inline ICNewObject_Fallback *New(ICStubSpace *space, JitCode *code,
-                                            JSObject *templateObject) {
+                                            NativeObject *templateObject) {
         if (!code)
             return nullptr;
         return space->allocate<ICNewObject_Fallback>(code, templateObject);
     }
 
     class Compiler : public ICStubCompiler {
-        RootedObject templateObject;
+        RootedNativeObject templateObject;
         bool generateStubCode(MacroAssembler &masm);
 
       public:
-        Compiler(JSContext *cx, JSObject *templateObject)
+        Compiler(JSContext *cx, NativeObject *templateObject)
           : ICStubCompiler(cx, ICStub::NewObject_Fallback),
             templateObject(cx, templateObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             return ICNewObject_Fallback::New(space, getStubCode(), templateObject);
         }
     };
 
-    HeapPtrObject &templateObject() {
+    HeapPtrNativeObject &templateObject() {
         return templateObject_;
     }
 };
 
 // Compare
 //      JSOP_LT
 //      JSOP_GT
 
@@ -5674,42 +5675,42 @@ class ICCall_Scripted : public ICMonitor
   public:
     // The maximum number of inlineable spread call arguments. Keep this small
     // to avoid controllable stack overflows by attackers passing large arrays
     // to spread call. This value is shared with ICCall_Native.
     static const uint32_t MAX_ARGS_SPREAD_LENGTH = 16;
 
   protected:
     HeapPtrScript calleeScript_;
-    HeapPtrObject templateObject_;
+    HeapPtrNativeObject templateObject_;
     uint32_t pcOffset_;
 
     ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub,
-                    HandleScript calleeScript, HandleObject templateObject,
+                    HandleScript calleeScript, HandleNativeObject templateObject,
                     uint32_t pcOffset);
 
   public:
     static inline ICCall_Scripted *New(
             ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub,
-            HandleScript calleeScript, HandleObject templateObject,
+            HandleScript calleeScript, HandleNativeObject templateObject,
             uint32_t pcOffset)
     {
         if (!code)
             return nullptr;
         return space->allocate<ICCall_Scripted>(code, firstMonitorStub,
                                                 calleeScript, templateObject, pcOffset);
     }
 
     static ICCall_Scripted *Clone(JSContext *cx, ICStubSpace *space, ICStub *firstMonitorStub,
                                   ICCall_Scripted &other);
 
     HeapPtrScript &calleeScript() {
         return calleeScript_;
     }
-    HeapPtrObject &templateObject() {
+    HeapPtrNativeObject &templateObject() {
         return templateObject_;
     }
 
     static size_t offsetOfCalleeScript() {
         return offsetof(ICCall_Scripted, calleeScript_);
     }
     static size_t offsetOfPCOffset() {
         return offsetof(ICCall_Scripted, pcOffset_);
@@ -5747,28 +5748,28 @@ class ICCall_AnyScripted : public ICMoni
 
 // Compiler for Call_Scripted and Call_AnyScripted stubs.
 class ICCallScriptedCompiler : public ICCallStubCompiler {
   protected:
     ICStub *firstMonitorStub_;
     bool isConstructing_;
     bool isSpread_;
     RootedScript calleeScript_;
-    RootedObject templateObject_;
+    RootedNativeObject templateObject_;
     uint32_t pcOffset_;
     bool generateStubCode(MacroAssembler &masm);
 
     virtual int32_t getKey() const {
         return static_cast<int32_t>(kind) | (static_cast<int32_t>(isConstructing_) << 16) |
                (static_cast<int32_t>(isSpread_) << 17);
     }
 
   public:
     ICCallScriptedCompiler(JSContext *cx, ICStub *firstMonitorStub,
-                           HandleScript calleeScript, HandleObject templateObject,
+                           HandleScript calleeScript, HandleNativeObject templateObject,
                            bool isConstructing, bool isSpread, uint32_t pcOffset)
       : ICCallStubCompiler(cx, ICStub::Call_Scripted),
         firstMonitorStub_(firstMonitorStub),
         isConstructing_(isConstructing),
         isSpread_(isSpread),
         calleeScript_(cx, calleeScript),
         templateObject_(cx, templateObject),
         pcOffset_(pcOffset)
@@ -5796,45 +5797,45 @@ class ICCallScriptedCompiler : public IC
 };
 
 class ICCall_Native : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
   protected:
     HeapPtrFunction callee_;
-    HeapPtrObject templateObject_;
+    HeapPtrNativeObject templateObject_;
     uint32_t pcOffset_;
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     void *native_;
 #endif
 
     ICCall_Native(JitCode *stubCode, ICStub *firstMonitorStub,
-                  HandleFunction callee, HandleObject templateObject,
+                  HandleFunction callee, HandleNativeObject templateObject,
                   uint32_t pcOffset);
 
   public:
     static inline ICCall_Native *New(ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub,
-                                     HandleFunction callee, HandleObject templateObject,
+                                     HandleFunction callee, HandleNativeObject templateObject,
                                      uint32_t pcOffset)
     {
         if (!code)
             return nullptr;
         return space->allocate<ICCall_Native>(code, firstMonitorStub,
                                               callee, templateObject, pcOffset);
     }
 
     static ICCall_Native *Clone(JSContext *cx, ICStubSpace *space, ICStub *firstMonitorStub,
                                 ICCall_Native &other);
 
     HeapPtrFunction &callee() {
         return callee_;
     }
-    HeapPtrObject &templateObject() {
+    HeapPtrNativeObject &templateObject() {
         return templateObject_;
     }
 
     static size_t offsetOfCallee() {
         return offsetof(ICCall_Native, callee_);
     }
     static size_t offsetOfPCOffset() {
         return offsetof(ICCall_Native, pcOffset_);
@@ -5848,28 +5849,28 @@ class ICCall_Native : public ICMonitored
 
     // Compiler for this stub kind.
     class Compiler : public ICCallStubCompiler {
       protected:
         ICStub *firstMonitorStub_;
         bool isConstructing_;
         bool isSpread_;
         RootedFunction callee_;
-        RootedObject templateObject_;
+        RootedNativeObject templateObject_;
         uint32_t pcOffset_;
         bool generateStubCode(MacroAssembler &masm);
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind) | (static_cast<int32_t>(isConstructing_) << 16) |
                    (static_cast<int32_t>(isSpread_) << 17);
         }
 
       public:
         Compiler(JSContext *cx, ICStub *firstMonitorStub,
-                 HandleFunction callee, HandleObject templateObject,
+                 HandleFunction callee, HandleNativeObject templateObject,
                  bool isConstructing, bool isSpread, uint32_t pcOffset)
           : ICCallStubCompiler(cx, ICStub::Call_Native),
             firstMonitorStub_(firstMonitorStub),
             isConstructing_(isConstructing),
             isSpread_(isSpread),
             callee_(cx, callee),
             templateObject_(cx, templateObject),
             pcOffset_(pcOffset)
@@ -6417,43 +6418,43 @@ class ICTypeOf_Typed : public ICFallback
         }
     };
 };
 
 class ICRest_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
-    HeapPtrObject templateObject_;
-
-    ICRest_Fallback(JitCode *stubCode, JSObject *templateObject)
+    HeapPtrArrayObject templateObject_;
+
+    ICRest_Fallback(JitCode *stubCode, ArrayObject *templateObject)
       : ICFallbackStub(ICStub::Rest_Fallback, stubCode), templateObject_(templateObject)
     { }
 
   public:
     static const uint32_t MAX_OPTIMIZED_STUBS = 8;
 
     static inline ICRest_Fallback *New(ICStubSpace *space, JitCode *code,
-                                       JSObject *templateObject) {
+                                       ArrayObject *templateObject) {
         if (!code)
             return nullptr;
         return space->allocate<ICRest_Fallback>(code, templateObject);
     }
 
-    HeapPtrObject &templateObject() {
+    HeapPtrArrayObject &templateObject() {
         return templateObject_;
     }
 
     class Compiler : public ICStubCompiler {
       protected:
-        RootedObject templateObject;
+        RootedArrayObject templateObject;
         bool generateStubCode(MacroAssembler &masm);
 
       public:
-        Compiler(JSContext *cx, JSObject *templateObject)
+        Compiler(JSContext *cx, ArrayObject *templateObject)
           : ICStubCompiler(cx, ICStub::Rest_Fallback),
             templateObject(cx, templateObject)
         { }
 
         ICStub *getStub(ICStubSpace *space) {
             return ICRest_Fallback::New(space, getStubCode(), templateObject);
         }
     };
@@ -6548,17 +6549,17 @@ IsCacheableDOMProxy(JSObject *obj)
     if (!obj->is<ProxyObject>())
         return false;
 
     const BaseProxyHandler *handler = obj->as<ProxyObject>().handler();
 
     if (handler->family() != GetDOMProxyHandlerFamily())
         return false;
 
-    if (obj->numFixedSlots() <= GetDOMProxyExpandoSlot())
+    if (obj->fakeNativeNumFixedSlots() <= GetDOMProxyExpandoSlot())
         return false;
 
     return true;
 }
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -408,44 +408,44 @@ BaselineInspector::hasSeenDoubleResult(j
     if (stub->isUnaryArith_Fallback())
         return stub->toUnaryArith_Fallback()->sawDoubleResult();
     else
         return stub->toBinaryArith_Fallback()->sawDoubleResult();
 
     return false;
 }
 
-JSObject *
+NativeObject *
 BaselineInspector::getTemplateObject(jsbytecode *pc)
 {
     if (!hasBaselineScript())
         return nullptr;
 
     const ICEntry &entry = icEntryFromPC(pc);
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         switch (stub->kind()) {
           case ICStub::NewArray_Fallback:
             return stub->toNewArray_Fallback()->templateObject();
           case ICStub::NewObject_Fallback:
             return stub->toNewObject_Fallback()->templateObject();
           case ICStub::Rest_Fallback:
             return stub->toRest_Fallback()->templateObject();
           case ICStub::Call_Scripted:
-            if (JSObject *obj = stub->toCall_Scripted()->templateObject())
+            if (NativeObject *obj = stub->toCall_Scripted()->templateObject())
                 return obj;
             break;
           default:
             break;
         }
     }
 
     return nullptr;
 }
 
-JSObject *
+NativeObject *
 BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native)
 {
     if (!hasBaselineScript())
         return nullptr;
 
     const ICEntry &entry = icEntryFromPC(pc);
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native)
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -104,18 +104,18 @@ class BaselineInspector
     MIRType expectedBinaryArithSpecialization(jsbytecode *pc);
 
     bool hasSeenNonNativeGetElement(jsbytecode *pc);
     bool hasSeenNegativeIndexGetElement(jsbytecode *pc);
     bool hasSeenAccessedGetter(jsbytecode *pc);
     bool hasSeenDoubleResult(jsbytecode *pc);
     bool hasSeenNonStringIterMore(jsbytecode *pc);
 
-    JSObject *getTemplateObject(jsbytecode *pc);
-    JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
+    NativeObject *getTemplateObject(jsbytecode *pc);
+    NativeObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
 
     DeclEnvObject *templateDeclEnvObject();
     CallObject *templateCallObject();
 
     JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter);
     JSObject *commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter);
 };
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1371,17 +1371,17 @@ CodeGenerator::visitTableSwitchV(LTableS
     masm.bind(&unboxInt);
     masm.unboxInt32(value, index);
 
     masm.bind(&isInt);
 
     return emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
 }
 
-typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
+typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 bool
 CodeGenerator::visitCloneLiteral(LCloneLiteral *lir)
 {
     pushArg(ImmWord(js::MaybeSingletonObject));
     pushArg(ToRegister(lir->getObjectLiteral()));
@@ -1629,17 +1629,17 @@ CodeGenerator::visitPointer(LPointer *li
     else
         masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output()));
     return true;
 }
 
 bool
 CodeGenerator::visitSlots(LSlots *lir)
 {
-    Address slots(ToRegister(lir->object()), JSObject::offsetOfSlots());
+    Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
     masm.loadPtr(slots, ToRegister(lir->output()));
     return true;
 }
 
 bool
 CodeGenerator::visitLoadSlotT(LLoadSlotT *lir)
 {
     Register base = ToRegister(lir->slots());
@@ -1717,22 +1717,22 @@ CodeGenerator::emitGetPropertyPolymorphi
             }
         } else {
             masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
         }
 
         Shape *shape = mir->shape(i);
         if (shape->slot() < shape->numFixedSlots()) {
             // Fixed slot.
-            masm.loadTypedOrValue(Address(obj, JSObject::getFixedSlotOffset(shape->slot())),
+            masm.loadTypedOrValue(Address(obj, NativeObject::getFixedSlotOffset(shape->slot())),
                                   output);
         } else {
             // Dynamic slot.
             uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value);
-            masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), scratch);
+            masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
             masm.loadTypedOrValue(Address(scratch, offset), output);
         }
 
         if (i != mir->numShapes() - 1)
             masm.jump(&done);
         masm.bind(&next);
     }
 
@@ -1779,23 +1779,23 @@ CodeGenerator::emitSetPropertyPolymorphi
             }
         } else {
             masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
         }
 
         Shape *shape = mir->shape(i);
         if (shape->slot() < shape->numFixedSlots()) {
             // Fixed slot.
-            Address addr(obj, JSObject::getFixedSlotOffset(shape->slot()));
+            Address addr(obj, NativeObject::getFixedSlotOffset(shape->slot()));
             if (mir->needsBarrier())
                 emitPreBarrier(addr);
             masm.storeConstantOrRegister(value, addr);
         } else {
             // Dynamic slot.
-            masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), scratch);
+            masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
             Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
             if (mir->needsBarrier())
                 emitPreBarrier(addr);
             masm.storeConstantOrRegister(value, addr);
         }
 
         if (i != mir->numShapes() - 1)
             masm.jump(&done);
@@ -1828,17 +1828,17 @@ CodeGenerator::visitSetPropertyPolymorph
         value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(ins->value()));
 
     return emitSetPropertyPolymorphic(ins, obj, temp, value);
 }
 
 bool
 CodeGenerator::visitElements(LElements *lir)
 {
-    Address elements(ToRegister(lir->object()), JSObject::offsetOfElements());
+    Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
     masm.loadPtr(elements, ToRegister(lir->output()));
     return true;
 }
 
 typedef bool (*ConvertElementsToDoublesFn)(JSContext *, uintptr_t);
 static const VMFunction ConvertElementsToDoublesInfo =
     FunctionInfo<ConvertElementsToDoublesFn>(ObjectElements::ConvertElementsToDoubles);
 
@@ -1882,32 +1882,32 @@ CodeGenerator::visitMaybeToDoubleElement
     masm.bind(&convert);
     masm.convertInt32ToDouble(value, temp);
     masm.boxDouble(temp, out);
 
     masm.bind(&done);
     return true;
 }
 
-typedef bool (*CopyElementsForWriteFn)(ThreadSafeContext *, JSObject *);
+typedef bool (*CopyElementsForWriteFn)(ThreadSafeContext *, NativeObject *);
 static const VMFunction CopyElementsForWriteInfo =
-    FunctionInfo<CopyElementsForWriteFn>(JSObject::CopyElementsForWrite);
+    FunctionInfo<CopyElementsForWriteFn>(NativeObject::CopyElementsForWrite);
 
 bool
 CodeGenerator::visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite *lir)
 {
     Register object = ToRegister(lir->object());
     Register temp = ToRegister(lir->temp());
 
     OutOfLineCode *ool = oolCallVM(CopyElementsForWriteInfo, lir,
                                    (ArgList(), object), StoreNothing());
     if (!ool)
         return false;
 
-    masm.loadPtr(Address(object, JSObject::offsetOfElements()), temp);
+    masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
     masm.branchTest32(Assembler::NonZero,
                       Address(temp, ObjectElements::offsetOfFlags()),
                       Imm32(ObjectElements::COPY_ON_WRITE),
                       ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
@@ -2273,17 +2273,17 @@ CodeGenerator::visitCallDOMNative(LCallD
     // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
     // StackPointer.
     JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
     JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgc ==
                      IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
     masm.computeEffectiveAddress(Address(StackPointer, 2 * sizeof(Value)), argArgs);
 
     // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
-    masm.loadPrivate(Address(obj, JSObject::getFixedSlotOffset(0)), argPrivate);
+    masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), argPrivate);
 
     // Push argc from the call instruction into what will become the IonExitFrame
     masm.Push(Imm32(call->numStackArgs()));
 
     // Push our argv onto the stack
     masm.Push(argArgs);
     // And store our JSJitMethodCallArgs* in argArgs.
     masm.movePtr(StackPointer, argArgs);
@@ -3590,20 +3590,20 @@ bool CodeGenerator::visitHypot(LHypot *l
 }
 
 bool
 CodeGenerator::visitNewArray(LNewArray *lir)
 {
     MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
-    JSObject *templateObject = lir->mir()->templateObject();
+    ArrayObject *templateObject = lir->mir()->templateObject();
     DebugOnly<uint32_t> count = lir->mir()->count();
 
-    MOZ_ASSERT(count < JSObject::NELEMENTS_LIMIT);
+    MOZ_ASSERT(count < NativeObject::NELEMENTS_LIMIT);
 
     if (lir->mir()->shouldUseVM())
         return visitNewArrayCallVM(lir);
 
     OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir);
     if (!addOutOfLineCode(ool, lir->mir()))
         return false;
 
@@ -3622,17 +3622,17 @@ CodeGenerator::visitOutOfLineNewArray(Ou
     return true;
 }
 
 bool
 CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
-    JSObject *templateObject = lir->mir()->templateObject();
+    ArrayObject *templateObject = lir->mir()->templateObject();
     gc::InitialHeap initialHeap = lir->mir()->initialHeap();
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool = oolCallVM(NewArrayCopyOnWriteInfo, lir,
                                    (ArgList(), ImmGCPtr(templateObject), Imm32(initialHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
@@ -3657,17 +3657,17 @@ class OutOfLineNewObject : public OutOfL
         return codegen->visitOutOfLineNewObject(this);
     }
 
     LNewObject *lir() const {
         return lir_;
     }
 };
 
-typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject);
+typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleNativeObject);
 static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
 
 typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandleObject);
 static const VMFunction NewInitObjectWithClassPrototypeInfo =
     FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
 
 bool
 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
@@ -3695,17 +3695,17 @@ CodeGenerator::visitNewObjectVMCall(LNew
     if (ReturnReg != objReg)
         masm.movePtr(ReturnReg, objReg);
 
     restoreLive(lir);
     return true;
 }
 
 static bool
-ShouldInitFixedSlots(LInstruction *lir, JSObject *templateObj)
+ShouldInitFixedSlots(LInstruction *lir, NativeObject *templateObj)
 {
     // Look for StoreFixedSlot instructions following an object allocation
     // that write to this object before a GC is triggered or this object is
     // passed to a VM call. If all fixed slots will be initialized, the
     // allocation code doesn't need to set the slots to |undefined|.
 
     uint32_t nfixed = templateObj->numUsedFixedSlots();
     if (nfixed == 0)
@@ -3716,18 +3716,18 @@ ShouldInitFixedSlots(LInstruction *lir, 
     // comment below.
     for (uint32_t slot = 0; slot < nfixed; slot++) {
         if (!templateObj->getSlot(slot).isUndefined())
             return true;
     }
 
     // Keep track of the fixed slots that are initialized. initializedSlots is
     // a bit mask with a bit for each slot.
-    MOZ_ASSERT(nfixed <= JSObject::MAX_FIXED_SLOTS);
-    static_assert(JSObject::MAX_FIXED_SLOTS <= 32, "Slot bits must fit in 32 bits");
+    MOZ_ASSERT(nfixed <= NativeObject::MAX_FIXED_SLOTS);
+    static_assert(NativeObject::MAX_FIXED_SLOTS <= 32, "Slot bits must fit in 32 bits");
     uint32_t initializedSlots = 0;
     uint32_t numInitialized = 0;
 
     MInstruction *allocMir = lir->mirRaw()->toInstruction();
     MBasicBlock *block = allocMir->block();
 
     // Skip the allocation instruction.
     MInstructionIterator iter = block->begin(allocMir);
@@ -3784,17 +3784,17 @@ ShouldInitFixedSlots(LInstruction *lir, 
 }
 
 bool
 CodeGenerator::visitNewObject(LNewObject *lir)
 {
     MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
-    JSObject *templateObject = lir->mir()->templateObject();
+    NativeObject *templateObject = lir->mir()->templateObject();
 
     if (lir->mir()->shouldUseVM())
         return visitNewObjectVMCall(lir);
 
     OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
     if (!addOutOfLineCode(ool, lir->mir()))
         return false;
 
@@ -3819,17 +3819,17 @@ typedef js::DeclEnvObject *(*NewDeclEnvO
 static const VMFunction NewDeclEnvObjectInfo =
     FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
 
 bool
 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
-    JSObject *templateObj = lir->mir()->templateObj();
+    NativeObject *templateObj = lir->mir()->templateObj();
     CompileInfo &info = lir->mir()->block()->info();
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(info.funMaybeLazy()),
                                     Imm32(gc::DefaultHeap)),
                                    StoreRegisterTo(objReg));
     if (!ool)
@@ -3848,17 +3848,17 @@ static const VMFunction NewCallObjectInf
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
-    JSObject *templateObj = lir->mir()->templateObject();
+    NativeObject *templateObj = lir->mir()->templateObject();
 
     OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(templateObj->lastProperty()),
                                                ImmGCPtr(templateObj->type())),
                                    StoreRegisterTo(objReg));
     if (!ool)
         return false;
 
@@ -3900,34 +3900,34 @@ CodeGenerator::visitNewSingletonCallObje
 
 bool
 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
-    JSObject *templateObj = lir->mir()->templateObj();
+    NativeObject *templateObj = lir->mir()->templateObj();
 
     return emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
 }
 
-typedef JSObject *(*ExtendArrayParFn)(ForkJoinContext*, JSObject*, uint32_t);
+typedef ArrayObject *(*ExtendArrayParFn)(ForkJoinContext*, ArrayObject*, uint32_t);
 static const VMFunction ExtendArrayParInfo =
     FunctionInfo<ExtendArrayParFn>(ExtendArrayPar);
 
 bool
 CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
 {
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register lengthReg = ToRegister(lir->length());
     Register tempReg0 = ToRegister(lir->getTemp0());
     Register tempReg1 = ToRegister(lir->getTemp1());
     Register tempReg2 = ToRegister(lir->getTemp2());
-    JSObject *templateObj = lir->mir()->templateObject();
+    ArrayObject *templateObj = lir->mir()->templateObject();
 
     if (!emitAllocateGCThingPar(lir, tempReg2, cxReg, tempReg0, tempReg1, templateObj))
         return false;
 
     // Invoke a C helper to allocate the elements.  The helper returns
     // nullptr on allocation error or the array object.
 
     saveLive(lir);
@@ -3971,17 +3971,17 @@ CodeGenerator::visitNewStringObject(LNew
 
 bool
 CodeGenerator::visitNewPar(LNewPar *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
-    JSObject *templateObject = lir->mir()->templateObject();
+    NativeObject *templateObject = lir->mir()->templateObject();
     return emitAllocateGCThingPar(lir, objReg, cxReg, tempReg1, tempReg2, templateObject);
 }
 
 #ifndef JSGC_FJGENERATIONAL
 class OutOfLineNewGCThingPar : public OutOfLineCodeBase<CodeGenerator>
 {
 public:
     LInstruction *lir;
@@ -4001,17 +4001,17 @@ public:
 #endif // JSGC_FJGENERATIONAL
 
 typedef JSObject *(*NewGCThingParFn)(ForkJoinContext *, js::gc::AllocKind allocKind);
 static const VMFunction NewGCThingParInfo =
     FunctionInfo<NewGCThingParFn>(NewGCThingPar);
 
 bool
 CodeGenerator::emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
-                                      Register tempReg1, Register tempReg2, JSObject *templateObj)
+                                      Register tempReg1, Register tempReg2, NativeObject *templateObj)
 {
     MOZ_ASSERT(lir->mirRaw());
     MOZ_ASSERT(lir->mirRaw()->isInstruction());
 
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
 #ifdef JSGC_FJGENERATIONAL
     OutOfLineCode *ool = oolCallVM(NewGCThingParInfo, lir,
                                    (ArgList(), Imm32(allocKind)), StoreRegisterTo(objReg));
@@ -4098,17 +4098,17 @@ CodeGenerator::visitMutateProto(LMutateP
     Register objReg = ToRegister(lir->getObject());
 
     pushArg(ToValue(lir, LMutateProto::ValueIndex));
     pushArg(objReg);
 
     return callVM(MutatePrototypeInfo, lir);
 }
 
-typedef bool(*InitPropFn)(JSContext *cx, HandleObject obj,
+typedef bool(*InitPropFn)(JSContext *cx, HandleNativeObject obj,
                           HandlePropertyName name, HandleValue value);
 static const VMFunction InitPropInfo =
     FunctionInfo<InitPropFn>(InitProp);
 
 bool
 CodeGenerator::visitInitProp(LInitProp *lir)
 {
     Register objReg = ToRegister(lir->getObject());
@@ -4187,17 +4187,17 @@ CodeGenerator::visitCreateThisWithProto(
 typedef JSObject *(*NewGCObjectFn)(JSContext *cx, gc::AllocKind allocKind,
                                    gc::InitialHeap initialHeap);
 static const VMFunction NewGCObjectInfo =
     FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject);
 
 bool
 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
 {
-    JSObject *templateObject = lir->mir()->templateObject();
+    NativeObject *templateObject = lir->mir()->templateObject();
     gc::AllocKind allocKind = templateObject->asTenured().getAllocKind();
     gc::InitialHeap initialHeap = lir->mir()->initialHeap();
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
     OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
                                    (ArgList(), Imm32(allocKind), Imm32(initialHeap)),
                                    StoreRegisterTo(objReg));
@@ -5942,17 +5942,17 @@ CodeGenerator::visitStoreElementHoleV(LS
         masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
     else
         masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
-typedef bool (*SetDenseElementFn)(JSContext *, HandleObject, int32_t, HandleValue,
+typedef bool (*SetDenseElementFn)(JSContext *, HandleNativeObject, int32_t, HandleValue,
                                   bool strict);
 typedef bool (*SetDenseElementParFn)(ForkJoinContext *, HandleObject, int32_t, HandleValue, bool);
 static const VMFunctionsModal SetDenseElementInfo = VMFunctionsModal(
     FunctionInfo<SetDenseElementFn>(SetDenseElement),
     FunctionInfo<SetDenseElementParFn>(SetDenseElementPar));
 
 bool
 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
@@ -6064,17 +6064,17 @@ CodeGenerator::emitArrayPopShift(LInstru
         if (!ool)
             return false;
     }
 
     // VM call if a write barrier is necessary.
     masm.branchTestNeedsIncrementalBarrier(Assembler::NonZero, ool->entry());
 
     // Load elements and length.
-    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
     masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
 
     // VM call if length != initializedLength.
     Int32Key key = Int32Key(lengthTemp);
     Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
     masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
 
     // Test for length != 0. On zero length either take a VM call or generate
@@ -6147,30 +6147,30 @@ CodeGenerator::visitArrayPopShiftT(LArra
 {
     Register obj = ToRegister(lir->object());
     Register elements = ToRegister(lir->temp0());
     Register length = ToRegister(lir->temp1());
     TypedOrValueRegister out(lir->mir()->type(), ToAnyRegister(lir->output()));
     return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
 }
 
-typedef bool (*ArrayPushDenseFn)(JSContext *, HandleObject, HandleValue, uint32_t *);
+typedef bool (*ArrayPushDenseFn)(JSContext *, HandleArrayObject, HandleValue, uint32_t *);
 static const VMFunction ArrayPushDenseInfo =
     FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense);
 
 bool
 CodeGenerator::emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
                              ConstantOrRegister value, Register elementsTemp, Register length)
 {
     OutOfLineCode *ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
     if (!ool)
         return false;
 
     // Load elements and length.
-    masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
     masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
 
     Int32Key key = Int32Key(length);
     Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
     Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
 
     // Guard length == initializedLength.
     masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
@@ -6222,21 +6222,21 @@ CodeGenerator::visitArrayConcat(LArrayCo
     Register rhs = ToRegister(lir->rhs());
     Register temp1 = ToRegister(lir->temp1());
     Register temp2 = ToRegister(lir->temp2());
 
     // If 'length == initializedLength' for both arrays we try to allocate an object
     // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
     // back to a slow path.
     Label fail, call;
-    masm.loadPtr(Address(lhs, JSObject::offsetOfElements()), temp1);
+    masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
     masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
-    masm.loadPtr(Address(rhs, JSObject::offsetOfElements()), temp1);
+    masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
     masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
     masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
 
     // Try to allocate an object.
     masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail);
     masm.jump(&call);
     {
         masm.bind(&fail);
@@ -6321,17 +6321,17 @@ CodeGenerator::visitIteratorStart(LItera
     // be a plain ), so we do not need to generate a loop here.
     masm.loadObjProto(obj, temp1);
     masm.loadObjProto(temp1, temp1);
     masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry());
 
     // Ensure the object does not have any elements. The presence of dense
     // elements is not captured by the shape tests above.
     masm.branchPtr(Assembler::NotEqual,
-                   Address(obj, JSObject::offsetOfElements()),
+                   Address(obj, NativeObject::offsetOfElements()),
                    ImmPtr(js::emptyObjectElements),
                    ool->entry());
 
     // Write barrier for stores to the iterator. We only need to take a write
     // barrier if NativeIterator::obj is actually going to change.
     {
 #ifdef JSGC_GENERATIONAL
         // Bug 867815: When using a nursery, we unconditionally take this out-
@@ -6566,17 +6566,17 @@ CodeGenerator::visitRunOncePrologue(LRun
     pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
     return callVM(RunOnceScriptPrologueInfo, lir);
 }
 
 
 typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
                                          HandleObject);
 typedef JSObject *(*InitRestParameterParFn)(ForkJoinContext *, uint32_t, Value *,
-                                            HandleObject, HandleObject);
+                                            HandleObject, HandleArrayObject);
 static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal(
     FunctionInfo<InitRestParameterFn>(InitRestParameter),
     FunctionInfo<InitRestParameterParFn>(InitRestParameterPar));
 
 bool
 CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
                         Register temp0, Register temp1, unsigned numFormals,
                         JSObject *templateObject, bool saveAndRestore, Register resultreg)
@@ -6619,17 +6619,17 @@ CodeGenerator::emitRest(LInstruction *li
 bool
 CodeGenerator::visitRest(LRest *lir)
 {
     Register numActuals = ToRegister(lir->numActuals());
     Register temp0 = ToRegister(lir->getTemp(0));
     Register temp1 = ToRegister(lir->getTemp(1));
     Register temp2 = ToRegister(lir->getTemp(2));
     unsigned numFormals = lir->mir()->numFormals();
-    JSObject *templateObject = lir->mir()->templateObject();
+    ArrayObject *templateObject = lir->mir()->templateObject();
 
     Label joinAlloc, failAlloc;
     masm.createGCObject(temp2, temp0, templateObject, gc::DefaultHeap, &failAlloc);
     masm.jump(&joinAlloc);
     {
         masm.bind(&failAlloc);
         masm.movePtr(ImmPtr(nullptr), temp2);
     }
@@ -6645,17 +6645,17 @@ bool
 CodeGenerator::visitRestPar(LRestPar *lir)
 {
     Register numActuals = ToRegister(lir->numActuals());
     Register cx = ToRegister(lir->forkJoinContext());
     Register temp0 = ToRegister(lir->getTemp(0));
     Register temp1 = ToRegister(lir->getTemp(1));
     Register temp2 = ToRegister(lir->getTemp(2));
     unsigned numFormals = lir->mir()->numFormals();
-    JSObject *templateObject = lir->mir()->templateObject();
+    ArrayObject *templateObject = lir->mir()->templateObject();
 
     if (!emitAllocateGCThingPar(lir, temp2, cx, temp0, temp1, templateObject))
         return false;
 
     return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject, true, ToRegister(lir->output()));
 }
 
 bool
@@ -7208,42 +7208,42 @@ CodeGenerator::visitCallInitElementArray
 
 bool
 CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV *ins)
 {
     const Register obj = ToRegister(ins->getOperand(0));
     size_t slot = ins->mir()->slot();
     ValueOperand result = GetValueOutput(ins);
 
-    masm.loadValue(Address(obj, JSObject::getFixedSlotOffset(slot)), result);
+    masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result);
     return true;
 }
 
 bool
 CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT *ins)
 {
     const Register obj = ToRegister(ins->getOperand(0));
     size_t slot = ins->mir()->slot();
     AnyRegister result = ToAnyRegister(ins->getDef(0));
     MIRType type = ins->mir()->type();
 
-    masm.loadUnboxedValue(Address(obj, JSObject::getFixedSlotOffset(slot)), type, result);
+    masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), type, result);
 
     return true;
 }
 
 bool
 CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV *ins)
 {
     const Register obj = ToRegister(ins->getOperand(0));
     size_t slot = ins->mir()->slot();
 
     const ValueOperand value = ToValue(ins, LStoreFixedSlotV::Value);
 
-    Address address(obj, JSObject::getFixedSlotOffset(slot));
+    Address address(obj, NativeObject::getFixedSlotOffset(slot));
     if (ins->mir()->needsBarrier())
         emitPreBarrier(address);
 
     masm.storeValue(value, address);
 
     return true;
 }
 
@@ -7255,17 +7255,17 @@ CodeGenerator::visitStoreFixedSlotT(LSto
 
     const LAllocation *value = ins->value();
     MIRType valueType = ins->mir()->value()->type();
 
     ConstantOrRegister nvalue = value->isConstant()
                               ? ConstantOrRegister(*value->toConstant())
                               : TypedOrValueRegister(valueType, ToAnyRegister(value));
 
-    Address address(obj, JSObject::getFixedSlotOffset(slot));
+    Address address(obj, NativeObject::getFixedSlotOffset(slot));
     if (ins->mir()->needsBarrier())
         emitPreBarrier(address);
 
     masm.storeConstantOrRegister(nvalue, address);
 
     return true;
 }
 
@@ -8590,24 +8590,24 @@ CodeGenerator::visitGetDOMProperty(LGetD
     const Register ValueReg = ToRegister(ins->getValueReg());
 
     Label haveValue;
     if (ins->mir()->valueMayBeInSlot()) {
         size_t slot = ins->mir()->domMemberSlotIndex();
         // It's a bit annoying to redo these slot calculations, which duplcate
         // LSlots and a few other things like that, but I'm not sure there's a
         // way to reuse those here.
-        if (slot < JSObject::MAX_FIXED_SLOTS) {
-            masm.loadValue(Address(ObjectReg, JSObject::getFixedSlotOffset(slot)),
+        if (slot < NativeObject::MAX_FIXED_SLOTS) {
+            masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)),
                            JSReturnOperand);
         } else {
             // It's a dynamic slot.
-            slot -= JSObject::MAX_FIXED_SLOTS;
+            slot -= NativeObject::MAX_FIXED_SLOTS;
             // Use PrivateReg as a scratch register for the slots pointer.
-            masm.loadPtr(Address(ObjectReg, JSObject::offsetOfSlots()),
+            masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()),
                          PrivateReg);
             masm.loadValue(Address(PrivateReg, slot*sizeof(js::Value)),
                            JSReturnOperand);
         }
         masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue);
     }
 
     DebugOnly<uint32_t> initialStack = masm.framePushed();
@@ -8620,17 +8620,17 @@ CodeGenerator::visitGetDOMProperty(LGetD
     // We pass the pointer to our out param as an instance of
     // JSJitGetterCallArgs, since on the binary level it's the same thing.
     JS_STATIC_ASSERT(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
     masm.movePtr(StackPointer, ValueReg);
 
     masm.Push(ObjectReg);
 
     // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
-    masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
+    masm.loadPrivate(Address(ObjectReg, NativeObject::getFixedSlotOffset(0)), PrivateReg);
 
     // Rooting will happen at GC time.
     masm.movePtr(StackPointer, ObjectReg);
 
     uint32_t safepointOffset;
     if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
         return false;
     masm.enterFakeExitFrame(IonDOMExitFrameLayout::GetterToken());
@@ -8672,17 +8672,17 @@ CodeGenerator::visitGetDOMMember(LGetDOM
     // use an LLoadFixedSlotV or some subclass of it for this case: that would
     // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
     // we'd have to duplicate a bunch of stuff we now get for free from
     // MGetDOMProperty.
     Register object = ToRegister(ins->object());
     size_t slot = ins->mir()->domMemberSlotIndex();
     ValueOperand result = GetValueOutput(ins);
 
-    masm.loadValue(Address(object, JSObject::getFixedSlotOffset(slot)), result);
+    masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), result);
     return true;
 }
 
 bool
 CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins)
 {
     const Register JSContextReg = ToRegister(ins->getJSContextReg());
     const Register ObjectReg = ToRegister(ins->getObjectReg());
@@ -8699,17 +8699,17 @@ CodeGenerator::visitSetDOMProperty(LSetD
     // We pass the pointer to our out param as an instance of
     // JSJitGetterCallArgs, since on the binary level it's the same thing.
     JS_STATIC_ASSERT(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
     masm.movePtr(StackPointer, ValueReg);
 
     masm.Push(ObjectReg);
 
     // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
-    masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
+    masm.loadPrivate(Address(ObjectReg, NativeObject::getFixedSlotOffset(0)), PrivateReg);
 
     // Rooting will happen at GC time.
     masm.movePtr(StackPointer, ObjectReg);
 
     uint32_t safepointOffset;
     if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
         return false;
     masm.enterFakeExitFrame(IonDOMExitFrameLayout::SetterToken());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -383,17 +383,17 @@ class CodeGenerator : public CodeGenerat
                             FloatRegister tempDouble, FloatRegister tempFloat32,
                             ValueOperand index, ConstantOrRegister value,
                             bool strict, bool guardHoles, jsbytecode *profilerLeavePc);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
     bool emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
                                 Register tempReg1, Register tempReg2,
-                                JSObject *templateObj);
+                                NativeObject *templateObj);
 
     bool emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg);
 
     void emitLambdaInit(Register resultReg, Register scopeChainReg,
                         const LambdaFunctionInfo &info);
 
     bool emitFilterArgumentsOrEval(LInstruction *lir, Register string, Register temp1,
                                    Register temp2);
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2684,17 +2684,17 @@ jit::ConvertLinearInequality(TempAllocat
     compare->setCompareType(MCompare::Compare_Int32);
 
     return compare;
 }
 
 static bool
 AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
                   MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted,
-                  HandleObject baseobj,
+                  HandleNativeObject baseobj,
                   Vector<types::TypeNewScript::Initializer> *initializerList,
                   Vector<PropertyName *> *accessedProperties,
                   bool *phandled)
 {
     // Determine the effect that a use of the |this| value when calling |new|
     // on a script has on the properties definitely held by the new object.
 
     if (ins->isCallSetProperty()) {
@@ -2709,17 +2709,17 @@ AnalyzePoppedThis(JSContext *cx, types::
         if (setprop->name() == cx->names().prototype ||
             setprop->name() == cx->names().proto ||
             setprop->name() == cx->names().constructor)
         {
             return true;
         }
 
         // Ignore assignments to properties that were already written to.
-        if (baseobj->nativeLookup(cx, NameToId(setprop->name()))) {
+        if (baseobj->lookup(cx, NameToId(setprop->name()))) {
             *phandled = true;
             return true;
         }
 
         // Don't add definite properties for properties that were already
         // read in the constructor.
         for (size_t i = 0; i < accessedProperties->length(); i++) {
             if ((*accessedProperties)[i] == setprop->name())
@@ -2739,17 +2739,17 @@ AnalyzePoppedThis(JSContext *cx, types::
         if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
             // The prototype chain already contains a getter/setter for this
             // property, or type information is too imprecise.
             return true;
         }
 
         // Add the property to the object, being careful not to update type information.
         DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
-        MOZ_ASSERT(!baseobj->nativeContainsPure(id));
+        MOZ_ASSERT(!baseobj->containsPure(id));
         if (!baseobj->addDataProperty(cx, id, baseobj->slotSpan(), JSPROP_ENUMERATE))
             return false;
         MOZ_ASSERT(baseobj->slotSpan() != slotSpan);
         MOZ_ASSERT(!baseobj->inDictionaryMode());
 
         Vector<MResumePoint *> callerResumePoints(cx);
         for (MResumePoint *rp = ins->block()->callerResumePoint();
              rp;
@@ -2788,17 +2788,17 @@ AnalyzePoppedThis(JSContext *cx, types::
          *   could cause 'this' to escape.
          *
          * - The accessed property is either already a definite property or
          *   is not later added as one. Since the definite properties are
          *   added to the object at the point of its creation, reading a
          *   definite property before it is assigned could incorrectly hit.
          */
         RootedId id(cx, NameToId(get->name()));
-        if (!baseobj->nativeLookup(cx, id) && !accessedProperties->append(get->name()))
+        if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name()))
             return false;
 
         if (!types::AddClearDefiniteGetterSetterForPrototypeChain(cx, type, id)) {
             // The |this| value can escape if any property reads it does go
             // through a getter.
             return true;
         }
 
@@ -2818,17 +2818,17 @@ static int
 CmpInstructions(const void *a, const void *b)
 {
     return (*static_cast<MInstruction * const *>(a))->id() -
            (*static_cast<MInstruction * const *>(b))->id();
 }
 
 bool
 jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
-                                        types::TypeObject *type, HandleObject baseobj,
+                                        types::TypeObject *type, HandleNativeObject baseobj,
                                         Vector<types::TypeNewScript::Initializer> *initializerList)
 {
     MOZ_ASSERT(cx->compartment()->activeAnalysis);
 
     // When invoking 'new' on the specified script, try to find some properties
     // which will definitely be added to the created object before it has a
     // chance to escape and be accessed elsewhere.
 
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -157,17 +157,17 @@ ConvertLinearSum(TempAllocator &alloc, M
 
 // Convert the test 'sum >= 0' to a comparison, adding any necessary
 // instructions to the end of block.
 MCompare *
 ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
 
 bool
 AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
-                                   types::TypeObject *type, HandleObject baseobj,
+                                   types::TypeObject *type, HandleNativeObject baseobj,
                                    Vector<types::TypeNewScript::Initializer> *initializerList);
 
 bool
 AnalyzeArgumentsUsage(JSContext *cx, JSScript *script);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -24,16 +24,17 @@
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/CompileInfo-inl.h"
 #include "jit/ExecutionMode-inl.h"
+#include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 
@@ -5804,17 +5805,17 @@ IonBuilder::jsop_compare(JSOp op)
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 bool
 IonBuilder::jsop_newarray(uint32_t count)
 {
-    JSObject *templateObject = inspector->getTemplateObject(pc);
+    NativeObject *templateObject = inspector->getTemplateObject(pc);
     if (!templateObject) {
         if (info().executionMode() == ArgumentsUsageAnalysis) {
             MUnknownValue *unknown = MUnknownValue::New(alloc());
             current->add(unknown);
             current->push(unknown);
             return true;
         }
         return abort("No template object for NEWARRAY");
@@ -5850,17 +5851,17 @@ IonBuilder::jsop_newarray(uint32_t count
     else
         templateObject->clearShouldConvertDoubleElements();
     return true;
 }
 
 bool
 IonBuilder::jsop_newarray_copyonwrite()
 {
-    JSObject *templateObject = types::GetCopyOnWriteObject(script(), pc);
+    ArrayObject *templateObject = types::GetCopyOnWriteObject(script(), pc);
 
     // The baseline compiler should have ensured the template object has a type
     // with the copy on write flag set already. During the arguments usage
     // analysis the baseline compiler hasn't run yet, however, though in this
     // case the template object's type doesn't matter.
     MOZ_ASSERT_IF(info().executionMode() != ArgumentsUsageAnalysis,
                   templateObject->type()->hasAnyFlags(types::OBJECT_FLAG_COPY_ON_WRITE));
 
@@ -5953,17 +5954,17 @@ IonBuilder::jsop_initelem_array()
 
     MConstant *id = MConstant::New(alloc(), Int32Value(GET_UINT24(pc)));
     current->add(id);
 
     // Get the elements vector.
     MElements *elements = MElements::New(alloc(), obj);
     current->add(elements);
 
-    JSObject *templateObject = obj->toNewArray()->templateObject();
+    NativeObject *templateObject = obj->toNewArray()->templateObject();
 
     if (templateObject->shouldConvertDoubleElements()) {
         MInstruction *valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
     }
 
     // Store the value.
@@ -5994,17 +5995,17 @@ IonBuilder::jsop_mutateproto()
 }
 
 bool
 IonBuilder::jsop_initprop(PropertyName *name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
-    JSObject *templateObject = nullptr;
+    NativeObject *templateObject = nullptr;
     Shape *shape = nullptr;
 
     bool useSlowPath = false;
 
     if (obj->isUnknownValue()) {
         useSlowPath = true;
     } else {
         templateObject = obj->toNewObject()->templateObject();
@@ -8571,18 +8572,17 @@ IonBuilder::jsop_arguments()
     MOZ_ASSERT(lazyArguments_);
     current->push(lazyArguments_);
     return true;
 }
 
 bool
 IonBuilder::jsop_rest()
 {
-    JSObject *templateObject = inspector->getTemplateObject(pc);
-    MOZ_ASSERT(templateObject->is<ArrayObject>());
+    ArrayObject *templateObject = &inspector->getTemplateObject(pc)->as<ArrayObject>();
 
     if (inliningDepth_ == 0) {
         // We don't know anything about the callee.
         MArgumentsLength *numActuals = MArgumentsLength::New(alloc());
         current->add(numActuals);
 
         // Pass in the number of actual arguments, the number of formals (not
         // including the rest parameter slot itself), and the template object.
@@ -9318,23 +9318,23 @@ IonBuilder::getPropTryDefiniteSlot(bool 
 
     if (obj->type() != MIRType_Object) {
         MGuardObject *guard = MGuardObject::New(alloc(), obj);
         current->add(guard);
         obj = guard;
     }
 
     MInstruction *load;
-    if (slot < JSObject::MAX_FIXED_SLOTS) {
+    if (slot < NativeObject::MAX_FIXED_SLOTS) {
         load = MLoadFixedSlot::New(alloc(), obj, slot);
     } else {
         MInstruction *slots = MSlots::New(alloc(), obj);
         current->add(slots);
 
-        load = MLoadSlot::New(alloc(), slots, slot - JSObject::MAX_FIXED_SLOTS);
+        load = MLoadSlot::New(alloc(), slots, slot - NativeObject::MAX_FIXED_SLOTS);
     }
 
     if (barrier == BarrierKind::NoBarrier)
         load->setResultType(types->getKnownMIRType());
 
     current->add(load);
     current->push(load);
 
@@ -9972,25 +9972,25 @@ IonBuilder::setPropTryDefiniteSlot(bool 
 
         types::HeapTypeSetKey property = type->property(NameToId(name));
         if (property.nonWritable(constraints()))
             return true;
         writeBarrier |= property.needsBarrier(constraints());
     }
 
     MInstruction *store;
-    if (slot < JSObject::MAX_FIXED_SLOTS) {
+    if (slot < NativeObject::MAX_FIXED_SLOTS) {
         store = MStoreFixedSlot::New(alloc(), obj, slot, value);
         if (writeBarrier)
             store->toStoreFixedSlot()->setNeedsBarrier();
     } else {
         MInstruction *slots = MSlots::New(alloc(), obj);
         current->add(slots);
 
-        store = MStoreSlot::New(alloc(), slots, slot - JSObject::MAX_FIXED_SLOTS, value);
+        store = MStoreSlot::New(alloc(), slots, slot - NativeObject::MAX_FIXED_SLOTS, value);
         if (writeBarrier)
             store->toStoreSlot()->setNeedsBarrier();
     }
 
     current->add(store);
     current->push(value);
 
     if (!resumeAfter(store))
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -649,25 +649,25 @@ IsCacheableGetPropCallPropertyOp(JSObjec
 
     if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter())
         return false;
 
     return true;
 }
 
 static inline void
-EmitLoadSlot(MacroAssembler &masm, JSObject *holder, Shape *shape, Register holderReg,
+EmitLoadSlot(MacroAssembler &masm, NativeObject *holder, Shape *shape, Register holderReg,
              TypedOrValueRegister output, Register scratchReg)
 {
     MOZ_ASSERT(holder);
     if (holder->isFixedSlot(shape->slot())) {
-        Address addr(holderReg, JSObject::getFixedSlotOffset(shape->slot()));
+        Address addr(holderReg, NativeObject::getFixedSlotOffset(shape->slot()));
         masm.loadTypedOrValue(addr, output);
     } else {
-        masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
+        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
 
         Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
         masm.loadTypedOrValue(addr, output);
     }
 }
 
 static void
 GenerateDOMProxyChecks(JSContext *cx, MacroAssembler &masm, JSObject *obj,
@@ -676,17 +676,17 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
     // Guard the following:
     //      1. The object is a DOMProxy.
     //      2. The object does not have expando properties, or has an expando
     //          which is known to not have the desired property.
     Address handlerAddr(object, ProxyObject::offsetOfHandler());
-    Address expandoSlotAddr(object, JSObject::getFixedSlotOffset(GetDOMProxyExpandoSlot()));
+    Address expandoSlotAddr(object, NativeObject::getFixedSlotOffset(GetDOMProxyExpandoSlot()));
 
     // Check that object is a DOMProxy.
     masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr,
                           ImmPtr(obj->as<ProxyObject>().handler()), stubFailure);
 
     if (skipExpandoCheck)
         return;
 
@@ -695,17 +695,17 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
     RegisterSet domProxyRegSet(RegisterSet::All());
     domProxyRegSet.take(AnyRegister(object));
     ValueOperand tempVal = domProxyRegSet.takeValueOperand();
     masm.pushValue(tempVal);
 
     Label failDOMProxyCheck;
     Label domProxyOk;
 
-    Value expandoVal = obj->getFixedSlot(GetDOMProxyExpandoSlot());
+    Value expandoVal = obj->fakeNativeGetSlot(GetDOMProxyExpandoSlot());
     masm.loadValue(expandoSlotAddr, tempVal);
 
     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
         masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failDOMProxyCheck);
 
         ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
         masm.movePtr(ImmPtr(expandoAndGeneration), tempVal.scratchReg());
 
@@ -721,17 +721,17 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
                        tempVal);
     }
 
     // If the incoming object does not have an expando object then we're sure we're not
     // shadowing.
     masm.branchTestUndefined(Assembler::Equal, tempVal, &domProxyOk);
 
     if (expandoVal.isObject()) {
-        MOZ_ASSERT(!expandoVal.toObject().nativeContains(cx, name));
+        MOZ_ASSERT(!expandoVal.toObject().as<NativeObject>().contains(cx, name));
 
         // Reference object has an expando object that doesn't define the name. Check that
         // the incoming object has an expando object with the same shape.
         masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck);
         masm.extractObject(tempVal, tempVal.scratchReg());
         masm.branchPtr(Assembler::Equal,
                        Address(tempVal.scratchReg(), JSObject::offsetOfShape()),
                        ImmGCPtr(expandoVal.toObject().lastProperty()),
@@ -745,17 +745,17 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
 
     // Success case: restore the tempval and proceed.
     masm.bind(&domProxyOk);
     masm.popValue(tempVal);
 }
 
 static void
 GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
-                 IonCache::StubAttacher &attacher, JSObject *obj, JSObject *holder,
+                 IonCache::StubAttacher &attacher, JSObject *obj, NativeObject *holder,
                  Shape *shape, Register object, TypedOrValueRegister output,
                  Label *failures = nullptr)
 {
     MOZ_ASSERT(obj->isNative());
     // 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 = (obj != holder) || (failures != nullptr && failures->used());
@@ -1070,17 +1070,17 @@ GenerateArrayLength(JSContext *cx, Macro
     Register outReg;
     if (output.hasValue()) {
         outReg = output.valueReg().scratchReg();
     } else {
         MOZ_ASSERT(output.type() == MIRType_Int32);
         outReg = output.typedReg().gpr();
     }
 
-    masm.loadPtr(Address(object, JSObject::offsetOfElements()), outReg);
+    masm.loadPtr(Address(object, NativeObject::offsetOfElements()), outReg);
     masm.load32(Address(outReg, ObjectElements::offsetOfLength()), outReg);
 
     // The length is an unsigned int, but the value encodes a signed int.
     MOZ_ASSERT(object != outReg);
     masm.branchTest32(Assembler::Signed, outReg, outReg, &failures);
 
     if (output.hasValue())
         masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
@@ -1147,17 +1147,17 @@ IsCacheableArrayLength(JSContext *cx, Ha
 
     return true;
 }
 
 template <class GetPropCache>
 static GetPropertyIC::NativeGetPropCacheability
 CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &cache,
                        HandleObject obj, HandlePropertyName name,
-                       MutableHandleObject holder, MutableHandleShape shape,
+                       MutableHandleNativeObject holder, MutableHandleShape shape,
                        bool skipArrayLen = false)
 {
     if (!obj || !obj->isNative())
         return GetPropertyIC::CanAttachNone;
 
     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     // of turn. We don't mind doing this even when purity isn't required, because we
     // only miss out on shape hashification, which is only a temporary perf cost.
@@ -1236,17 +1236,17 @@ GetPropertyIC::tryAttachNative(JSContext
                                HandleObject obj, HandlePropertyName name,
                                void *returnAddr, bool *emitted)
 {
     MOZ_ASSERT(canAttachStub());
     MOZ_ASSERT(!*emitted);
     MOZ_ASSERT(outerScript->ionScript() == ion);
 
     RootedShape shape(cx);
-    RootedObject holder(cx);
+    RootedNativeObject holder(cx);
 
     NativeGetPropCacheability type =
         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape);
     if (type == CanAttachNone)
         return true;
 
     *emitted = true;
 
@@ -1443,17 +1443,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
 {
     MOZ_ASSERT(canAttachStub());
     MOZ_ASSERT(!*emitted);
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     MOZ_ASSERT(monitoredResult());
     MOZ_ASSERT(output().hasValue());
 
     RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull());
-    RootedObject holder(cx);
+    RootedNativeObject holder(cx);
     RootedShape shape(cx);
 
     NativeGetPropCacheability canCache =
         CanAttachNativeGetProp(cx, *this, checkObj, name, &holder, &shape,
                                /* skipArrayLen = */true);
     MOZ_ASSERT(canCache != CanAttachArrayLength);
 
     if (canCache == CanAttachNone)
@@ -1839,17 +1839,17 @@ GetPropertyParIC::reset()
 {
     ParallelIonCache::reset();
     hasTypedArrayLengthStub_ = false;
     hasSharedTypedArrayLengthStub_ = false;
 }
 
 bool
 GetPropertyParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
-                                 HandleObject holder, HandleShape shape)
+                                 HandleNativeObject holder, HandleShape shape)
 {
     // Ready to generate the read slot stub.
     DispatchStubPrepender attacher(*this);
     MacroAssembler masm(cx, ion);
     GenerateReadSlot(cx, ion, masm, attacher, obj, holder, shape, object(), output());
 
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel reading");
 }
@@ -1905,17 +1905,17 @@ GetPropertyParIC::update(ForkJoinContext
             if (alreadyStubbed)
                 return true;
 
             // See note about the stub limit in GetPropertyCache.
             bool attachedStub = false;
 
             {
                 RootedShape shape(ncx);
-                RootedObject holder(ncx);
+                RootedNativeObject holder(ncx);
                 RootedPropertyName name(ncx, cache.name());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
                     CanAttachNativeGetProp(ncx, cache, obj, name, &holder, &shape);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot) {
                     if (!cache.attachReadSlot(ncx, ion, obj, holder, shape))
                         return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
@@ -1958,17 +1958,17 @@ IonCache::reset()
 
 void
 IonCache::destroy()
 {
 }
 
 static void
 GenerateSetSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                JSObject *obj, Shape *shape, Register object, ConstantOrRegister value,
+                NativeObject *obj, Shape *shape, Register object, ConstantOrRegister value,
                 bool needsTypeBarrier, bool checkTypeset)
 {
     MOZ_ASSERT(obj->isNative());
 
     Label failures, barrierFailure;
     masm.branchPtr(Assembler::NotEqual,
                    Address(object, JSObject::offsetOfShape()),
                    ImmGCPtr(obj->lastProperty()), &failures);
@@ -1998,25 +1998,25 @@ GenerateSetSlot(JSContext *cx, MacroAsse
             masm.push(scratchReg);
 
             masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
             masm.pop(object);
         }
     }
 
     if (obj->isFixedSlot(shape->slot())) {
-        Address addr(object, JSObject::getFixedSlotOffset(shape->slot()));
+        Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
 
         if (cx->zone()->needsIncrementalBarrier())
             masm.callPreBarrier(addr, MIRType_Value);
 
         masm.storeConstantOrRegister(value, addr);
     } else {
         Register slotsReg = object;
-        masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg);
+        masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
 
         Address addr(slotsReg, obj->dynamicSlotIndex(shape->slot()) * sizeof(Value));
 
         if (cx->zone()->needsIncrementalBarrier())
             masm.callPreBarrier(addr, MIRType_Value);
 
         masm.storeConstantOrRegister(value, addr);
     }
@@ -2029,17 +2029,17 @@ GenerateSetSlot(JSContext *cx, MacroAsse
     }
 
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 }
 
 bool
 SetPropertyIC::attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                             HandleObject obj, HandleShape shape, bool checkTypeset)
+                             HandleNativeObject obj, HandleShape shape, bool checkTypeset)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
                     checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "setting");
 }
 
@@ -2518,17 +2518,17 @@ SetPropertyIC::attachCallSetter(JSContex
     masm.bind(&failure);
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "setter call");
 }
 
 static void
 GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                JSObject *obj, Shape *oldShape, types::TypeObject *oldType,
+                NativeObject *obj, Shape *oldShape, types::TypeObject *oldType,
                 Register object, ConstantOrRegister value,
                 bool checkTypeset)
 {
     MOZ_ASSERT(obj->isNative());
 
     Label failures;
 
     // Guard the type of the object
@@ -2604,22 +2604,22 @@ GenerateAddSlot(JSContext *cx, MacroAsse
         masm.bind(&noTypeChange);
         masm.pop(object);
         masm.bind(&skipPop);
     }
 
     // Set the value on the object. Since this is an add, obj->lastProperty()
     // must be the shape of the property we are adding.
     if (obj->isFixedSlot(newShape->slot())) {
-        Address addr(object, JSObject::getFixedSlotOffset(newShape->slot()));
+        Address addr(object, NativeObject::getFixedSlotOffset(newShape->slot()));
         masm.storeConstantOrRegister(value, addr);
     } else {
         Register slotsReg = object;
 
-        masm.loadPtr(Address(object, JSObject::offsetOfSlots()), slotsReg);
+        masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), slotsReg);
 
         Address addr(slotsReg, obj->dynamicSlotIndex(newShape->slot()) * sizeof(Value));
         masm.storeConstantOrRegister(value, addr);
     }
 
     // Success.
     attacher.jumpRejoin(masm);
 
@@ -2628,17 +2628,17 @@ GenerateAddSlot(JSContext *cx, MacroAsse
     masm.pop(object);
     masm.bind(&failures);
 
     attacher.jumpNextStub(masm);
 }
 
 bool
 SetPropertyIC::attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                             HandleObject obj, HandleShape oldShape, HandleTypeObject oldType,
+                             HandleNativeObject obj, HandleShape oldShape, HandleTypeObject oldType,
                              bool checkTypeset)
 {
     MOZ_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
 
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
     GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldType, object(), value(), checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "adding");
@@ -2678,24 +2678,22 @@ CanInlineSetPropTypeCheck(JSObject *obj,
         }
     }
 
     *checkTypeset = shouldCheck;
     return true;
 }
 
 static bool
-IsPropertySetInlineable(HandleObject obj, HandleId id, MutableHandleShape pshape,
+IsPropertySetInlineable(NativeObject *obj, HandleId id, MutableHandleShape pshape,
                         ConstantOrRegister val, bool needsTypeBarrier, bool *checkTypeset)
 {
-    MOZ_ASSERT(obj->isNative());
-
     // Do a pure non-proto chain climbing lookup. See note in
     // CanAttachNativeGetProp.
-    pshape.set(obj->nativeLookupPure(id));
+    pshape.set(obj->lookupPure(id));
 
     if (!pshape)
         return false;
 
     if (!pshape->hasSlot())
         return false;
 
     if (!pshape->hasDefaultSetter())
@@ -2706,26 +2704,24 @@ IsPropertySetInlineable(HandleObject obj
 
     if (needsTypeBarrier)
         return CanInlineSetPropTypeCheck(obj, id, val, checkTypeset);
 
     return true;
 }
 
 static bool
-IsPropertyAddInlineable(HandleObject obj, HandleId id, ConstantOrRegister val, uint32_t oldSlots,
+IsPropertyAddInlineable(NativeObject *obj, HandleId id, ConstantOrRegister val, uint32_t oldSlots,
                         HandleShape oldShape, bool needsTypeBarrier, bool *checkTypeset)
 {
-    MOZ_ASSERT(obj->isNative());
-
     // If the shape of the object did not change, then this was not an add.
     if (obj->lastProperty() == oldShape)
         return false;
 
-    Shape *shape = obj->nativeLookupPure(id);
+    Shape *shape = obj->lookupPure(id);
     if (!shape || shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter())
         return false;
 
     // If we have a shape at this point and the object's shape changed, then
     // the shape must be the one we just added.
     MOZ_ASSERT(shape == obj->lastProperty());
 
     // If object has a non-default resolve hook, don't inline
@@ -2744,17 +2740,17 @@ IsPropertyAddInlineable(HandleObject obj
     // 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
-        Shape *protoShape = proto->nativeLookupPure(id);
+        Shape *protoShape = proto->as<NativeObject>().lookupPure(id);
         if (protoShape && !protoShape->hasDefaultSetter())
             return false;
 
         // Otherwise, 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;
@@ -2776,24 +2772,24 @@ IsPropertyAddInlineable(HandleObject obj
         return CanInlineSetPropTypeCheck(obj, id, val, checkTypeset);
 
     *checkTypeset = false;
     return true;
 }
 
 static SetPropertyIC::NativeSetPropCacheability
 CanAttachNativeSetProp(HandleObject obj, HandleId id, ConstantOrRegister val,
-                       bool needsTypeBarrier, MutableHandleObject holder,
+                       bool needsTypeBarrier, MutableHandleNativeObject holder,
                        MutableHandleShape shape, bool *checkTypeset)
 {
     if (!obj->isNative())
         return SetPropertyIC::CanAttachNone;
 
     // See if the property exists on the object.
-    if (IsPropertySetInlineable(obj, id, shape, val, needsTypeBarrier, checkTypeset))
+    if (IsPropertySetInlineable(&obj->as<NativeObject>(), id, shape, val, needsTypeBarrier, checkTypeset))
         return SetPropertyIC::CanAttachSetSlot;
 
     // If we couldn't find the property on the object itself, do a full, but
     // still pure lookup for setters.
     if (!LookupPropertyPure(obj, id, holder.address(), shape.address()))
         return SetPropertyIC::CanAttachNone;
 
     // If the object doesn't have the property, we don't know if we can attach
@@ -2854,48 +2850,51 @@ SetPropertyIC::update(JSContext *cx, siz
             if (!addedSetterStub && !cache.hasGenericProxyStub()) {
                 if (!cache.attachGenericProxy(cx, script, ion, returnAddr))
                     return false;
                 addedSetterStub = true;
             }
         }
 
         RootedShape shape(cx);
-        RootedObject holder(cx);
+        RootedNativeObject holder(cx);
         bool checkTypeset;
         canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
                                           &holder, &shape, &checkTypeset);
 
         if (!addedSetterStub && canCache == CanAttachSetSlot) {
-            if (!cache.attachSetSlot(cx, script, ion, obj, shape, checkTypeset))
+            RootedNativeObject nobj(cx, &obj->as<NativeObject>());
+            if (!cache.attachSetSlot(cx, script, ion, nobj, shape, checkTypeset))
                 return false;
             addedSetterStub = true;
         }
 
         if (!addedSetterStub && canCache == CanAttachCallSetter) {
             if (!cache.attachCallSetter(cx, script, ion, obj, holder, shape, returnAddr))
                 return false;
             addedSetterStub = true;
         }
     }
 
-    uint32_t oldSlots = obj->numDynamicSlots();
+    uint32_t oldSlots = obj->fakeNativeNumDynamicSlots();
     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(), cache.pc()))
         return false;
 
     // The property did not exist before, now we can try to inline the property add.
     bool checkTypeset;
     if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
-        IsPropertyAddInlineable(obj, id, cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
+        IsPropertyAddInlineable(&obj->as<NativeObject>(), id,
+                                cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
                                 &checkTypeset))
     {
-        if (!cache.attachAddSlot(cx, script, ion, obj, oldShape, oldType, checkTypeset))
+        RootedNativeObject nobj(cx, &obj->as<NativeObject>());
+        if (!cache.attachAddSlot(cx, script, ion, nobj, oldShape, oldType, checkTypeset))
             return false;
     }
 
     return true;
 }
 
 void
 SetPropertyIC::reset()
@@ -2911,95 +2910,100 @@ SetPropertyParIC::update(ForkJoinContext
     MOZ_ASSERT(cx->isThreadLocal(obj));
 
     IonScript *ion = GetTopIonJSScript(cx)->parallelIonScript();
     SetPropertyParIC &cache = ion->getCache(cacheIndex).toSetPropertyPar();
 
     RootedValue v(cx, value);
     RootedId id(cx, AtomToId(cache.name()));
 
+    if (!obj->isNative())
+        return false;
+    RootedNativeObject nobj(cx, &obj->as<NativeObject>());
+
     // Avoid unnecessary locking if cannot attach stubs.
     if (!cache.canAttachStub()) {
         return baseops::SetPropertyHelper<ParallelExecution>(
-            cx, obj, obj, id, baseops::Qualified, &v, cache.strict());
+            cx, nobj, nobj, id, baseops::Qualified, &v, cache.strict());
     }
 
     SetPropertyIC::NativeSetPropCacheability canCache = SetPropertyIC::CanAttachNone;
     bool attachedStub = false;
 
     {
         // See note about locking context in GetPropertyParIC::update.
         LockedJSContext ncx(cx);
 
         if (cache.canAttachStub()) {
             bool alreadyStubbed;
-            if (!cache.hasOrAddStubbedShape(ncx, obj->lastProperty(), &alreadyStubbed))
+            if (!cache.hasOrAddStubbedShape(ncx, nobj->lastProperty(), &alreadyStubbed))
                 return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
             if (alreadyStubbed) {
                 return baseops::SetPropertyHelper<ParallelExecution>(
-                    cx, obj, obj, id, baseops::Qualified, &v, cache.strict());
+                    cx, nobj, nobj, id, baseops::Qualified, &v, cache.strict());
             }
 
             // If the object has a lazy type, we need to de-lazify it, but
             // this is not safe in parallel.
-            if (obj->hasLazyType())
+            if (nobj->hasLazyType())
                 return false;
 
             {
                 RootedShape shape(cx);
-                RootedObject holder(cx);
+                RootedNativeObject holder(cx);
                 bool checkTypeset;
-                canCache = CanAttachNativeSetProp(obj, id, cache.value(), cache.needsTypeBarrier(),
+                canCache = CanAttachNativeSetProp(nobj, id, cache.value(), cache.needsTypeBarrier(),
                                                   &holder, &shape, &checkTypeset);
 
                 if (canCache == SetPropertyIC::CanAttachSetSlot) {
-                    if (!cache.attachSetSlot(ncx, ion, obj, shape, checkTypeset))
+                    if (!cache.attachSetSlot(ncx, ion, nobj, shape, checkTypeset))
                         return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
                     attachedStub = true;
                 }
             }
         }
     }
 
-    uint32_t oldSlots = obj->numDynamicSlots();
-    RootedShape oldShape(cx, obj->lastProperty());
-    RootedTypeObject oldType(cx, obj->type());
-
-    if (!baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, baseops::Qualified, &v,
+    uint32_t oldSlots = nobj->numDynamicSlots();
+    RootedShape oldShape(cx, nobj->lastProperty());
+    RootedTypeObject oldType(cx, nobj->type());
+
+    if (!baseops::SetPropertyHelper<ParallelExecution>(cx, nobj, nobj, id, baseops::Qualified, &v,
                                                        cache.strict()))
     {
         return false;
     }
 
     bool checkTypeset;
     if (!attachedStub && canCache == SetPropertyIC::MaybeCanAttachAddSlot &&
-        IsPropertyAddInlineable(obj, id, cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
+        IsPropertyAddInlineable(nobj, id,
+                                cache.value(), oldSlots, oldShape, cache.needsTypeBarrier(),
                                 &checkTypeset))
     {
         LockedJSContext ncx(cx);
-        if (cache.canAttachStub() && !cache.attachAddSlot(ncx, ion, obj, oldShape, oldType, checkTypeset))
+        if (cache.canAttachStub() && !cache.attachAddSlot(ncx, ion, nobj, oldShape, oldType, checkTypeset))
             return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
     }
 
     return true;
 }
 
 bool
-SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+SetPropertyParIC::attachSetSlot(LockedJSContext &cx, IonScript *ion, HandleNativeObject obj,
                                 HandleShape shape, bool checkTypeset)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
                     checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel setting");
 }
 
 bool
-SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+SetPropertyParIC::attachAddSlot(LockedJSContext &cx, IonScript *ion, HandleNativeObject obj,
                                 HandleShape oldShape, HandleTypeObject oldType, bool checkTypeset)
 {
     MOZ_ASSERT_IF(!needsTypeBarrier(), !checkTypeset);
 
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
     GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldType, object(), value(), checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "parallel adding");
@@ -3033,17 +3037,17 @@ EqualStringsHelper(JSString *str1, JSStr
 
 bool
 GetElementIC::attachGetProp(JSContext *cx, HandleScript outerScript, IonScript *ion,
                             HandleObject obj, const Value &idval, HandlePropertyName name,
                             void *returnAddr)
 {
     MOZ_ASSERT(index().reg().hasValue());
 
-    RootedObject holder(cx);
+    RootedNativeObject holder(cx);
     RootedShape shape(cx);
 
     GetPropertyIC::NativeGetPropCacheability canCache =
         CanAttachNativeGetProp(cx, *this, obj, name, &holder, &shape,
                                /* skipArrayLen =*/true);
 
     bool cacheable = canCache == GetPropertyIC::CanAttachReadSlot ||
                      (canCache == GetPropertyIC::CanAttachCallGetter &&
@@ -3160,17 +3164,17 @@ GenerateDenseElement(JSContext *cx, Macr
         masm.unboxInt32(val, indexReg);
     } else {
         MOZ_ASSERT(!index.reg().typedReg().isFloat());
         indexReg = index.reg().typedReg().gpr();
     }
 
     // Load elements vector.
     masm.push(object);
-    masm.loadPtr(Address(object, JSObject::offsetOfElements()), object);
+    masm.loadPtr(Address(object, NativeObject::offsetOfElements()), object);
 
     Label hole;
 
     // Guard on the initialized length.
     Address initLength(object, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, indexReg, &hole);
 
     // Check for holes & load the value.
@@ -3668,17 +3672,17 @@ GenerateSetDenseElement(JSContext *cx, M
     masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
 
     // Unbox the index.
     Register index = masm.extractInt32(indexVal, tempToUnboxIndex);
 
     {
         // Load obj->elements.
         Register elements = temp;
-        masm.loadPtr(Address(object, JSObject::offsetOfElements()), elements);
+        masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements);
 
         // Compute the location of the element.
         BaseIndex target(elements, index, TimesEight);
 
         // If TI cannot help us deal with HOLES by preventing indexed properties
         // on the prototype chain, we have to be very careful to check for ourselves
         // to avoid stomping on what should be a setter call. Start by only allowing things
         // within the initialized length.
@@ -3959,17 +3963,17 @@ SetElementParIC::update(ForkJoinContext 
         }
     }
 
     return SetElementPar(cx, obj, idval, value, cache.strict());
 }
 
 bool
 GetElementParIC::attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
-                                const Value &idval, HandlePropertyName name, HandleObject holder,
+                                const Value &idval, HandlePropertyName name, HandleNativeObject holder,
                                 HandleShape shape)
 {
     MacroAssembler masm(cx, ion);
     DispatchStubPrepender attacher(*this);
 
     // Guard on the index value.
     Label failures;
     ValueOperand val = index().reg().valueReg();
@@ -4035,17 +4039,17 @@ GetElementParIC::update(ForkJoinContext 
             if (!ValueToIdPure(idval, &id))
                 return false;
 
             bool attachedStub = false;
             if (cache.monitoredResult() &&
                 GetElementIC::canAttachGetProp(obj, idval, id))
             {
                 RootedShape shape(ncx);
-                RootedObject holder(ncx);
+                RootedNativeObject holder(ncx);
                 RootedPropertyName name(ncx, JSID_TO_ATOM(id)->asPropertyName());
 
                 GetPropertyIC::NativeGetPropCacheability canCache =
                     CanAttachNativeGetProp(ncx, cache, obj, name, &holder, &shape);
 
                 if (canCache == GetPropertyIC::CanAttachReadSlot)
                 {
                     if (!cache.attachReadSlot(ncx, ion, obj, idval, name, holder, shape))
@@ -4240,17 +4244,17 @@ BindNameIC::update(JSContext *cx, size_t
     }
 
     return holder;
 }
 
 bool
 NameIC::attachReadSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
                        HandleObject scopeChain, HandleObject holderBase,
-                       HandleObject holder, HandleShape shape)
+                       HandleNativeObject holder, HandleShape shape)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     Label failures;
     RepatchStubAppender attacher(*this);
 
     Register scratchReg = outputReg().valueReg().scratchReg();
 
     // Don't guard the base of the proto chain the name was found on. It will be guarded
@@ -4376,18 +4380,21 @@ NameIC::update(JSContext *cx, size_t cac
     RootedObject obj(cx);
     RootedObject holder(cx);
     RootedShape shape(cx);
     if (!LookupName(cx, name, scopeChain, &obj, &holder, &shape))
         return false;
 
     if (cache.canAttachStub()) {
         if (IsCacheableNameReadSlot(scopeChain, obj, holder, shape, pc, cache.outputReg())) {
-            if (!cache.attachReadSlot(cx, outerScript, ion, scopeChain, obj, holder, shape))
+            if (!cache.attachReadSlot(cx, outerScript, ion, scopeChain, obj,
+                                      holder.as<NativeObject>(), shape))
+            {
                 return false;
+            }
         } else if (IsCacheableNameCallGetter(scopeChain, obj, holder, shape)) {
             if (!cache.attachCallGetter(cx, outerScript, ion, scopeChain, obj, holder, shape, returnAddr))
                 return false;
         }
     }
 
     if (cache.isTypeOf()) {
         if (!FetchName<true>(cx, obj, holder, name, shape, vp))
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -733,24 +733,24 @@ class SetPropertyIC : public RepatchIonC
     enum NativeSetPropCacheability {
         CanAttachNone,
         CanAttachSetSlot,
         MaybeCanAttachAddSlot,
         CanAttachCallSetter
     };
 
     bool attachSetSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                       HandleObject obj, HandleShape shape, bool checkTypeset);
+                       HandleNativeObject obj, HandleShape shape, bool checkTypeset);
 
     bool attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
                           HandleObject obj, HandleObject holder, HandleShape shape,
                           void *returnAddr);
 
     bool attachAddSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
-                       HandleObject obj, HandleShape oldShape, HandleTypeObject oldType,
+                       HandleNativeObject obj, HandleShape oldShape, HandleTypeObject oldType,
                        bool checkTypeset);
 
     bool attachGenericProxy(JSContext *cx, HandleScript outerScript, IonScript *ion,
                             void *returnAddr);
 
     bool attachDOMProxyShadowed(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                 HandleObject obj, void *returnAddr);
 
@@ -1024,17 +1024,17 @@ class NameIC : public RepatchIonCache
         return output_;
     }
     bool isTypeOf() const {
         return typeOf_;
     }
 
     bool attachReadSlot(JSContext *cx, HandleScript outerScript, IonScript *ion,
                         HandleObject scopeChain, HandleObject holderBase,
-                        HandleObject holder, HandleShape shape);
+                        HandleNativeObject holder, HandleShape shape);
 
     bool attachCallGetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
                           HandleObject scopeChain, HandleObject obj, HandleObject holder,
                           HandleShape shape, void *returnAddr);
 
     static bool
     update(JSContext *cx, size_t cacheIndex, HandleObject scopeChain, MutableHandleValue vp);
 };
@@ -1151,17 +1151,18 @@ class GetPropertyParIC : public Parallel
     }
 
     // CanAttachNativeGetProp Helpers
     typedef LockedJSContext & Context;
     bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return true; }
 
-    bool attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj, HandleObject holder,
+    bool attachReadSlot(LockedJSContext &cx, IonScript *ion,
+                        HandleObject obj, HandleNativeObject holder,
                         HandleShape shape);
     bool attachArrayLength(LockedJSContext &cx, IonScript *ion, HandleObject obj);
     bool attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, HandleObject obj);
 
     static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                        MutableHandleValue vp);
 };
 
@@ -1212,17 +1213,17 @@ class GetElementParIC : public ParallelI
 
     // CanAttachNativeGetProp Helpers
     typedef LockedJSContext & Context;
     bool canMonitorSingletonUndefinedSlot(HandleObject, HandleShape) const { return true; }
     bool allowGetters() const { return false; }
     bool allowArrayLength(Context, HandleObject) const { return false; }
 
     bool attachReadSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj, const Value &idval,
-                        HandlePropertyName name, HandleObject holder, HandleShape shape);
+                        HandlePropertyName name, HandleNativeObject holder, HandleShape shape);
     bool attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
                             const Value &idval);
     bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleObject tarr,
                                  const Value &idval);
 
     static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
                        MutableHandleValue vp);
 
@@ -1267,19 +1268,19 @@ class SetPropertyParIC : public Parallel
     }
     bool strict() const {
         return strict_;
     }
     bool needsTypeBarrier() const {
         return needsTypeBarrier_;
     }
 
-    bool attachSetSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj, HandleShape shape,
+    bool attachSetSlot(LockedJSContext &cx, IonScript *ion, HandleNativeObject obj, HandleShape shape,
                        bool checkTypeset);
-    bool attachAddSlot(LockedJSContext &cx, IonScript *ion, HandleObject obj,
+    bool attachAddSlot(LockedJSContext &cx, IonScript *ion, HandleNativeObject obj,
                        HandleShape oldShape, HandleTypeObject oldType, bool checkTypeset);
 
     static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
                        HandleValue value);
 };
 
 class SetElementParIC : public ParallelIonCache
 {
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -582,32 +582,32 @@ MacroAssembler::allocateObject(Register 
     pop(slots);
     callFreeStub(slots);
     jump(fail);
 
     bind(&success);
 }
 
 void
-MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObj,
+MacroAssembler::newGCThing(Register result, Register temp, NativeObject *templateObj,
                             gc::InitialHeap initialHeap, Label *fail)
 {
     // This method does not initialize the object: if external slots get
     // allocated into |temp|, there is no easy way for us to ensure the caller
     // frees them. Instead just assert this case does not happen.
     MOZ_ASSERT(!templateObj->numDynamicSlots());
 
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
     MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
     allocateObject(result, temp, allocKind, templateObj->numDynamicSlots(), initialHeap, fail);
 }
 
 void
-MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
+MacroAssembler::createGCObject(Register obj, Register temp, NativeObject *templateObj,
                                gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots)
 {
     uint32_t nDynamicSlots = templateObj->numDynamicSlots();
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
     MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
     // Arrays with copy on write elements do not need fixed space for an
     // elements header. The template object, which owns the original elements,
@@ -729,17 +729,17 @@ MacroAssembler::newGCTenuredThingPar(Reg
 
     // Update |first|.
     // tempReg1->head.first = tempReg2;
     storePtr(tempReg2, Address(tempReg1, gc::FreeList::offsetOfFirst()));
 }
 
 void
 MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
-                              JSObject *templateObject, Label *fail)
+                              NativeObject *templateObject, Label *fail)
 {
     gc::AllocKind allocKind = templateObject->asTenured().getAllocKind();
     MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
     MOZ_ASSERT(!templateObject->numDynamicSlots());
 
     newGCThingPar(result, cx, tempReg1, tempReg2, allocKind, fail);
 }
 
@@ -753,22 +753,22 @@ MacroAssembler::newGCStringPar(Register 
 void
 MacroAssembler::newGCFatInlineStringPar(Register result, Register cx, Register tempReg1,
                                         Register tempReg2, Label *fail)
 {
     newGCTenuredThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_FAT_INLINE_STRING, fail);
 }
 
 void
-MacroAssembler::copySlotsFromTemplate(Register obj, const JSObject *templateObj,
+MacroAssembler::copySlotsFromTemplate(Register obj, const NativeObject *templateObj,
                                       uint32_t start, uint32_t end)
 {
     uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
     for (unsigned i = start; i < nfixed; i++)
-        storeValue(templateObj->getFixedSlot(i), Address(obj, JSObject::getFixedSlotOffset(i)));
+        storeValue(templateObj->getFixedSlot(i), Address(obj, NativeObject::getFixedSlotOffset(i)));
 }
 
 void
 MacroAssembler::fillSlotsWithUndefined(Address base, Register temp, uint32_t start, uint32_t end)
 {
 #ifdef JS_NUNBOX32
     // We only have a single spare register, so do the initialization as two
     // strided writes of the tag and body.
@@ -786,29 +786,29 @@ MacroAssembler::fillSlotsWithUndefined(A
 #else
     moveValue(UndefinedValue(), temp);
     for (uint32_t i = start; i < end; ++i, base.offset += sizeof(HeapValue))
         storePtr(temp, base);
 #endif
 }
 
 static uint32_t
-FindStartOfUndefinedSlots(JSObject *templateObj, uint32_t nslots)
+FindStartOfUndefinedSlots(NativeObject *templateObj, uint32_t nslots)
 {
     MOZ_ASSERT(nslots == templateObj->lastProperty()->slotSpan(templateObj->getClass()));
     MOZ_ASSERT(nslots > 0);
     for (uint32_t first = nslots; first != 0; --first) {
         if (templateObj->getSlot(first - 1) != UndefinedValue())
             return first;
     }
     return 0;
 }
 
 void
-MacroAssembler::initGCSlots(Register obj, Register slots, JSObject *templateObj,
+MacroAssembler::initGCSlots(Register obj, Register slots, NativeObject *templateObj,
                             bool initFixedSlots)
 {
     // Slots of non-array objects are required to be initialized.
     // Use the values currently in the template object.
     uint32_t nslots = templateObj->lastProperty()->slotSpan(templateObj->getClass());
     if (nslots == 0)
         return;
 
@@ -825,78 +825,78 @@ MacroAssembler::initGCSlots(Register obj
     uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots);
     MOZ_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed.
 
     // Copy over any preserved reserved slots.
     copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
 
     // Fill the rest of the fixed slots with undefined.
     if (initFixedSlots) {
-        fillSlotsWithUndefined(Address(obj, JSObject::getFixedSlotOffset(startOfUndefined)), slots,
+        fillSlotsWithUndefined(Address(obj, NativeObject::getFixedSlotOffset(startOfUndefined)), slots,
                                startOfUndefined, nfixed);
     }
 
     if (ndynamic) {
         // We are short one register to do this elegantly. Borrow the obj
         // register briefly for our slots base address.
         push(obj);
-        loadPtr(Address(obj, JSObject::offsetOfSlots()), obj);
+        loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
         fillSlotsWithUndefined(Address(obj, 0), slots, 0, ndynamic);
         pop(obj);
     }
 }
 
 void
-MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
+MacroAssembler::initGCThing(Register obj, Register slots, NativeObject *templateObj,
                             bool initFixedSlots)
 {
     // Fast initialization of an empty object returned by allocateObject().
 
     MOZ_ASSERT_IF(!templateObj->denseElementsAreCopyOnWrite(), !templateObj->hasDynamicElements());
 
     storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape()));
     storePtr(ImmGCPtr(templateObj->type()), Address(obj, JSObject::offsetOfType()));
     if (templateObj->hasDynamicSlots())
-        storePtr(slots, Address(obj, JSObject::offsetOfSlots()));
+        storePtr(slots, Address(obj, NativeObject::offsetOfSlots()));
     else
-        storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots()));
+        storePtr(ImmPtr(nullptr), Address(obj, NativeObject::offsetOfSlots()));
 
     if (templateObj->denseElementsAreCopyOnWrite()) {
         storePtr(ImmPtr((const Value *) templateObj->getDenseElements()),
-                 Address(obj, JSObject::offsetOfElements()));
+                 Address(obj, NativeObject::offsetOfElements()));
     } else if (templateObj->is<ArrayObject>()) {
         Register temp = slots;
         MOZ_ASSERT(!templateObj->getDenseInitializedLength());
 
-        int elementsOffset = JSObject::offsetOfFixedElements();
+        int elementsOffset = NativeObject::offsetOfFixedElements();
 
         computeEffectiveAddress(Address(obj, elementsOffset), temp);
-        storePtr(temp, Address(obj, JSObject::offsetOfElements()));
+        storePtr(temp, Address(obj, NativeObject::offsetOfElements()));
 
         // Fill in the elements header.
         store32(Imm32(templateObj->getDenseCapacity()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfCapacity()));
         store32(Imm32(templateObj->getDenseInitializedLength()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfInitializedLength()));
         store32(Imm32(templateObj->as<ArrayObject>().length()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfLength()));
         store32(Imm32(templateObj->shouldConvertDoubleElements()
                       ? ObjectElements::CONVERT_DOUBLE_ELEMENTS
                       : 0),
                 Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
         MOZ_ASSERT(!templateObj->hasPrivate());
     } else {
-        storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
+        storePtr(ImmPtr(emptyObjectElements), Address(obj, NativeObject::offsetOfElements()));
 
         initGCSlots(obj, slots, templateObj, initFixedSlots);
 
         if (templateObj->hasPrivate()) {
             uint32_t nfixed = templateObj->numFixedSlots();
             storePtr(ImmPtr(templateObj->getPrivate()),
-                     Address(obj, JSObject::getPrivateDataOffset(nfixed)));
+                     Address(obj, NativeObject::getPrivateDataOffset(nfixed)));
         }
     }
 
 #ifdef JS_GC_TRACE
     RegisterSet regs = RegisterSet::Volatile();
     PushRegsInMask(regs);
     regs.takeUnchecked(obj);
     Register temp = regs.takeGeneral();
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -359,17 +359,17 @@ class MacroAssembler : public MacroAssem
 
     // Branches to |label| if |reg| is true. |reg| should be a C++ bool.
     void branchIfTrueBool(Register reg, Label *label) {
         // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
         branchTest32(Assembler::NonZero, reg, Imm32(0xFF), label);
     }
 
     void loadObjPrivate(Register obj, uint32_t nfixed, Register dest) {
-        loadPtr(Address(obj, JSObject::getPrivateDataOffset(nfixed)), dest);
+        loadPtr(Address(obj, NativeObject::getPrivateDataOffset(nfixed)), dest);
     }
 
     void loadObjProto(Register obj, Register dest) {
         loadPtr(Address(obj, JSObject::offsetOfType()), dest);
         loadPtr(Address(dest, types::TypeObject::offsetOfProto()), dest);
     }
 
     void loadStringLength(Register str, Register dest) {
@@ -806,45 +806,45 @@ class MacroAssembler : public MacroAssem
     void checkAllocatorState(Label *fail);
     bool shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap);
     void nurseryAllocate(Register result, Register slots, gc::AllocKind allocKind,
                          size_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
     void freeListAllocate(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
     void allocateObject(Register result, Register slots, gc::AllocKind allocKind,
                         uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
     void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
-    void copySlotsFromTemplate(Register obj, const JSObject *templateObj,
+    void copySlotsFromTemplate(Register obj, const NativeObject *templateObj,
                                uint32_t start, uint32_t end);
     void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
-    void initGCSlots(Register obj, Register temp, JSObject *templateObj, bool initFixedSlots);
+    void initGCSlots(Register obj, Register temp, NativeObject *templateObj, bool initFixedSlots);
 
   public:
     void callMallocStub(size_t nbytes, Register result, Label *fail);
     void callFreeStub(Register slots);
-    void createGCObject(Register result, Register temp, JSObject *templateObj,
+    void createGCObject(Register result, Register temp, NativeObject *templateObj,
                         gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);
 
-    void newGCThing(Register result, Register temp, JSObject *templateObj,
+    void newGCThing(Register result, Register temp, NativeObject *templateObj,
                      gc::InitialHeap initialHeap, Label *fail);
-    void initGCThing(Register obj, Register temp, JSObject *templateObj,
+    void initGCThing(Register obj, Register temp, NativeObject *templateObj,
                      bool initFixedSlots = true);
 
     void newGCString(Register result, Register temp, Label *fail);
     void newGCFatInlineString(Register result, Register temp, Label *fail);
 
     void newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                        gc::AllocKind allocKind, Label *fail);
 #ifdef JSGC_FJGENERATIONAL
     void newGCNurseryThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                               gc::AllocKind allocKind, Label *fail);
 #endif
     void newGCTenuredThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                               gc::AllocKind allocKind, Label *fail);
     void newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2,
-                       JSObject *templateObject, Label *fail);
+                       NativeObject *templateObject, Label *fail);
     void newGCStringPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                         Label *fail);
     void newGCFatInlineStringPar(Register result, Register cx, Register tempReg1, Register tempReg2,
                                  Label *fail);
 
 
     // Compares two strings for equality based on the JSOP.
     // This checks for identical pointers, atoms and length and fails for everything else.
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -12,16 +12,17 @@
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
 
 #include "jsscriptinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
 #include "vm/StringObject-inl.h"
 
 using mozilla::ArrayLength;
 
 namespace js {
 namespace jit {
 
 IonBuilder::InliningStatus
@@ -296,17 +297,17 @@ IonBuilder::inlineMathFunction(CallInfo 
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineArray(CallInfo &callInfo)
 {
     uint32_t initLength = 0;
     AllocatingBehaviour allocating = NewArray_Unallocating;
 
-    JSObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array);
+    NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, js_Array);
     if (!templateObject)
         return InliningStatus_NotInlined;
     MOZ_ASSERT(templateObject->is<ArrayObject>());
 
     // Multiple arguments imply array initialization, not just construction.
     if (callInfo.argc() >= 2) {
         initLength = callInfo.argc();
         allocating = NewArray_FullyAllocating;
@@ -330,17 +331,17 @@ IonBuilder::inlineArray(CallInfo &callIn
         if (callInfo.getArg(0)->type() != MIRType_Int32)
             return InliningStatus_NotInlined;
         MDefinition *arg = callInfo.getArg(0);
         if (!arg->isConstant())
             return InliningStatus_NotInlined;
 
         // Negative lengths generate a RangeError, unhandled by the inline path.
         initLength = arg->toConstant()->value().toInt32();
-        if (initLength >= JSObject::NELEMENTS_LIMIT)
+        if (initLength >= NativeObject::NELEMENTS_LIMIT)
             return InliningStatus_NotInlined;
 
         // Make sure initLength matches the template object's length. This is
         // not guaranteed to be the case, for instance if we're inlining the
         // MConstant may come from an outer script.
         if (initLength != templateObject->as<ArrayObject>().length())
             return InliningStatus_NotInlined;
 
@@ -667,25 +668,26 @@ IonBuilder::inlineArrayConcat(CallInfo &
             return InliningStatus_NotInlined;
 
         types::HeapTypeSetKey elemTypes = argType->property(JSID_VOID);
         if (!elemTypes.knownSubset(constraints(), thisElemTypes))
             return InliningStatus_NotInlined;
     }
 
     // Inline the call.
-    JSObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
+    NativeObject *templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->type() != baseThisType)
         return InliningStatus_NotInlined;
     MOZ_ASSERT(templateObj->is<ArrayObject>());
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MArrayConcat *ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0),
-                                          templateObj, templateObj->type()->initialHeap(constraints()));
+                                          &templateObj->as<ArrayObject>(),
+                                          templateObj->type()->initialHeap(constraints()));
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
@@ -1743,26 +1745,26 @@ IonBuilder::inlineNewDenseArrayForParall
     if (returnTypes->getKnownMIRType() != MIRType_Object)
         return InliningStatus_NotInlined;
     if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType_Int32)
         return InliningStatus_NotInlined;
     types::TypeObject *typeObject = returnTypes->getTypeObject(0);
 
-    JSObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray);
+    NativeObject *templateObject = inspector->getTemplateObjectForNative(pc, intrinsic_NewDenseArray);
     if (!templateObject || templateObject->type() != typeObject)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MNewDenseArrayPar *newObject = MNewDenseArrayPar::New(alloc(),
                                                           graph().forkJoinContext(),
                                                           callInfo.getArg(0),
-                                                          templateObject);
+                                                          &templateObject->as<ArrayObject>());
     current->add(newObject);
     current->push(newObject);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineHasClass(CallInfo &callInfo, const Class *clasp1, const Class *clasp2, const Class *clasp3)
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3175,34 +3175,34 @@ MBeta::printOpcode(FILE *fp) const
     } else {
         fprintf(fp, " ???");
     }
 }
 
 bool
 MNewObject::shouldUseVM() const
 {
-    JSObject *obj = templateObject();
+    NativeObject *obj = templateObject();
     return obj->hasSingletonType() || obj->hasDynamicSlots();
 }
 
 bool
 MCreateThisWithTemplate::canRecoverOnBailout() const
 {
     MOZ_ASSERT(!templateObject()->denseElementsAreCopyOnWrite());
     MOZ_ASSERT(!templateObject()->is<ArrayObject>());
     return true;
 }
 
 MObjectState::MObjectState(MDefinition *obj)
 {
     // This instruction is only used as a summary for bailout paths.
     setResultType(MIRType_Object);
     setRecoveredOnBailout();
-    JSObject *templateObject = nullptr;
+    NativeObject *templateObject = nullptr;
     if (obj->isNewObject())
         templateObject = obj->toNewObject()->templateObject();
     else
         templateObject = obj->toCreateThisWithTemplate()->templateObject();
     numSlots_ = templateObject->slotSpan();
     numFixedSlots_ = templateObject->numFixedSlots();
 }
 
@@ -3236,17 +3236,17 @@ MObjectState::Copy(TempAllocator &alloc,
     for (size_t i = 0; i < res->numSlots(); i++)
         res->initSlot(i, state->getSlot(i));
     return res;
 }
 
 bool
 MNewArray::shouldUseVM() const
 {
-    MOZ_ASSERT(count() < JSObject::NELEMENTS_LIMIT);
+    MOZ_ASSERT(count() < NativeObject::NELEMENTS_LIMIT);
 
     size_t arraySlots =
         gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
 
     // Allocate space using the VMCall when mir hints it needs to get allocated
     // immediately, but only when data doesn't fit the available array slots.
     bool allocating = allocatingBehaviour() != NewArray_Unallocating && count() > arraySlots;
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -17,16 +17,17 @@
 
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/IonAllocPolicy.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
 #include "jit/TypePolicy.h"
+#include "vm/ArrayObject.h"
 #include "vm/ScopeObject.h"
 #include "vm/TypedArrayCommon.h"
 
 namespace js {
 
 class StringObject;
 
 namespace jit {
@@ -2264,16 +2265,17 @@ class AlwaysTenured
 
   private:
     AlwaysTenured() MOZ_DELETE;
     AlwaysTenured(const AlwaysTenured<T> &) MOZ_DELETE;
     AlwaysTenured<T> &operator=(const AlwaysTenured<T> &) MOZ_DELETE;
 };
 
 typedef AlwaysTenured<JSObject*> AlwaysTenuredObject;
+typedef AlwaysTenured<NativeObject*> AlwaysTenuredNativeObject;
 typedef AlwaysTenured<JSFunction*> AlwaysTenuredFunction;
 typedef AlwaysTenured<JSScript*> AlwaysTenuredScript;
 typedef AlwaysTenured<PropertyName*> AlwaysTenuredPropertyName;
 typedef AlwaysTenured<Shape*> AlwaysTenuredShape;
 
 class MNewArray : public MUnaryInstruction
 {
   private:
@@ -2287,17 +2289,17 @@ class MNewArray : public MUnaryInstructi
 
     MNewArray(types::CompilerConstraintList *constraints, uint32_t count, MConstant *templateConst,
               gc::InitialHeap initialHeap, AllocatingBehaviour allocating)
       : MUnaryInstruction(templateConst),
         count_(count),
         initialHeap_(initialHeap),
         allocating_(allocating)
     {
-        JSObject *obj = templateObject();
+        ArrayObject *obj = templateObject();
         setResultType(MIRType_Object);
         if (!obj->hasSingletonType())
             setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
     }
 
   public:
     INSTRUCTION_HEADER(NewArray)
 
@@ -2307,18 +2309,18 @@ class MNewArray : public MUnaryInstructi
     {
         return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, allocating);
     }
 
     uint32_t count() const {
         return count_;
     }
 
-    JSObject *templateObject() const {
-        return &getOperand(0)->toConstant()->value().toObject();
+    ArrayObject *templateObject() const {
+        return &getOperand(0)->toConstant()->value().toObject().as<ArrayObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     AllocatingBehaviour allocatingBehaviour() const {
         return allocating_;
@@ -2343,41 +2345,41 @@ class MNewArray : public MUnaryInstructi
         // The template object can safely be used in the recover instruction
         // because it can never be mutated by any other function execution.
         return true;
     }
 };
 
 class MNewArrayCopyOnWrite : public MNullaryInstruction
 {
-    AlwaysTenuredObject templateObject_;
+    AlwaysTenured<ArrayObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
-    MNewArrayCopyOnWrite(types::CompilerConstraintList *constraints, JSObject *templateObject,
+    MNewArrayCopyOnWrite(types::CompilerConstraintList *constraints, ArrayObject *templateObject,
               gc::InitialHeap initialHeap)
       : templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
         MOZ_ASSERT(!templateObject->hasSingletonType());
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewArrayCopyOnWrite)
 
     static MNewArrayCopyOnWrite *New(TempAllocator &alloc,
                                      types::CompilerConstraintList *constraints,
-                                     JSObject *templateObject,
+                                     ArrayObject *templateObject,
                                      gc::InitialHeap initialHeap)
     {
         return new(alloc) MNewArrayCopyOnWrite(constraints, templateObject, initialHeap);
     }
 
-    JSObject *templateObject() const {
+    ArrayObject *templateObject() const {
         return templateObject_;
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const {
@@ -2424,18 +2426,18 @@ class MNewObject : public MUnaryInstruct
     // Returns true if the code generator should call through to the
     // VM rather than the fast path.
     bool shouldUseVM() const;
 
     bool templateObjectIsClassPrototype() const {
         return templateObjectIsClassPrototype_;
     }
 
-    JSObject *templateObject() const {
-        return &getOperand(0)->toConstant()->value().toObject();
+    NativeObject *templateObject() const {
+        return &getOperand(0)->toConstant()->value().toObject().as<NativeObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
@@ -2443,37 +2445,37 @@ class MNewObject : public MUnaryInstruct
         // because it can never be mutated by any other function execution.
         return true;
     }
 };
 
 // Could be allocating either a new array or a new object.
 class MNewPar : public MUnaryInstruction
 {
-    AlwaysTenuredObject templateObject_;
-
-    MNewPar(MDefinition *cx, JSObject *templateObject)
+    AlwaysTenuredNativeObject templateObject_;
+
+    MNewPar(MDefinition *cx, NativeObject *templateObject)
       : MUnaryInstruction(cx),
         templateObject_(templateObject)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewPar);
 
-    static MNewPar *New(TempAllocator &alloc, MDefinition *cx, JSObject *templateObject) {
+    static MNewPar *New(TempAllocator &alloc, MDefinition *cx, NativeObject *templateObject) {
         return new(alloc) MNewPar(cx, templateObject);
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
-    JSObject *templateObject() const {
+    NativeObject *templateObject() const {
         return templateObject_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
@@ -3712,18 +3714,18 @@ class MCreateThisWithTemplate
     INSTRUCTION_HEADER(CreateThisWithTemplate);
     static MCreateThisWithTemplate *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
                                         MConstant *templateConst, gc::InitialHeap initialHeap)
     {
         return new(alloc) MCreateThisWithTemplate(constraints, templateConst, initialHeap);
     }
 
     // Template for |this|, provided by TI.
-    JSObject *templateObject() const {
-        return &getOperand(0)->toConstant()->value().toObject();
+    NativeObject *templateObject() const {
+        return &getOperand(0)->toConstant()->value().toObject().as<NativeObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     // Although creation of |this| modifies global state, it is safely repeatable.
     AliasSet getAliasSet() const {
@@ -7664,40 +7666,40 @@ class MArrayPush
     ALLOW_CLONE(MArrayPush)
 };
 
 // Array.prototype.concat on two dense arrays.
 class MArrayConcat
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
-    AlwaysTenuredObject templateObj_;
+    AlwaysTenured<ArrayObject*> templateObj_;
     gc::InitialHeap initialHeap_;
 
     MArrayConcat(types::CompilerConstraintList *constraints, MDefinition *lhs, MDefinition *rhs,
-                 JSObject *templateObj, gc::InitialHeap initialHeap)
+                 ArrayObject *templateObj, gc::InitialHeap initialHeap)
       : MBinaryInstruction(lhs, rhs),
         templateObj_(templateObj),
         initialHeap_(initialHeap)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
     }
 
   public:
     INSTRUCTION_HEADER(ArrayConcat)
 
     static MArrayConcat *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
                              MDefinition *lhs, MDefinition *rhs,
-                             JSObject *templateObj, gc::InitialHeap initialHeap)
+                             ArrayObject *templateObj, gc::InitialHeap initialHeap)
     {
         return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, initialHeap);
     }
 
-    JSObject *templateObj() const {
+    ArrayObject *templateObj() const {
         return templateObj_;
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     AliasSet getAliasSet() const {
@@ -10303,53 +10305,53 @@ class MSetFrameArgument
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::FrameArgument);
     }
 };
 
 class MRestCommon
 {
     unsigned numFormals_;
-    AlwaysTenuredObject templateObject_;
+    AlwaysTenured<ArrayObject*> templateObject_;
 
   protected:
-    MRestCommon(unsigned numFormals, JSObject *templateObject)
+    MRestCommon(unsigned numFormals, ArrayObject *templateObject)
       : numFormals_(numFormals),
         templateObject_(templateObject)
    { }
 
   public:
     unsigned numFormals() const {
         return numFormals_;
     }
-    JSObject *templateObject() const {
+    ArrayObject *templateObject() const {
         return templateObject_;
     }
 };
 
 class MRest
   : public MUnaryInstruction,
     public MRestCommon,
     public IntPolicy<0>::Data
 {
     MRest(types::CompilerConstraintList *constraints, MDefinition *numActuals, unsigned numFormals,
-          JSObject *templateObject)
+          ArrayObject *templateObject)
       : MUnaryInstruction(numActuals),
         MRestCommon(numFormals, templateObject)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(Rest);
 
     static MRest *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
                       MDefinition *numActuals, unsigned numFormals,
-                      JSObject *templateObject)
+                      ArrayObject *templateObject)
     {
         return new(alloc) MRest(constraints, numActuals, numFormals, templateObject);
     }
 
     MDefinition *numActuals() const {
         return getOperand(0);
     }
 
@@ -10362,17 +10364,17 @@ class MRest
 };
 
 class MRestPar
   : public MBinaryInstruction,
     public MRestCommon,
     public IntPolicy<1>::Data
 {
     MRestPar(MDefinition *cx, MDefinition *numActuals, unsigned numFormals,
-             JSObject *templateObject, types::TemporaryTypeSet *resultTypes)
+             ArrayObject *templateObject, types::TemporaryTypeSet *resultTypes)
       : MBinaryInstruction(cx, numActuals),
         MRestCommon(numFormals, templateObject)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(resultTypes);
     }
 
   public:
@@ -10602,98 +10604,98 @@ class MPostWriteBarrier : public MBinary
     }
 #endif
 
     ALLOW_CLONE(MPostWriteBarrier)
 };
 
 class MNewDeclEnvObject : public MNullaryInstruction
 {
-    AlwaysTenuredObject templateObj_;
-
-    explicit MNewDeclEnvObject(JSObject *templateObj)
+    AlwaysTenuredNativeObject templateObj_;
+
+    explicit MNewDeclEnvObject(NativeObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewDeclEnvObject);
 
-    static MNewDeclEnvObject *New(TempAllocator &alloc, JSObject *templateObj) {
+    static MNewDeclEnvObject *New(TempAllocator &alloc, NativeObject *templateObj) {
         return new(alloc) MNewDeclEnvObject(templateObj);
     }
 
-    JSObject *templateObj() {
+    NativeObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MNewCallObjectBase : public MNullaryInstruction
 {
-    AlwaysTenuredObject templateObj_;
+    AlwaysTenuredNativeObject templateObj_;
 
   protected:
-    explicit MNewCallObjectBase(JSObject *templateObj)
+    explicit MNewCallObjectBase(NativeObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
-    JSObject *templateObject() {
+    NativeObject *templateObject() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MNewCallObject : public MNewCallObjectBase
 {
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
-    explicit MNewCallObject(JSObject *templateObj)
+    explicit MNewCallObject(NativeObject *templateObj)
       : MNewCallObjectBase(templateObj)
     {}
 
     static MNewCallObject *
-    New(TempAllocator &alloc, JSObject *templateObj)
+    New(TempAllocator &alloc, NativeObject *templateObj)
     {
         return new(alloc) MNewCallObject(templateObj);
     }
 };
 
 class MNewRunOnceCallObject : public MNewCallObjectBase
 {
   public:
     INSTRUCTION_HEADER(NewRunOnceCallObject)
 
-    explicit MNewRunOnceCallObject(JSObject *templateObj)
+    explicit MNewRunOnceCallObject(NativeObject *templateObj)
       : MNewCallObjectBase(templateObj)
     {}
 
     static MNewRunOnceCallObject *
-    New(TempAllocator &alloc, JSObject *templateObj)
+    New(TempAllocator &alloc, NativeObject *templateObj)
     {
         return new(alloc) MNewRunOnceCallObject(templateObj);
     }
 };
 
 class MNewCallObjectPar : public MUnaryInstruction
 {
-    AlwaysTenuredObject templateObj_;
-
-    MNewCallObjectPar(MDefinition *cx, JSObject *templateObj)
+    AlwaysTenuredNativeObject templateObj_;
+
+    MNewCallObjectPar(MDefinition *cx, NativeObject *templateObj)
         : MUnaryInstruction(cx),
           templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObjectPar);
@@ -10701,17 +10703,17 @@ class MNewCallObjectPar : public MUnaryI
     static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObjectBase *callObj) {
         return new(alloc) MNewCallObjectPar(cx, callObj->templateObject());
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
-    JSObject *templateObj() const {
+    NativeObject *templateObj() const {
         return templateObj_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
@@ -10800,44 +10802,44 @@ class MEnclosingScope : public MLoadFixe
     }
 };
 
 // Creates a dense array of the given length.
 //
 // Note: the template object should be an *empty* dense array!
 class MNewDenseArrayPar : public MBinaryInstruction
 {
-    AlwaysTenuredObject templateObject_;
-
-    MNewDenseArrayPar(MDefinition *cx, MDefinition *length, JSObject *templateObject)
+    AlwaysTenured<ArrayObject*> templateObject_;
+
+    MNewDenseArrayPar(MDefinition *cx, MDefinition *length, ArrayObject *templateObject)
       : MBinaryInstruction(cx, length),
         templateObject_(templateObject)
     {
         MOZ_ASSERT(length->type() == MIRType_Int32);
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewDenseArrayPar);
 
     static MNewDenseArrayPar *New(TempAllocator &alloc, MDefinition *cx, MDefinition *length,
-                                  JSObject *templateObject)
+                                  ArrayObject *templateObject)
     {
         return new(alloc) MNewDenseArrayPar(cx, length, templateObject);
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
     MDefinition *length() const {
         return getOperand(1);
     }
 
-    JSObject *templateObject() const {
+    ArrayObject *templateObject() const {
         return templateObject_;
     }
 
     bool possiblyCalls() const {
         return true;
     }
 
     AliasSet getAliasSet() const {
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -10,16 +10,18 @@
 #include "jit/arm/Simulator-arm.h"
 #include "jit/mips/Simulator-mips.h"
 #include "jit/RematerializedFrame.h"
 #include "vm/ArrayObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
+
 using namespace js;
 using namespace jit;
 
 using mozilla::IsInRange;
 
 using JS::AutoCheckCannotGC;
 
 using parallel::Spew;
@@ -160,64 +162,73 @@ jit::InterruptCheckPar(ForkJoinContext *
     bool result = cx->check();
     if (!result) {
         cx->bailoutRecord->joinCause(ParallelBailoutInterrupt);
         return false;
     }
     return true;
 }
 
-JSObject *
-jit::ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length)
+ArrayObject *
+jit::ExtendArrayPar(ForkJoinContext *cx, ArrayObject *array, uint32_t length)
 {
-    JSObject::EnsureDenseResult res =
+    NativeObject::EnsureDenseResult res =
         array->ensureDenseElementsPreservePackedFlag(cx, 0, length);
-    if (res != JSObject::ED_OK)
+    if (res != NativeObject::ED_OK)
         return nullptr;
     return array;
 }
 
 bool
 jit::SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc)
 {
     MOZ_ASSERT(cx->isThreadLocal(obj));
 
     if (*pc == JSOP_SETALIASEDVAR) {
         // See comment in jit::SetProperty.
-        Shape *shape = obj->nativeLookupPure(name);
+        Shape *shape = obj->as<NativeObject>().lookupPure(name);
         MOZ_ASSERT(shape && shape->hasSlot());
-        return obj->nativeSetSlotIfHasType(shape, value);
+        return obj->as<NativeObject>().setSlotIfHasType(shape, value);
     }
 
     // Fail early on hooks.
     if (obj->getOps()->setProperty)
         return TP_RETRY_SEQUENTIALLY;
 
     RootedValue v(cx, value);
     RootedId id(cx, NameToId(name));
-    return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, baseops::Qualified, &v,
+    return baseops::SetPropertyHelper<ParallelExecution>(cx,
+                                                         obj.as<NativeObject>(),
+                                                         obj.as<NativeObject>(),
+                                                         id, baseops::Qualified, &v,
                                                          strict);
 }
 
 bool
 jit::SetElementPar(ForkJoinContext *cx, HandleObject obj, HandleValue index, HandleValue value,
                    bool strict)
 {
     RootedId id(cx);
     if (!ValueToIdPure(index, id.address()))
         return false;
 
+    if (!obj->isNative())
+        return false;
+
     // SetObjectElementOperation, the sequential version, has several checks
     // for certain deoptimizing behaviors, such as marking having written to
     // holes and non-indexed element accesses. We don't do that here, as we
     // can't modify any TI state anyways. If we need to add a new type, we
     // would bail out.
     RootedValue v(cx, value);
-    return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, baseops::Qualified, &v,
+    return baseops::SetPropertyHelper<ParallelExecution>(cx,
+                                                         obj.as<NativeObject>(),
+                                                         obj.as<NativeObject>(),
+                                                         id, baseops::Qualified, &v,
                                                          strict);
 }
 
 bool
 jit::SetDenseElementPar(ForkJoinContext *cx, HandleObject obj, int32_t index, HandleValue value,
                         bool strict)
 {
     RootedValue indexVal(cx, Int32Value(index));
@@ -584,29 +595,28 @@ jit::CallToUncompiledScriptPar(ForkJoinC
     }
 #endif
 
     return false;
 }
 
 JSObject *
 jit::InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
-                          HandleObject templateObj, HandleObject res)
+                          HandleObject templateObj, HandleArrayObject res)
 {
     // In parallel execution, we should always have succeeded in allocation
     // before this point. We can do the allocation here like in the sequential
     // path, but duplicating the initGCThing logic is too tedious.
     MOZ_ASSERT(res);
-    MOZ_ASSERT(res->is<ArrayObject>());
     MOZ_ASSERT(!res->getDenseInitializedLength());
     MOZ_ASSERT(res->type() == templateObj->type());
 
     if (length > 0) {
-        JSObject::EnsureDenseResult edr =
+        NativeObject::EnsureDenseResult edr =
             res->ensureDenseElementsPreservePackedFlag(cx, 0, length);
-        if (edr != JSObject::ED_OK)
+        if (edr != NativeObject::ED_OK)
             return nullptr;
         res->initDenseElementsUnbarriered(0, rest, length);
-        res->as<ArrayObject>().setLengthInt32(length);
+        res->setLengthInt32(length);
     }
 
     return res;
 }
--- a/js/src/jit/ParallelFunctions.h
+++ b/js/src/jit/ParallelFunctions.h
@@ -21,17 +21,17 @@ JSObject *NewGCThingPar(ForkJoinContext 
 bool ParallelWriteGuard(ForkJoinContext *cx, JSObject *object);
 bool IsInTargetRegion(ForkJoinContext *cx, TypedObject *object);
 bool CheckOverRecursedPar(ForkJoinContext *cx);
 bool InterruptCheckPar(ForkJoinContext *cx);
 
 // Extends the given array with `length` new holes.  Returns nullptr on
 // failure or else `array`, which is convenient during code
 // generation.
-JSObject *ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length);
+ArrayObject *ExtendArrayPar(ForkJoinContext *cx, ArrayObject *array, uint32_t length);
 
 // Set properties and elements on thread local objects.
 bool SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
                     HandleValue value, bool strict, jsbytecode *pc);
 bool SetElementPar(ForkJoinContext *cx, HandleObject obj, HandleValue index,
                    HandleValue value, bool strict);
 bool SetDenseElementPar(ForkJoinContext *cx, HandleObject obj, int32_t index,
                         HandleValue value, bool strict);
@@ -64,17 +64,17 @@ bool BitOrPar(ForkJoinContext *cx, Handl
 bool BitAndPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
 bool BitLshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
 bool BitRshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out);
 
 bool UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, MutableHandleValue out);
 
 // Make a new rest parameter in parallel.
 JSObject *InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
-                               HandleObject templateObj, HandleObject res);
+                               HandleObject templateObj, HandleArrayObject res);
 
 // Abort and debug tracing functions.
 void BailoutPar(BailoutStack *sp, uint8_t **entryFramePointer);
 bool CallToUncompiledScriptPar(ForkJoinContext *cx, JSObject *obj);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -67,17 +67,17 @@ using parallel::SpewCompile;
 class ParallelSafetyVisitor : public MDefinitionVisitor
 {
     MIRGraph &graph_;
     bool unsafe_;
     MDefinition *cx_;
 
     bool insertWriteGuard(MInstruction *writeInstruction, MDefinition *valueBeingWritten);
 
-    bool replaceWithNewPar(MInstruction *newInstruction, JSObject *templateObject);
+    bool replaceWithNewPar(MInstruction *newInstruction, NativeObject *templateObject);
     bool replace(MInstruction *oldInstruction, MInstruction *replacementInstruction);
 
     bool visitSpecializedInstruction(MInstruction *ins, MIRType spec, uint32_t flags);
 
     // Intended for use in a visitXyz() instruction like "return
     // markUnsafe()".  Sets the unsafe flag and returns true (since
     // this does not indicate an unrecoverable compilation failure).
     bool markUnsafe() {
@@ -579,17 +579,17 @@ ParallelSafetyVisitor::visitToString(MTo
     MIRType inputType = ins->input()->type();
     if (inputType != MIRType_Int32 && inputType != MIRType_Double)
         return markUnsafe();
     return true;
 }
 
 bool
 ParallelSafetyVisitor::replaceWithNewPar(MInstruction *newInstruction,
-                                         JSObject *templateObject)
+                                         NativeObject *templateObject)
 {
     return replace(newInstruction, MNewPar::New(alloc(), ForkJoinContext(), templateObject));
 }
 
 bool
 ParallelSafetyVisitor::replace(MInstruction *oldInstruction,
                                MInstruction *replacementInstruction)
 {
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1502,17 +1502,17 @@ MArrayLength::computeRange(TempAllocator
     // nodes when the value is known to be int32 (see the
     // OBJECT_FLAG_LENGTH_OVERFLOW flag).
     setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
 }
 
 void
 MInitializedLength::computeRange(TempAllocator &alloc)
 {
-    setRange(Range::NewUInt32Range(alloc, 0, JSObject::NELEMENTS_LIMIT));
+    setRange(Range::NewUInt32Range(alloc, 0, NativeObject::NELEMENTS_LIMIT));
 }
 
 void
 MTypedArrayLength::computeRange(TempAllocator &alloc)
 {
     setRange(Range::NewUInt32Range(alloc, 0, INT32_MAX));
 }
 
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -16,19 +16,20 @@
 
 #include "gc/Heap.h"
 
 #include "jit/JitFrameIterator.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "jit/VMFunctions.h"
+#include "vm/Interpreter.h"
 
-#include "vm/Interpreter.h"
 #include "vm/Interpreter-inl.h"
+#include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 bool
 MNode::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_CRASH("This instruction is not serializable");
@@ -998,17 +999,17 @@ MNewObject::writeRecoverData(CompactBuff
 RNewObject::RNewObject(CompactBufferReader &reader)
 {
     templateObjectIsClassPrototype_ = reader.readByte();
 }
 
 bool
 RNewObject::recover(JSContext *cx, SnapshotIterator &iter) const
 {
-    RootedObject templateObject(cx, &iter.read().toObject());
+    RootedNativeObject templateObject(cx, &iter.read().toObject().as<NativeObject>());
     RootedValue result(cx);
     JSObject *resultObject = nullptr;
 
     // Use AutoEnterAnalysis to avoid invoking the object metadata callback
     // while bailing out, which could try to walk the stack.
     types::AutoEnterAnalysis enter(cx);
 
     // See CodeGenerator::visitNewObjectVMCall
@@ -1108,26 +1109,26 @@ MCreateThisWithTemplate::writeRecoverDat
 RCreateThisWithTemplate::RCreateThisWithTemplate(CompactBufferReader &reader)
 {
     tenuredHeap_ = reader.readByte();
 }
 
 bool
 RCreateThisWithTemplate::recover(JSContext *cx, SnapshotIterator &iter) const
 {
-    RootedObject templateObject(cx, &iter.read().toObject());
+    RootedNativeObject templateObject(cx, &iter.read().toObject().as<NativeObject>());
 
     // Use AutoEnterAnalysis to avoid invoking the object metadata callback
     // while bailing out, which could try to walk the stack.
     types::AutoEnterAnalysis enter(cx);
 
     // See CodeGenerator::visitCreateThisWithTemplate
     gc::AllocKind allocKind = templateObject->asTenured().getAllocKind();
     gc::InitialHeap initialHeap = tenuredHeap_ ? gc::TenuredHeap : gc::DefaultHeap;
-    JSObject *resultObject = JSObject::copy(cx, allocKind, initialHeap, templateObject);
+    JSObject *resultObject = NativeObject::copy(cx, allocKind, initialHeap, templateObject);
     if (!resultObject)
         return false;
 
     RootedValue result(cx);
     result.setObject(*resultObject);
     iter.storeInstructionResult(result);
     return true;
 }
@@ -1144,23 +1145,23 @@ MObjectState::writeRecoverData(CompactBu
 RObjectState::RObjectState(CompactBufferReader &reader)
 {
     numSlots_ = reader.readUnsigned();
 }
 
 bool
 RObjectState::recover(JSContext *cx, SnapshotIterator &iter) const
 {
-    RootedObject object(cx, &iter.read().toObject());
+    RootedNativeObject object(cx, &iter.read().toObject().as<NativeObject>());
     MOZ_ASSERT(object->slotSpan() == numSlots());
 
     RootedValue val(cx);
     for (size_t i = 0; i < numSlots(); i++) {
         val = iter.read();
-        object->nativeSetSlot(i, val);
+        object->setSlot(i, val);
     }
 
     val.setObject(*object);
     iter.storeInstructionResult(val);
     return true;
 }
 
 bool
@@ -1176,17 +1177,17 @@ RArrayState::RArrayState(CompactBufferRe
 {
     numElements_ = reader.readUnsigned();
 }
 
 bool
 RArrayState::recover(JSContext *cx, SnapshotIterator &iter) const
 {
     RootedValue result(cx);
-    JSObject *object = &iter.read().toObject();
+    ArrayObject *object = &iter.read().toObject().as<ArrayObject>();
     uint32_t initLength = iter.read().toInt32();
 
     object->setDenseInitializedLength(initLength);
     for (size_t index = 0; index < numElements(); index++) {
         Value val = iter.read();
 
         if (index >= initLength) {
             MOZ_ASSERT(val.isUndefined());
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -18,16 +18,17 @@
 #include "vm/Interpreter.h"
 #include "vm/TraceLogging.h"
 
 #include "jsinferinlines.h"
 
 #include "jit/BaselineFrame-inl.h"
 #include "jit/IonFrames-inl.h"
 #include "vm/Interpreter-inl.h"
+#include "vm/ObjectImpl-inl.h"
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 namespace js {
 namespace jit {
 
@@ -216,17 +217,17 @@ MutatePrototype(JSContext *cx, HandleObj
     bool succeeded;
     if (!JSObject::setProto(cx, obj, newProto, &succeeded))
         return false;
     MOZ_ASSERT(succeeded);
     return true;
 }
 
 bool
-InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value)
+InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value)
 {
     RootedId id(cx, NameToId(name));
     return DefineNativeProperty(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE);
 }
 
 template<bool Equal>
 bool
 LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
@@ -289,17 +290,17 @@ StringsEqual(JSContext *cx, HandleString
         *res = !*res;
     return true;
 }
 
 template bool StringsEqual<true>(JSContext *cx, HandleString lhs, HandleString rhs, bool *res);
 template bool StringsEqual<false>(JSContext *cx, HandleString lhs, HandleString rhs, bool *res);
 
 JSObject*
-NewInitObject(JSContext *cx, HandleObject templateObject)
+NewInitObject(JSContext *cx, HandleNativeObject templateObject)
 {
     NewObjectKind newKind = templateObject->hasSingletonType() ? SingletonObject : GenericObject;
     if (!templateObject->hasLazyType() && templateObject->type()->shouldPreTenure())
         newKind = TenuredObject;
     RootedObject obj(cx, CopyInitializerObject(cx, templateObject, newKind));
 
     if (!obj)
         return nullptr;
@@ -361,31 +362,29 @@ ArrayPopDense(JSContext *cx, HandleObjec
     // have to monitor the return value.
     rval.set(argv[0]);
     if (rval.isUndefined())
         types::TypeScript::Monitor(cx, rval);
     return true;
 }
 
 bool
-ArrayPushDense(JSContext *cx, HandleObject obj, HandleValue v, uint32_t *length)
+ArrayPushDense(JSContext *cx, HandleArrayObject obj, HandleValue v, uint32_t *length)
 {
-    MOZ_ASSERT(obj->is<ArrayObject>());
-
-    if (MOZ_LIKELY(obj->as<ArrayObject>().lengthIsWritable())) {
-        uint32_t idx = obj->as<ArrayObject>().length();
-        JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, idx, 1);
-        if (result == JSObject::ED_FAILED)
+    if (MOZ_LIKELY(obj->lengthIsWritable())) {
+        uint32_t idx = obj->length();
+        NativeObject::EnsureDenseResult result = obj->ensureDenseElements(cx, idx, 1);
+        if (result == NativeObject::ED_FAILED)
             return false;
 
-        if (result == JSObject::ED_OK) {
+        if (result == NativeObject::ED_OK) {
             obj->setDenseElement(idx, v);
             MOZ_ASSERT(idx < INT32_MAX);
             *length = idx + 1;
-            obj->as<ArrayObject>().setLengthInt32(*length);
+            obj->setLengthInt32(*length);
             return true;
         }
     }
 
     JS::AutoValueArray<3> argv(cx);
     argv[0].setUndefined();
     argv[1].setObject(*obj);
     argv[2].set(v);
@@ -506,25 +505,25 @@ SetProperty(JSContext *cx, HandleObject 
     RootedValue v(cx, value);
     RootedId id(cx, NameToId(name));
 
     JSOp op = JSOp(*pc);
 
     if (op == JSOP_SETALIASEDVAR) {
         // Aliased var assigns ignore readonly attributes on the property, as
         // required for initializing 'const' closure variables.
-        Shape *shape = obj->nativeLookup(cx, name);
+        Shape *shape = obj->as<NativeObject>().lookup(cx, name);
         MOZ_ASSERT(shape && shape->hasSlot());
-        obj->nativeSetSlotWithType(cx, shape, value);
+        obj->as<NativeObject>().setSlotWithType(cx, shape, value);
         return true;
     }
 
     if (MOZ_LIKELY(!obj->getOps()->setProperty)) {
         return baseops::SetPropertyHelper<SequentialExecution>(
-            cx, obj, obj, id,
+            cx, obj.as<NativeObject>(), obj.as<NativeObject>(), id,
             (op == JSOP_SETNAME || op == JSOP_SETGNAME)
             ? baseops::Unqualified
             : baseops::Qualified,
             &v,
             strict);
     }
 
     return JSObject::setGeneric(cx, obj, obj, id, &v, strict);
@@ -1069,49 +1068,47 @@ Recompile(JSContext *cx)
     MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing);
     if (status == Method_Error)
         return false;
 
     return true;
 }
 
 bool
-SetDenseElement(JSContext *cx, HandleObject obj, int32_t index, HandleValue value,
+SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
                 bool strict)
 {
     // This function is called from Ion code for StoreElementHole's OOL path.
     // In this case we know the object is native, has no indexed properties
     // and we can use setDenseElement instead of setDenseElementWithType.
-
-    MOZ_ASSERT(obj->isNative());
     MOZ_ASSERT(!obj->isIndexed());
 
-    JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
+    NativeObject::EnsureDenseResult result = NativeObject::ED_SPARSE;
     do {
         if (index < 0)
             break;
         bool isArray = obj->is<ArrayObject>();
         if (isArray && !obj->as<ArrayObject>().lengthIsWritable())
             break;
         uint32_t idx = uint32_t(index);
         result = obj->ensureDenseElements(cx, idx, 1);
-        if (result != JSObject::ED_OK)
+        if (result != NativeObject::ED_OK)
             break;
         if (isArray) {
             ArrayObject &arr = obj->as<ArrayObject>();
             if (idx >= arr.length())
                 arr.setLengthInt32(idx + 1);
         }
         obj->setDenseElement(idx, value);
         return true;
     } while (false);
 
-    if (result == JSObject::ED_FAILED)
+    if (result == NativeObject::ED_FAILED)
         return false;
-    MOZ_ASSERT(result == JSObject::ED_SPARSE);
+    MOZ_ASSERT(result == NativeObject::ED_SPARSE);
 
     RootedValue indexVal(cx, Int32Value(index));
     return SetObjectElement(cx, obj, indexVal, value, strict);
 }
 
 void
 AutoDetectInvalidation::setReturnOverride()
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -283,24 +283,27 @@ struct VMFunctionsModal
     }
 
     mozilla::Array<VMFunction, NumExecutionModes> funs_;
 };
 
 template <class> struct TypeToDataType { /* Unexpected return type for a VMFunction. */ };
 template <> struct TypeToDataType<bool> { static const DataType result = Type_Bool; };
 template <> struct TypeToDataType<JSObject *> { static const DataType result = Type_Object; };
+template <> struct TypeToDataType<NativeObject *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<DeclEnvObject *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<ArrayObject *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<JSString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<JSFlatString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleFunction> { static const DataType result = Type_Handle; };
+template <> struct TypeToDataType<Handle<NativeObject *> > { static const DataType result = Type_Handle; };
+template <> struct TypeToDataType<Handle<ArrayObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<StaticWithObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<StaticBlockObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
 
 // Convert argument types to properties of the argument known by the jit.
 template <class T> struct TypeToArgProperties {
@@ -317,16 +320,22 @@ template <> struct TypeToArgProperties<H
     static const uint32_t result = TypeToArgProperties<JSString *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandlePropertyName> {
     static const uint32_t result = TypeToArgProperties<PropertyName *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleFunction> {
     static const uint32_t result = TypeToArgProperties<JSFunction *>::result | VMFunction::ByRef;
 };
+template <> struct TypeToArgProperties<Handle<NativeObject *> > {
+    static const uint32_t result = TypeToArgProperties<NativeObject *>::result | VMFunction::ByRef;
+};
+template <> struct TypeToArgProperties<Handle<ArrayObject *> > {
+    static const uint32_t result = TypeToArgProperties<ArrayObject *>::result | VMFunction::ByRef;
+};
 template <> struct TypeToArgProperties<Handle<StaticWithObject *> > {
     static const uint32_t result = TypeToArgProperties<StaticWithObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<StaticBlockObject *> > {
     static const uint32_t result = TypeToArgProperties<StaticBlockObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleScript> {
     static const uint32_t result = TypeToArgProperties<JSScript *>::result | VMFunction::ByRef;
@@ -379,16 +388,22 @@ template <> struct TypeToRootType<Handle
     static const uint32_t result = VMFunction::RootCell;
 };
 template <> struct TypeToRootType<HandleTypeObject> {
     static const uint32_t result = VMFunction::RootCell;
 };
 template <> struct TypeToRootType<HandleScript> {
     static const uint32_t result = VMFunction::RootCell;
 };
+template <> struct TypeToRootType<Handle<NativeObject *> > {
+    static const uint32_t result = VMFunction::RootObject;
+};
+template <> struct TypeToRootType<Handle<ArrayObject *> > {
+    static const uint32_t result = VMFunction::RootObject;
+};
 template <> struct TypeToRootType<Handle<StaticBlockObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<StaticWithObject *> > {
     static const uint32_t result = VMFunction::RootCell;
 };
 template <class T> struct TypeToRootType<Handle<T> > {
     // Fail for Handle types that aren't specialized above.
@@ -611,17 +626,17 @@ JSObject *NewGCObject(JSContext *cx, gc:
 
 bool CheckOverRecursed(JSContext *cx);
 bool CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame,
                                 uint32_t extra, uint32_t earlyCheck);
 
 bool DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
 bool SetConst(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, HandleValue rval);
 bool MutatePrototype(JSContext *cx, HandleObject obj, HandleValue value);
-bool InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value);
+bool InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value);
 
 template<bool Equal>
 bool LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
 
 template<bool Equal>
 bool StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
 
 bool LessThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
@@ -629,21 +644,21 @@ bool LessThanOrEqual(JSContext *cx, Muta
 bool GreaterThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
 bool GreaterThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
 
 template<bool Equal>
 bool StringsEqual(JSContext *cx, HandleString left, HandleString right, bool *res);
 
 // Allocation functions for JSOP_NEWARRAY and JSOP_NEWOBJECT and parallel array inlining
 JSObject *NewInitParallelArray(JSContext *cx, HandleObject templateObj);
-JSObject *NewInitObject(JSContext *cx, HandleObject templateObject);
+JSObject *NewInitObject(JSContext *cx, HandleNativeObject templateObject);
 JSObject *NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject);
 
 bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
-bool ArrayPushDense(JSContext *cx, HandleObject obj, HandleValue v, uint32_t *length);
+bool ArrayPushDense(JSContext *cx, HandleArrayObject obj, HandleValue v, uint32_t *length);
 bool ArrayShiftDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
 JSObject *ArrayConcatDense(JSContext *cx, HandleObject obj1, HandleObject obj2, HandleObject res);
 JSString *ArrayJoin(JSContext *cx, HandleObject array, HandleString sep);
 
 bool CharCodeAt(JSContext *cx, HandleString str, int32_t index, uint32_t *code);
 JSFlatString *StringFromCharCode(JSContext *cx, int32_t code);
 
 bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
@@ -709,17 +724,17 @@ JSObject *CreateDerivedTypedObj(JSContex
 bool ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
 
 bool Recompile(JSContext *cx);
 JSString *RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp,
                         HandleString repl);
 JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString repl);
 
-bool SetDenseElement(JSContext *cx, HandleObject obj, int32_t index, HandleValue value,
+bool SetDenseElement(JSContext *cx, HandleNativeObject obj, int32_t index, HandleValue value,
                      bool strict);
 
 #ifdef DEBUG
 void AssertValidObjectPtr(JSContext *cx, JSObject *obj);
 void AssertValidStringPtr(JSContext *cx, JSString *str);
 void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym);
 void AssertValidValue(JSContext *cx, Value *v);
 #endif
--- a/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
+++ b/js/src/jsapi-tests/testDefinePropertyIgnoredAttributes.cpp
@@ -85,13 +85,13 @@ BEGIN_TEST(testDefinePropertyIgnoredAttr
     CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
     CHECK(CheckDescriptor(desc, false, false, true));
 
     // Just make it writable. Leave the old value and everythign else alone.
     defineValue.setUndefined();
     CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, AllowWritable));
     CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
     CHECK(CheckDescriptor(desc, false, true, true));
-    CHECK_SAME(ObjectValue(*obj), desc.value());
+    CHECK_SAME(JS::ObjectValue(*obj), desc.value());
 
     return true;
 }
 END_TEST(testDefinePropertyIgnoredAttributes)
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -62,17 +62,17 @@ document_resolve(JSContext *cx, JS::Hand
         JSFlatString *flatStr = JS_FlattenString(cx, str);
         if (!flatStr)
             return false;
         if (JS_FlatStringEqualsAscii(flatStr, "all")) {
             JS::Rooted<JSObject*> docAll(cx,
                                          JS_NewObject(cx, &DocumentAllClass, JS::NullPtr(), JS::NullPtr()));
             if (!docAll)
                 return false;
-            JS::Rooted<JS::Value> allValue(cx, ObjectValue(*docAll));
+            JS::Rooted<JS::Value> allValue(cx, JS::ObjectValue(*docAll));
             bool ok = JS_DefinePropertyById(cx, obj, id, allValue, 0);
             objp.set(ok ? obj.get() : nullptr);
             return ok;
         }
     }
     objp.set(nullptr);
     return true;
 }
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -33,18 +33,18 @@ BEGIN_TEST(testResolveRecursion)
 
     obj1 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
     CHECK(obj1);
     obj2 = JS_NewObject(cx, &my_resolve_class, JS::NullPtr(), JS::NullPtr());
     CHECK(obj2);
     JS_SetPrivate(obj1, this);
     JS_SetPrivate(obj2, this);
 
-    JS::RootedValue obj1Val(cx, ObjectValue(*obj1));
-    JS::RootedValue obj2Val(cx, ObjectValue(*obj2));
+    JS::RootedValue obj1Val(cx, JS::ObjectValue(*obj1));
+    JS::RootedValue obj2Val(cx, JS::ObjectValue(*obj2));
     CHECK(JS_DefineProperty(cx, global, "obj1", obj1Val, 0));
     CHECK(JS_DefineProperty(cx, global, "obj2", obj2Val, 0));
 
     resolveEntryCount = 0;
     resolveExitCount = 0;
 
     /* Start the essence of the test via invoking the first resolve hook. */
     JS::RootedValue v(cx);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2228,32 +2228,32 @@ JS_HasInstance(JSContext *cx, HandleObje
     assertSameCompartment(cx, obj, value);
     return HasInstance(cx, obj, value, bp);
 }
 
 JS_PUBLIC_API(void *)
 JS_GetPrivate(JSObject *obj)
 {
     /* This function can be called by a finalizer. */
-    return obj->getPrivate();
+    return obj->fakeNativeGetPrivate();
 }
 
 JS_PUBLIC_API(void)
 JS_SetPrivate(JSObject *obj, void *data)
 {
     /* This function can be called by a finalizer. */
-    obj->setPrivate(data);
+    obj->fakeNativeSetPrivate(data);
 }
 
 JS_PUBLIC_API(void *)
 JS_GetInstancePrivate(JSContext *cx, HandleObject obj, const JSClass *clasp, CallArgs *args)
 {
     if (!JS_InstanceOf(cx, obj, clasp, args))
         return nullptr;
-    return obj->getPrivate();
+    return obj->fakeNativeGetPrivate();
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPrototype(JSContext *cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> protop)
 {
     return JSObject::getProto(cx, obj, protop);
 }
 
@@ -2561,18 +2561,18 @@ JS_DeepFreezeObject(JSContext *cx, Handl
         return false;
     if (!extensible)
         return true;
 
     if (!JSObject::freeze(cx, obj))
         return false;
 
     /* Walk slots in obj and if any value is a non-null object, seal it. */
-    for (uint32_t i = 0, n = obj->slotSpan(); i < n; ++i) {
-        const Value &v = obj->getSlot(i);
+    for (uint32_t i = 0, n = obj->fakeNativeSlotSpan(); i < n; ++i) {
+        const Value &v = obj->fakeNativeGetSlot(i);
         if (v.isPrimitive())
             continue;
         RootedObject obj(cx, &v.toObject());
         if (!JS_DeepFreezeObject(cx, obj))
             return false;
     }
 
     return true;
@@ -2607,22 +2607,22 @@ LookupResult(JSContext *cx, HandleObject
             if (!Proxy::getPropertyDescriptor(cx, obj2, id, &desc))
                 return false;
             if (!desc.isShared()) {
                 vp.set(desc.value());
                 return true;
             }
         }
     } else if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
+        vp.set(obj2->as<NativeObject>().getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         return true;
     } else {
         /* Peek at the native property's slot value, without doing a Get. */
         if (shape->hasSlot()) {
-            vp.set(obj2->nativeGetSlot(shape->slot()));
+            vp.set(obj2->as<NativeObject>().getSlot(shape->slot()));
             return true;
         }
     }
 
     /* XXX bad API: no way to return "defined but value unknown" */
     vp.setBoolean(true);
     return true;
 }
@@ -2730,28 +2730,28 @@ JS_AlreadyHasOwnPropertyById(JSContext *
         return true;
     }
 
     // Check for an existing native property on the object. Be careful not to
     // call any lookup or resolve hooks.
     if (JSID_IS_INT(id)) {
         uint32_t index = JSID_TO_INT(id);
 
-        if (obj->containsDenseElement(index)) {
+        if (obj->as<NativeObject>().containsDenseElement(index)) {
             *foundp = true;
             return true;
         }
 
         if (IsAnyTypedArray(obj) && index < AnyTypedArrayLength(obj)) {
             *foundp = true;
             return true;
         }
     }
 
-    *foundp = obj->nativeContains(cx, id);
+    *foundp = obj->as<NativeObject>().contains(cx, id);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnElement(JSContext *cx, HandleObject obj, uint32_t index, bool *foundp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -3316,24 +3316,24 @@ GetPropertyDescriptorById(JSContext *cx,
     desc.clear();
     if (!shape)
         return true;
 
     desc.object().set(obj2);
     if (obj2->isNative()) {
         if (IsImplicitDenseOrTypedArrayElement(shape)) {
             desc.setEnumerable();
-            desc.value().set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
+            desc.value().set(obj2->as<NativeObject>().getDenseOrTypedArrayElement(JSID_TO_INT(id)));
         } else {
             desc.setAttributes(shape->attributes());
             desc.setGetter(shape->getter());
             desc.setSetter(shape->setter());
             MOZ_ASSERT(desc.value().isUndefined());
             if (shape->hasSlot())
-                desc.value().set(obj2->nativeGetSlot(shape->slot()));
+                desc.value().set(obj2->as<NativeObject>().getSlot(shape->slot()));
         }
     } else {
         if (obj2->is<ProxyObject>())
             return Proxy::getPropertyDescriptor(cx, obj2, id, desc);
         if (!JSObject::getGenericAttributes(cx, obj2, id, &desc.attributesRef()))
             return false;
         MOZ_ASSERT(desc.getter() == nullptr);
         MOZ_ASSERT(desc.setter() == nullptr);
@@ -3597,19 +3597,19 @@ JS_SetAllNonReservedSlotsToUndefined(JSC
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     if (!obj->isNative())
         return;
 
     const Class *clasp = obj->getClass();
     unsigned numReserved = JSCLASS_RESERVED_SLOTS(clasp);
-    unsigned numSlots = obj->slotSpan();
+    unsigned numSlots = obj->as<NativeObject>().slotSpan();
     for (unsigned i = numReserved; i < numSlots; i++)
-        obj->setSlot(i, UndefinedValue());
+        obj->as<NativeObject>().setSlot(i, UndefinedValue());
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
@@ -3628,43 +3628,43 @@ JS_Enumerate(JSContext *cx, HandleObject
  * + native case here uses a JSShape *, but that iterates in reverse!
  * + so we make non-native match, by reverse-iterating after JS_Enumerating
  */
 static const uint32_t JSSLOT_ITER_INDEX = 0;
 
 static void
 prop_iter_finalize(FreeOp *fop, JSObject *obj)
 {
-    void *pdata = obj->getPrivate();
+    void *pdata = obj->as<NativeObject>().getPrivate();
     if (!pdata)
         return;
 
-    if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) {
+    if (obj->as<NativeObject>().getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) {
         /* Non-native case: destroy the ida enumerated when obj was created. */
         JSIdArray *ida = (JSIdArray *) pdata;
         fop->free_(ida);
     }
 }
 
 static void
 prop_iter_trace(JSTracer *trc, JSObject *obj)
 {
-    void *pdata = obj->getPrivate();
+    void *pdata = obj->as<NativeObject>().getPrivate();
     if (!pdata)
         return;
 
-    if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) {
+    if (obj->as<NativeObject>().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 = static_cast<Shape *>(pdata);
         MarkShapeUnbarriered(trc, &tmp, "prop iter shape");
-        obj->setPrivateUnbarriered(tmp);
+        obj->as<NativeObject>().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");
     }
 }
 
 static const Class prop_iter_class = {
@@ -3686,17 +3686,17 @@ static const Class prop_iter_class = {
 
 JS_PUBLIC_API(JSObject *)
 JS_NewPropertyIterator(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
-    RootedObject iterobj(cx, NewObjectWithClassProto(cx, &prop_iter_class, nullptr, obj));
+    NativeObject *iterobj = NewNativeObjectWithClassProto(cx, &prop_iter_class, nullptr, obj);
     if (!iterobj)
         return nullptr;
 
     int index;
     if (obj->isNative()) {
         /* Native case: start with the last property in obj. */
         iterobj->setPrivateGCThing(obj->lastProperty());
         index = -1;
@@ -3715,57 +3715,57 @@ JS_NewPropertyIterator(JSContext *cx, Ha
 }
 
 JS_PUBLIC_API(bool)
 JS_NextProperty(JSContext *cx, HandleObject iterobj, MutableHandleId idp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, iterobj);
-    int32_t i = iterobj->getSlot(JSSLOT_ITER_INDEX).toInt32();
+    int32_t i = iterobj->as<NativeObject>().getSlot(JSSLOT_ITER_INDEX).toInt32();
     if (i < 0) {
         /* Native case: private data is a property tree node pointer. */
         MOZ_ASSERT(iterobj->getParent()->isNative());
-        Shape *shape = static_cast<Shape *>(iterobj->getPrivate());
+        Shape *shape = static_cast<Shape *>(iterobj->as<NativeObject>().getPrivate());
 
         while (shape->previous() && !shape->enumerable())
             shape = shape->previous();
 
         if (!shape->previous()) {
             MOZ_ASSERT(shape->isEmptyShape());
             idp.set(JSID_VOID);
         } else {
-            iterobj->setPrivateGCThing(const_cast<Shape *>(shape->previous().get()));
+            iterobj->as<NativeObject>().setPrivateGCThing(const_cast<Shape *>(shape->previous().get()));
             idp.set(shape->propid());
         }
     } else {
         /* Non-native case: use the ida enumerated when iterobj was created. */
-        JSIdArray *ida = (JSIdArray *) iterobj->getPrivate();
+        JSIdArray *ida = (JSIdArray *) iterobj->as<NativeObject>().getPrivate();
         MOZ_ASSERT(i <= ida->length);
         STATIC_ASSUME(i <= ida->length);
         if (i == 0) {
             idp.set(JSID_VOID);
         } else {
             idp.set(ida->vector[--i]);
-            iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(i));
+            iterobj->as<NativeObject>().setSlot(JSSLOT_ITER_INDEX, Int32Value(i));
         }
     }
     return true;
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetReservedSlot(JSObject *obj, uint32_t index)
 {
-    return obj->getReservedSlot(index);
+    return obj->fakeNativeGetReservedSlot(index);
 }
 
 JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject *obj, uint32_t index, Value value)
 {
-    obj->setReservedSlot(index, value);
+    obj->fakeNativeSetReservedSlot(index, value);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, const JS::HandleValueArray& contents)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
@@ -6471,17 +6471,17 @@ JS_EncodeScript(JSContext *cx, HandleScr
         return nullptr;
     return encoder.forgetData(lengthp);
 }
 
 JS_PUBLIC_API(void *)
 JS_EncodeInterpretedFunction(JSContext *cx, HandleObject funobjArg, uint32_t *lengthp)
 {
     XDREncoder encoder(cx);
-    RootedObject funobj(cx, funobjArg);
+    RootedFunction funobj(cx, &funobjArg->as<JSFunction>());
     if (!encoder.codeFunction(&funobj))
         return nullptr;
     return encoder.forgetData(lengthp);
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_DecodeScript(JSContext *cx, const void *data, uint32_t length)
 {
@@ -6491,17 +6491,17 @@ JS_DecodeScript(JSContext *cx, const voi
         return nullptr;
     return script;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length)
 {
     XDRDecoder decoder(cx, data, length);
-    RootedObject funobj(cx);
+    RootedFunction funobj(cx);
     if (!decoder.codeFunction(&funobj))
         return nullptr;
     return funobj;
 }
 
 JS_PUBLIC_API(bool)
 JS_PreventExtensions(JSContext *cx, JS::HandleObject obj)
 {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -34,16 +34,17 @@
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/Interpreter-inl.h"
+#include "vm/ObjectImpl-inl.h"
 #include "vm/Runtime-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
@@ -208,18 +209,18 @@ AssertGreaterThanZero(uint32_t index)
 }
 
 template <typename IndexType>
 static bool
 GetElement(JSContext *cx, HandleObject obj, HandleObject receiver,
            IndexType index, bool *hole, MutableHandleValue vp)
 {
     AssertGreaterThanZero(index);
-    if (obj->isNative() && index < obj->getDenseInitializedLength()) {
-        vp.set(obj->getDenseElement(uint32_t(index)));
+    if (obj->isNative() && index < obj->as<NativeObject>().getDenseInitializedLength()) {
+        vp.set(obj->as<NativeObject>().getDenseElement(uint32_t(index)));
         if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
             *hole = false;
             return true;
         }
     }
     if (obj->is<ArgumentsObject>()) {
         if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
             *hole = false;
@@ -246,21 +247,22 @@ GetElementsSlow(JSContext *cx, HandleObj
     }
 
     return true;
 }
 
 bool
 js::GetElements(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
 {
-    if (aobj->is<ArrayObject>() && length <= aobj->getDenseInitializedLength() &&
+    if (aobj->is<ArrayObject>() &&
+        length <= aobj->as<ArrayObject>().getDenseInitializedLength() &&
         !ObjectMayHaveExtraIndexedProperties(aobj))
     {
         /* No other indexed properties so hole = undefined */
-        const Value *srcbeg = aobj->getDenseElements();
+        const Value *srcbeg = aobj->as<ArrayObject>().getDenseElements();
         const Value *srcend = srcbeg + length;
         const Value *src = srcbeg;
         for (Value *dst = vp; src < srcend; ++dst, ++src)
             *dst = src->isMagic(JS_ELEMENTS_HOLE) ? UndefinedValue() : *src;
         return true;
     }
 
     if (aobj->is<ArgumentsObject>()) {
@@ -280,38 +282,38 @@ js::GetElements(JSContext *cx, HandleObj
 static bool
 SetArrayElement(JSContext *cx, HandleObject obj, double index, HandleValue v)
 {
     MOZ_ASSERT(index >= 0);
 
     if (obj->is<ArrayObject>() && !obj->isIndexed()) {
         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
         /* Predicted/prefetched code should favor the remains-dense case. */
-        JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
+        NativeObject::EnsureDenseResult result = NativeObject::ED_SPARSE;
         do {
             if (index > uint32_t(-1))
                 break;
             uint32_t idx = uint32_t(index);
             if (idx >= arr->length() && !arr->lengthIsWritable()) {
                 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
                                              JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
                 return false;
             }
             result = arr->ensureDenseElements(cx, idx, 1);
-            if (result != JSObject::ED_OK)
+            if (result != NativeObject::ED_OK)
                 break;
             if (idx >= arr->length())
                 arr->setLengthInt32(idx + 1);
             arr->setDenseElementWithType(cx, idx, v);
             return true;
         } while (false);
 
-        if (result == JSObject::ED_FAILED)
+        if (result == NativeObject::ED_FAILED)
             return false;
-        MOZ_ASSERT(result == JSObject::ED_SPARSE);
+        MOZ_ASSERT(result == NativeObject::ED_SPARSE);
     }
 
     RootedId id(cx);
     if (!ToId(cx, index, &id))
         return false;
 
     RootedValue tmp(cx, v);
     return JSObject::setGeneric(cx, obj, obj, id, &tmp, true);
@@ -331,26 +333,27 @@ SetArrayElement(JSContext *cx, HandleObj
  */
 static bool
 DeleteArrayElement(JSContext *cx, HandleObject obj, double index, bool *succeeded)
 {
     MOZ_ASSERT(index >= 0);
     MOZ_ASSERT(floor(index) == index);
 
     if (obj->is<ArrayObject>() && !obj->isIndexed()) {
+        ArrayObject *aobj = &obj->as<ArrayObject>();
         if (index <= UINT32_MAX) {
             uint32_t idx = uint32_t(index);
-            if (idx < obj->getDenseInitializedLength()) {
-                if (!obj->maybeCopyElementsForWrite(cx))
+            if (idx < aobj->getDenseInitializedLength()) {
+                if (!aobj->maybeCopyElementsForWrite(cx))
                     return false;
-                if (idx+1 == obj->getDenseInitializedLength()) {
-                    obj->setDenseInitializedLength(idx);
+                if (idx+1 == aobj->getDenseInitializedLength()) {
+                    aobj->setDenseInitializedLength(idx);
                 } else {
-                    obj->markDenseElementsNotPacked(cx);
-                    obj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE));
+                    aobj->markDenseElementsNotPacked(cx);
+                    aobj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE));
                 }
                 if (!SuppressDeletedElement(cx, obj, idx))
                     return false;
             }
         }
 
         *succeeded = true;
         return true;
@@ -509,17 +512,17 @@ js::ArraySetLength(typename ExecutionMod
             return false;
         return Throw(cxArg->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
     }
 
     /* Steps 6-7. */
     bool lengthIsWritable = arr->lengthIsWritable();
 #ifdef DEBUG
     {
-        RootedShape lengthShape(cxArg, arr->nativeLookupPure(id));
+        RootedShape lengthShape(cxArg, arr->lookupPure(id));
         MOZ_ASSERT(lengthShape);
         MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
     }
 #endif
 
     uint32_t oldLen = arr->length();
 
     /* Steps 8-9 for arrays with non-writable length. */
@@ -687,21 +690,21 @@ js::ArraySetLength(typename ExecutionMod
 
     /* Steps 12, 16. */
 
     // Yes, we totally drop a non-stub getter/setter from a defineProperty
     // API call on the floor here.  Given that getter/setter will go away in
     // the long run, with accessors replacing them both internally and at the
     // API level, just run with this.
     RootedShape lengthShape(cxArg, mode == ParallelExecution
-                            ? arr->nativeLookupPure(id)
-                            : arr->nativeLookup(cxArg->asJSContext(), id));
-    if (!JSObject::changeProperty<mode>(cxArg, arr, lengthShape, attrs,
-                                        JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
-                                        array_length_getter, array_length_setter))
+                            ? arr->lookupPure(id)
+                            : arr->lookup(cxArg->asJSContext(), id));
+    if (!NativeObject::changeProperty<mode>(cxArg, arr, lengthShape, attrs,
+                                            JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
+                                            array_length_getter, array_length_setter))
     {
         return false;
     }
 
     if (mode == ParallelExecution) {
         // Overflowing int32 requires changing TI state.
         if (newLen > INT32_MAX)
             return false;
@@ -842,41 +845,41 @@ js::ObjectMayHaveExtraIndexedProperties(
          * If the prototype is a non-native object (possibly a dense array), or
          * a native object (possibly a slow array) that has indexed properties,
          * return true.
          */
         if (!obj->isNative())
             return true;
         if (obj->isIndexed())
             return true;
-        if (obj->getDenseInitializedLength() > 0)
+        if (obj->as<NativeObject>().getDenseInitializedLength() > 0)
             return true;
         if (IsAnyTypedArray(obj))
             return true;
     }
 
     return false;
 }
 
 static bool
-AddLengthProperty(ExclusiveContext *cx, HandleObject obj)
+AddLengthProperty(ExclusiveContext *cx, HandleArrayObject obj)
 {
     /*
      * Add the 'length' property for a newly created array,
      * and update the elements to be an empty array owned by the object.
      * The shared emptyObjectElements singleton cannot be used for slow arrays,
      * as accesses to 'length' will use the elements header.
      */
 
     RootedId lengthId(cx, NameToId(cx->names().length));
-    MOZ_ASSERT(!obj->nativeLookup(cx, lengthId));
-
-    return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
-                                 SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0,
-                                 /* allowDictionary = */ false);
+    MOZ_ASSERT(!obj->lookup(cx, lengthId));
+
+    return NativeObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
+                                     SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0,
+                                     /* allowDictionary = */ false);
 }
 
 #if JS_HAS_TOSOURCE
 
 static bool
 array_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
@@ -983,22 +986,22 @@ ArrayJoinKernel(JSContext *cx, Separator
                StringBuffer &sb)
 {
     uint32_t i = 0;
 
     if (!Locale && obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
         // This loop handles all elements up to initializedLength. If
         // length > initLength we rely on the second loop to add the
         // other elements.
-        uint32_t initLength = obj->getDenseInitializedLength();
+        uint32_t initLength = obj->as<ArrayObject>().getDenseInitializedLength();
         while (i < initLength) {
             if (!CheckForInterrupt(cx))
                 return false;
 
-            const Value &elem = obj->getDenseElement(i);
+            const Value &elem = obj->as<ArrayObject>().getDenseElement(i);
 
             if (elem.isString()) {
                 if (!sb.append(elem.toString()))
                     return false;
             } else if (elem.isNumber()) {
                 if (!NumberValueToStringBuffer(cx, elem, sb))
                     return false;
             } else if (elem.isBoolean()) {
@@ -1064,19 +1067,19 @@ js::ArrayJoin(JSContext *cx, HandleObjec
     // Steps 1 to 6, should be done by the caller.
 
     // Step 6 is implicit in the loops below.
 
     // An optimized version of a special case of steps 7-11: when length==1 and
     // the 0th element is a string, ToString() of that element is a no-op and
     // so it can be immediately returned as the result.
     if (length == 1 && !Locale && obj->is<ArrayObject>() &&
-        obj->getDenseInitializedLength() == 1)
+        obj->as<ArrayObject>().getDenseInitializedLength() == 1)
     {
-        const Value &elem0 = obj->getDenseElement(0);
+        const Value &elem0 = obj->as<ArrayObject>().getDenseElement(0);
         if (elem0.isString()) {
             return elem0.toString();
         }
     }
 
     StringBuffer sb(cx);
     if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars())
         return nullptr;
@@ -1265,29 +1268,29 @@ InitArrayElements(JSContext *cx, HandleO
      * length.
      */
     do {
         if (!obj->is<ArrayObject>())
             break;
         if (ObjectMayHaveExtraIndexedProperties(obj))
             break;
 
-        if (obj->shouldConvertDoubleElements())
+        HandleArrayObject arr = obj.as<ArrayObject>();
+
+        if (arr->shouldConvertDoubleElements())
             break;
 
-        Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
-
         if (!arr->lengthIsWritable() && start + count > arr->length())
             break;
 
-        JSObject::EnsureDenseResult result = arr->ensureDenseElements(cx, start, count);
-        if (result != JSObject::ED_OK) {
-            if (result == JSObject::ED_FAILED)
+        NativeObject::EnsureDenseResult result = arr->ensureDenseElements(cx, start, count);
+        if (result != NativeObject::ED_OK) {
+            if (result == NativeObject::ED_FAILED)
                 return false;
-            MOZ_ASSERT(result == JSObject::ED_SPARSE);
+            MOZ_ASSERT(result == NativeObject::ED_SPARSE);
             break;
         }
 
         uint32_t newlen = start + count;
         if (newlen > arr->length())
             arr->setLengthInt32(newlen);
 
         MOZ_ASSERT(count < UINT32_MAX / sizeof(Value));
@@ -1339,68 +1342,71 @@ array_reverse(JSContext *cx, unsigned ar
         return false;
 
     do {
         if (!obj->is<ArrayObject>())
             break;
         if (ObjectMayHaveExtraIndexedProperties(obj))
             break;
 
+        HandleArrayObject arr = obj.as<ArrayObject>();
+
         /* An empty array or an array with no elements is already reversed. */
-        if (len == 0 || obj->getDenseCapacity() == 0) {
+        if (len == 0 || arr->getDenseCapacity() == 0) {
             args.rval().setObject(*obj);
             return true;
         }
 
         /*
          * It's actually surprisingly complicated to reverse an array due to the
          * orthogonality of array length and array capacity while handling
          * leading and trailing holes correctly.  Reversing seems less likely to
          * be a common operation than other array mass-mutation methods, so for
          * now just take a probably-small memory hit (in the absence of too many
          * holes in the array at its start) and ensure that the capacity is
          * sufficient to hold all the elements in the array if it were full.
          */
-        JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, len, 0);
-        if (result != JSObject::ED_OK) {
-            if (result == JSObject::ED_FAILED)
+        NativeObject::EnsureDenseResult result =
+            arr->ensureDenseElements(cx, len, 0);
+        if (result != NativeObject::ED_OK) {
+            if (result == NativeObject::ED_FAILED)
                 return false;
-            MOZ_ASSERT(result == JSObject::ED_SPARSE);
+            MOZ_ASSERT(result == NativeObject::ED_SPARSE);
             break;
         }
 
         /* Fill out the array's initialized length to its proper length. */
-        obj->ensureDenseInitializedLength(cx, len, 0);
+        arr->ensureDenseInitializedLength(cx, len, 0);
 
         RootedValue origlo(cx), orighi(cx);
 
         uint32_t lo = 0, hi = len - 1;
         for (; lo < hi; lo++, hi--) {
-            origlo = obj->getDenseElement(lo);
-            orighi = obj->getDenseElement(hi);
-            obj->setDenseElement(lo, orighi);
+            origlo = arr->getDenseElement(lo);
+            orighi = arr->getDenseElement(hi);
+            arr->setDenseElement(lo, orighi);
             if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
-                !SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
+                !SuppressDeletedProperty(cx, arr, INT_TO_JSID(lo)))
             {
                 return false;
             }
-            obj->setDenseElement(hi, origlo);
+            arr->setDenseElement(hi, origlo);
             if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
-                !SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
+                !SuppressDeletedProperty(cx, arr, INT_TO_JSID(hi)))
             {
                 return false;
             }
         }
 
         /*
          * Per ECMA-262, don't update the length of the array, even if the new
          * array has trailing holes (and thus the original array began with
          * holes).
          */
-        args.rval().setObject(*obj);
+        args.rval().setObject(*arr);
         return true;
     } while (false);
 
     RootedValue lowval(cx), hival(cx);
     for (uint32_t i = 0, half = len / 2; i < half; i++) {
         bool hole, hole2;
         if (!CheckForInterrupt(cx) ||
             !GetElement(cx, obj, i, &hole, &lowval) ||
@@ -2079,33 +2085,34 @@ js::array_push(JSContext *cx, unsigned a
 
         if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable())
             break;
 
         if (ObjectMayHaveExtraIndexedProperties(obj))
             break;
 
         uint32_t argCount = args.length();
-        JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, argCount);
-        if (result == JSObject::ED_FAILED)
+        NativeObject::EnsureDenseResult result =
+            obj->as<NativeObject>().ensureDenseElements(cx, length, argCount);
+        if (result == NativeObject::ED_FAILED)
             return false;
 
-        if (result == JSObject::ED_OK) {
+        if (result == NativeObject::ED_OK) {
             for (uint32_t i = 0, index = length; i < argCount; index++, i++)
-                obj->setDenseElementWithType(cx, index, args[i]);
+                obj->as<NativeObject>().setDenseElementWithType(cx, index, args[i]);
             uint32_t newlength = length + argCount;
             args.rval().setNumber(newlength);
             if (obj->is<ArrayObject>()) {
                 obj->as<ArrayObject>().setLengthInt32(newlength);
                 return true;
             }
             return SetLengthProperty(cx, obj, newlength);
         }
 
-        MOZ_ASSERT(result == JSObject::ED_SPARSE);
+        MOZ_ASSERT(result == NativeObject::ED_SPARSE);
     } while (false);
 
     /* Steps 4-5. */
     if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
         return false;
 
     /* Steps 6-7. */
     double newlength = length + double(args.length());
@@ -2147,20 +2154,19 @@ js::array_pop(JSContext *cx, unsigned ar
             return false;
     }
 
     /* Steps 4a, 5d. */
     return SetLengthProperty(cx, obj, index);
 }
 
 void
-js::ArrayShiftMoveElements(JSObject *obj)
+js::ArrayShiftMoveElements(ArrayObject *obj)
 {
-    MOZ_ASSERT(obj->is<ArrayObject>());
-    MOZ_ASSERT(obj->as<ArrayObject>().lengthIsWritable());
+    MOZ_ASSERT(obj->lengthIsWritable());
 
     /*
      * At this point the length and initialized length have already been
      * decremented and the result fetched, so just shift the array elements
      * themselves.
      */
     uint32_t initlen = obj->getDenseInitializedLength();
     obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
@@ -2191,35 +2197,37 @@ js::array_shift(JSContext *cx, unsigned 
         /* Step 4b. */
         args.rval().setUndefined();
         return true;
     }
 
     uint32_t newlen = len - 1;
 
     /* Fast paths. */
-    if (obj->is<ArrayObject>() &&
-        obj->getDenseInitializedLength() > 0 &&
-        newlen < obj->getDenseCapacity() &&
-        !ObjectMayHaveExtraIndexedProperties(obj))
-    {
-        args.rval().set(obj->getDenseElement(0));
-        if (args.rval().isMagic(JS_ELEMENTS_HOLE))
-            args.rval().setUndefined();
-
-        if (!obj->maybeCopyElementsForWrite(cx))
-            return false;
-
-        obj->moveDenseElements(0, 1, obj->getDenseInitializedLength() - 1);
-        obj->setDenseInitializedLength(obj->getDenseInitializedLength() - 1);
-
-        if (!SetLengthProperty(cx, obj, newlen))
-            return false;
-
-        return SuppressDeletedProperty(cx, obj, INT_TO_JSID(newlen));
+    if (obj->is<ArrayObject>()) {
+        ArrayObject *aobj = &obj->as<ArrayObject>();
+        if (aobj->getDenseInitializedLength() > 0 &&
+            newlen < aobj->getDenseCapacity() &&
+            !ObjectMayHaveExtraIndexedProperties(aobj))
+        {
+            args.rval().set(aobj->getDenseElement(0));
+            if (args.rval().isMagic(JS_ELEMENTS_HOLE))
+                args.rval().setUndefined();
+
+            if (!aobj->maybeCopyElementsForWrite(cx))
+                return false;
+
+            aobj->moveDenseElements(0, 1, aobj->getDenseInitializedLength() - 1);
+            aobj->setDenseInitializedLength(aobj->getDenseInitializedLength() - 1);
+
+            if (!SetLengthProperty(cx, obj, newlen))
+                return false;
+
+            return SuppressDeletedProperty(cx, obj, INT_TO_JSID(newlen));
+        }
     }
 
     /* Steps 5, 10. */
     bool hole;
     if (!GetElement(cx, obj, uint32_t(0), &hole, args.rval()))
         return false;
 
     /* Steps 6-7. */
@@ -2263,28 +2271,30 @@ js::array_unshift(JSContext *cx, unsigne
         /* Slide up the array to make room for all args at the bottom. */
         if (length > 0) {
             bool optimized = false;
             do {
                 if (!obj->is<ArrayObject>())
                     break;
                 if (ObjectMayHaveExtraIndexedProperties(obj))
                     break;
-                if (!obj->as<ArrayObject>().lengthIsWritable())
+                ArrayObject *aobj = &obj->as<ArrayObject>();
+                if (!aobj->lengthIsWritable())
                     break;
-                JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, args.length());
-                if (result != JSObject::ED_OK) {
-                    if (result == JSObject::ED_FAILED)
+                NativeObject::EnsureDenseResult result =
+                    aobj->ensureDenseElements(cx, length, args.length());
+                if (result != NativeObject::ED_OK) {
+                    if (result == NativeObject::ED_FAILED)
                         return false;
-                    MOZ_ASSERT(result == JSObject::ED_SPARSE);
+                    MOZ_ASSERT(result == NativeObject::ED_SPARSE);
                     break;
                 }
-                obj->moveDenseElements(args.length(), 0, length);
+                aobj->moveDenseElements(args.length(), 0, length);
                 for (uint32_t i = 0; i < args.length(); i++)
-                    obj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE));
+                    aobj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE));
                 optimized = true;
             } while (false);
 
             if (!optimized) {
                 double last = length;
                 double upperIndex = last + args.length();
                 RootedValue value(cx);
                 do {
@@ -2369,17 +2379,17 @@ CanOptimizeForDenseStorage(HandleObject 
         return false;
 
     /*
      * Now watch out for getters and setters along the prototype chain or in
      * other indexed properties on the object.  (Note that non-writable length
      * is subsumed by the initializedLength comparison.)
      */
     return !ObjectMayHaveExtraIndexedProperties(arr) &&
-           startingIndex + count <= arr->getDenseInitializedLength();
+           startingIndex + count <= arr->as<ArrayObject>().getDenseInitializedLength();
 }
 
 /* ES5 15.4.4.12. */
 bool
 js::array_splice(JSContext *cx, unsigned argc, Value *vp)
 {
     return array_splice_impl(cx, argc, vp, true);
 }
@@ -2428,17 +2438,17 @@ js::array_splice_impl(JSContext *cx, uns
     }
 
     MOZ_ASSERT(len - actualStart >= actualDeleteCount);
 
     /* Steps 2, 8-9. */
     Rooted<ArrayObject*> arr(cx);
     if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
         if (returnValueIsUsed) {
-            arr = NewDenseCopiedArray(cx, actualDeleteCount, obj, actualStart);
+            arr = NewDenseCopiedArray(cx, actualDeleteCount, obj.as<ArrayObject>(), actualStart);
             if (!arr)
                 return false;
             TryReuseArrayType(obj, arr);
         }
     } else {
         arr = NewDenseFullyAllocatedArray(cx, actualDeleteCount);
         if (!arr)
             return false;
@@ -2461,30 +2471,32 @@ js::array_splice_impl(JSContext *cx, uns
 
     if (itemCount < actualDeleteCount) {
         /* Step 12: the array is being shrunk. */
         uint32_t sourceIndex = actualStart + actualDeleteCount;
         uint32_t targetIndex = actualStart + itemCount;
         uint32_t finalLength = len - actualDeleteCount + itemCount;
 
         if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
-            if (!obj->maybeCopyElementsForWrite(cx))
+            ArrayObject *aobj = &obj->as<ArrayObject>();
+
+            if (!aobj->maybeCopyElementsForWrite(cx))
                 return false;
 
             /* Steps 12(a)-(b). */
-            obj->moveDenseElements(targetIndex, sourceIndex, len - sourceIndex);
+            aobj->moveDenseElements(targetIndex, sourceIndex, len - sourceIndex);
 
             /*
              * Update the initialized length. Do so before shrinking so that we
              * can apply the write barrier to the old slots.
              */
-            obj->setDenseInitializedLength(finalLength);
+            aobj->setDenseInitializedLength(finalLength);
 
             /* Steps 12(c)-(d). */
-            obj->shrinkElements(cx, finalLength);
+            aobj->shrinkElements(cx, finalLength);
         } else {
             /*
              * This is all very slow if the length is very large. We don't yet
              * have the ability to iterate in sorted order, so we just do the
              * pessimistic thing and let CheckForInterrupt handle the
              * fallout.
              */
 
@@ -2537,30 +2549,31 @@ js::array_splice_impl(JSContext *cx, uns
          * If the array length is non-writable, this method *will* throw.  For
          * simplicity, have the slow-path code do it.  (Also note that the slow
          * path may validly *not* throw -- if all the elements being moved are
          * holes.)
          */
         if (obj->is<ArrayObject>()) {
             Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
             if (arr->lengthIsWritable()) {
-                JSObject::EnsureDenseResult res =
+                NativeObject::EnsureDenseResult res =
                     arr->ensureDenseElements(cx, arr->length(), itemCount - actualDeleteCount);
-                if (res == JSObject::ED_FAILED)
+                if (res == NativeObject::ED_FAILED)
                     return false;
             }
         }
 
         if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
-            if (!obj->maybeCopyElementsForWrite(cx))
+            ArrayObject *aobj = &obj->as<ArrayObject>();
+            if (!aobj->maybeCopyElementsForWrite(cx))
                 return false;
-            obj->moveDenseElements(actualStart + itemCount,
-                                   actualStart + actualDeleteCount,
-                                   len - (actualStart + actualDeleteCount));
-            obj->setDenseInitializedLength(len + itemCount - actualDeleteCount);
+            aobj->moveDenseElements(actualStart + itemCount,
+                                    actualStart + actualDeleteCount,
+                                    len - (actualStart + actualDeleteCount));
+            aobj->setDenseInitializedLength(len + itemCount - actualDeleteCount);
         } else {
             RootedValue fromValue(cx);
             for (double k = len - actualDeleteCount; k > actualStart; k--) {
                 if (!CheckForInterrupt(cx))
                     return false;
 
                 double from = k + actualDeleteCount - 1;
                 double to = k + itemCount - 1;
@@ -2641,18 +2654,18 @@ js::array_concat(JSContext *cx, unsigned
     RootedObject aobj(cx, ToObject(cx, args.thisv()));
     if (!aobj)
         return false;
 
     Rooted<ArrayObject*> narr(cx);
     uint32_t length;
     if (aobj->is<ArrayObject>() && !aobj->isIndexed()) {
         length = aobj->as<ArrayObject>().length();
-        uint32_t initlen = aobj->getDenseInitializedLength();
-        narr = NewDenseCopiedArray(cx, initlen, aobj, 0);
+        uint32_t initlen = aobj->as<ArrayObject>().getDenseInitializedLength();
+        narr = NewDenseCopiedArray(cx, initlen, aobj.as<ArrayObject>(), 0);
         if (!narr)
             return false;
         TryReuseArrayType(aobj, narr);
         narr->setLength(cx, length);
         args.rval().setObject(*narr);
         if (argc == 0)
             return true;
         argc--;
@@ -2749,42 +2762,43 @@ js::array_slice(JSContext *cx, unsigned 
 
     Rooted<ArrayObject*> narr(cx);
     narr = NewDenseFullyAllocatedArray(cx, end - begin);
     if (!narr)
         return false;
     TryReuseArrayType(obj, narr);
 
     if (obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
-        if (obj->getDenseInitializedLength() > begin) {
-            uint32_t numSourceElements = obj->getDenseInitializedLength() - begin;
+        ArrayObject *aobj = &obj->as<ArrayObject>();
+        if (aobj->getDenseInitializedLength() > begin) {
+            uint32_t numSourceElements = aobj->getDenseInitializedLength() - begin;
             uint32_t initLength = Min(numSourceElements, end - begin);
             narr->setDenseInitializedLength(initLength);
-            narr->initDenseElements(0, &obj->getDenseElement(begin), initLength);
+            narr->initDenseElements(0, &aobj->getDenseElement(begin), initLength);
         }
         args.rval().setObject(*narr);
         return true;
     }
 
     if (js::SliceOp op = obj->getOps()->slice) {
         // Ensure that we have dense elements, so that DOM can use js::UnsafeDefineElement.
-        JSObject::EnsureDenseResult result = narr->ensureDenseElements(cx, 0, end - begin);
-        if (result == JSObject::ED_FAILED)
+        NativeObject::EnsureDenseResult result = narr->ensureDenseElements(cx, 0, end - begin);
+        if (result == NativeObject::ED_FAILED)
              return false;
 
-        if (result == JSObject::ED_OK) {
+        if (result == NativeObject::ED_OK) {
             if (!op(cx, obj, begin, end, narr))
                 return false;
 
             args.rval().setObject(*narr);
             return true;
         }
 
         // Fallthrough
-        MOZ_ASSERT(result == JSObject::ED_SPARSE);
+        MOZ_ASSERT(result == NativeObject::ED_SPARSE);
     }
 
 
     if (!SliceSlowly(cx, obj, obj, begin, end, narr))
         return false;
 
     args.rval().setObject(*narr);
     return true;
@@ -3109,19 +3123,24 @@ CreateArrayPrototype(JSContext *cx, JSPr
         return nullptr;
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
                                                       proto->getParent(), metadata,
                                                       gc::FINALIZE_OBJECT0));
     if (!shape)
         return nullptr;
 
-    RootedObject arrayProto(cx, JSObject::createArray(cx, gc::FINALIZE_OBJECT4, gc::TenuredHeap, shape, type, 0));
-    if (!arrayProto || !JSObject::setSingletonType(cx, arrayProto) || !AddLengthProperty(cx, arrayProto))
+    RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::FINALIZE_OBJECT4,
+                                                              gc::TenuredHeap, shape, type, 0));
+    if (!arrayProto ||
+        !JSObject::setSingletonType(cx, arrayProto) ||
+        !AddLengthProperty(cx, arrayProto))
+    {
         return nullptr;
+    }
 
     /*
      * The default 'new' type of Array.prototype is required by type inference
      * to have unknown properties, to simplify handling of e.g. heterogenous
      * arrays in JSON and script literals and allows setDenseArrayElement to
      * be used without updating the indexed type set for such default arrays.
      */
     if (!JSObject::setNewTypeUnknown(cx, &ArrayObject::class_, arrayProto))
@@ -3153,17 +3172,17 @@ const Class ArrayObject::class_ = {
     }
 };
 
 /*
  * Array allocation functions.
  */
 
 static inline bool
-EnsureNewArrayElements(ExclusiveContext *cx, JSObject *obj, uint32_t length)
+EnsureNewArrayElements(ExclusiveContext *cx, ArrayObject *obj, uint32_t length)
 {
     /*
      * If ensureElements creates dynamically allocated slots, then having
      * fixedSlots is a waste.
      */
     DebugOnly<uint32_t> cap = obj->getDenseCapacity();
 
     if (!obj->ensureElements(cx, length))
@@ -3232,17 +3251,17 @@ NewArray(ExclusiveContext *cxArg, uint32
      * See JSObject::createArray.
      */
     RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_,
                                                          TaggedProto(proto), cxArg->global(),
                                                          metadata, gc::FINALIZE_OBJECT0));
     if (!shape)
         return nullptr;
 
-    Rooted<ArrayObject*> arr(cxArg, JSObject::createArray(cxArg, allocKind,
+    RootedArrayObject arr(cxArg, ArrayObject::createArray(cxArg, allocKind,
                                                           GetInitialHeap(newKind, &ArrayObject::class_),
                                                           shape, type, length));
     if (!arr)
         return nullptr;
 
     if (shape->isEmptyShape()) {
         if (!AddLengthProperty(cxArg, arr))
             return nullptr;
@@ -3272,17 +3291,17 @@ js::NewDenseEmptyArray(JSContext *cx, JS
     return NewArray<0>(cx, 0, proto, newKind);
 }
 
 ArrayObject * JS_FASTCALL
 js::NewDenseFullyAllocatedArray(ExclusiveContext *cx, uint32_t length,
                                 JSObject *proto /* = nullptr */,
                                 NewObjectKind newKind /* = GenericObject */)
 {
-    return NewArray<JSObject::NELEMENTS_LIMIT>(cx, length, proto, newKind);
+    return NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto, newKind);
 }
 
 ArrayObject * JS_FASTCALL
 js::NewDensePartlyAllocatedArray(ExclusiveContext *cx, uint32_t length,
                                  JSObject *proto /* = nullptr */,
                                  NewObjectKind newKind /* = GenericObject */)
 {
     return NewArray<ArrayObject::EagerAllocationMaxLength>(cx, length, proto, newKind);
@@ -3322,22 +3341,22 @@ js::NewDenseArray(ExclusiveContext *cx, 
     // new type.
     if (arr->length() > INT32_MAX)
         arr->setLength(cx, arr->length());
 
     return arr;
 }
 
 ArrayObject *
-js::NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset,
-                        JSObject *proto /* = nullptr */)
+js::NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleArrayObject src,
+                        uint32_t elementOffset, JSObject *proto /* = nullptr */)
 {
     MOZ_ASSERT(!src->isIndexed());
 
-    ArrayObject* arr = NewArray<JSObject::NELEMENTS_LIMIT>(cx, length, proto);
+    ArrayObject *arr = NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto);
     if (!arr)
         return nullptr;
 
     MOZ_ASSERT(arr->getDenseCapacity() >= length);
 
     const Value* vp = src->getDenseElements() + elementOffset;
     arr->setDenseInitializedLength(vp ? length : 0);
 
@@ -3347,17 +3366,17 @@ js::NewDenseCopiedArray(JSContext *cx, u
     return arr;
 }
 
 // values must point at already-rooted Value objects
 ArrayObject *
 js::NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values,
                         JSObject *proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */)
 {
-    ArrayObject* arr = NewArray<JSObject::NELEMENTS_LIMIT>(cx, length, proto);
+    ArrayObject *arr = NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto);
     if (!arr)
         return nullptr;
 
     MOZ_ASSERT(arr->getDenseCapacity() >= length);
 
     arr->setDenseInitializedLength(values ? length : 0);
 
     if (values)
@@ -3372,45 +3391,46 @@ js::NewDenseFullyAllocatedArrayWithTempl
     gc::AllocKind allocKind = GuessArrayGCKind(length);
     MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
     allocKind = GetBackgroundAllocKind(allocKind);
 
     RootedTypeObject type(cx, templateObject->type());
     RootedShape shape(cx, templateObject->lastProperty());
 
     gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
-    Rooted<ArrayObject *> arr(cx, JSObject::createArray(cx, allocKind, heap, shape, type, length));
+    Rooted<ArrayObject *> arr(cx, ArrayObject::createArray(cx, allocKind,
+                                                           heap, shape, type, length));
     if (!arr)
         return nullptr;
 
     if (!EnsureNewArrayElements(cx, arr, length))
         return nullptr;
 
     probes::CreateObject(cx, arr);
 
     return arr;
 }
 
 JSObject *
-js::NewDenseCopyOnWriteArray(JSContext *cx, HandleObject templateObject, gc::InitialHeap heap)
+js::NewDenseCopyOnWriteArray(JSContext *cx, HandleNativeObject templateObject, gc::InitialHeap heap)
 {
     RootedShape shape(cx, templateObject->lastProperty());
 
     MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
 
     JSObject *metadata = nullptr;
     if (!NewObjectMetadata(cx, &metadata))
         return nullptr;
     if (metadata) {
         shape = Shape::setObjectMetadata(cx, metadata, templateObject->getTaggedProto(), shape);
         if (!shape)
             return nullptr;
     }
 
-    Rooted<ArrayObject *> arr(cx, JSObject::createCopyOnWriteArray(cx, heap, shape, templateObject));
+    ArrayObject *arr = ArrayObject::createCopyOnWriteArray(cx, heap, shape, templateObject);
     if (!arr)
         return nullptr;
 
     probes::CreateObject(cx, arr);
     return arr;
 }
 
 #ifdef DEBUG
@@ -3428,17 +3448,17 @@ js_ArrayInfo(JSContext *cx, unsigned arg
             return false;
         if (arg.isPrimitive() ||
             !(obj = arg.toObjectOrNull())->is<ArrayObject>()) {
             fprintf(stderr, "%s: not array\n", bytes);
             js_free(bytes);
             continue;
         }
         fprintf(stderr, "%s: (len %u", bytes, obj->as<ArrayObject>().length());
-        fprintf(stderr, ", capacity %u", obj->getDenseCapacity());
+        fprintf(stderr, ", capacity %u", obj->as<ArrayObject>().getDenseCapacity());
         fputs(")\n", stderr);
         js_free(bytes);
     }
 
     args.rval().setUndefined();
     return true;
 }
 #endif
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -80,30 +80,31 @@ enum AllocatingBehaviour {
  * contents if the length is not excessive.
  */
 extern ArrayObject *
 NewDenseArray(ExclusiveContext *cx, uint32_t length, HandleTypeObject type,
               AllocatingBehaviour allocating);
 
 /* Create a dense array with a copy of the dense array elements in src. */
 extern ArrayObject *
-NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset, JSObject *proto = nullptr);
+NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleArrayObject src,
+                    uint32_t elementOffset, JSObject *proto = nullptr);
 
 /* Create a dense array from the given array values, which must be rooted */
 extern ArrayObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values, JSObject *proto = nullptr,
                     NewObjectKind newKind = GenericObject);
 
 /* Create a dense array based on templateObject with the given length. */
 extern ArrayObject *
 NewDenseFullyAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSObject *templateObject);
 
 /* Create a dense array with the same copy-on-write elements as another object. */
 extern JSObject *
-NewDenseCopyOnWriteArray(JSContext *cx, HandleObject templateObject, gc::InitialHeap heap);
+NewDenseCopyOnWriteArray(JSContext *cx, HandleNativeObject templateObject, gc::InitialHeap heap);
 
 /*
  * Determines whether a write to the given element on |obj| should fail because
  * |obj| is an Array with a non-writable length, and writing that element would
  * increase the length of the array.
  */
 extern bool
 WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
@@ -169,17 +170,17 @@ array_concat_dense(JSContext *cx, Handle
 
 bool
 array_join(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSString *
 array_join_impl(JSContext *cx, HandleValue array, HandleString sep);
 
 extern void
-ArrayShiftMoveElements(JSObject *obj);
+ArrayShiftMoveElements(ArrayObject *obj);
 
 extern bool
 array_shift(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 array_unshift(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -135,17 +135,17 @@ Boolean(JSContext *cx, unsigned argc, Va
 
 JSObject *
 js_InitBooleanClass(JSContext *cx, HandleObject obj)
 {
     MOZ_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
-    RootedObject booleanProto (cx, global->createBlankPrototype(cx, &BooleanObject::class_));
+    RootedNativeObject booleanProto(cx, global->createBlankPrototype(cx, &BooleanObject::class_));
     if (!booleanProto)
         return nullptr;
     booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT, BooleanValue(false));
 
     RootedFunction ctor(cx, global->createConstructor(cx, Boolean, cx->names().Boolean, 1));
     if (!ctor)
         return nullptr;
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -519,17 +519,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();
     RootedId id(cx, NameToId(usageAtom));
-    DebugOnly<Shape *> shape = static_cast<Shape *>(callee->nativeLookup(cx, id));
+    DebugOnly<Shape *> shape = static_cast<Shape *>(callee->as<NativeObject>().lookup(cx, id));
     MOZ_ASSERT(!shape->configurable());
     MOZ_ASSERT(!shape->writable());
     MOZ_ASSERT(shape->hasDefaultGetter());
 
     RootedValue usage(cx);
     if (!JS_LookupProperty(cx, callee, "usage", &usage))
         return;
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -156,19 +156,19 @@ namespace frontend { struct CompileError
 
 struct ThreadSafeContext : ContextFriendFields,
                            public MallocProvider<ThreadSafeContext>
 {
     friend struct StackBaseShape;
     friend class Activation;
     friend UnownedBaseShape *BaseShape::lookupUnowned(ThreadSafeContext *cx,
                                                       const StackBaseShape &base);
-    friend Shape *JSObject::lookupChildProperty(ThreadSafeContext *cx,
-                                                JS::HandleObject obj, js::HandleShape parent,
-                                                js::StackShape &child);
+    friend Shape *NativeObject::lookupChildProperty(ThreadSafeContext *cx,
+                                                    HandleNativeObject obj, HandleShape parent,
+                                                    StackShape &child);
 
   public:
     enum ContextKind {
         Context_JS,
         Context_Exclusive,
         Context_ForkJoin
     };
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3004,18 +3004,18 @@ FinishDateClassInit(JSContext *cx, Handl
 
     /*
      * Date.prototype.toGMTString has the same initial value as
      * Date.prototype.toUTCString.
      */
     RootedValue toUTCStringFun(cx);
     RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
     RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
-    return baseops::GetProperty(cx, proto, toUTCStringId, &toUTCStringFun) &&
-           baseops::DefineGeneric(cx, proto, toGMTStringId, toUTCStringFun,
+    return baseops::GetProperty(cx, proto.as<NativeObject>(), toUTCStringId, &toUTCStringFun) &&
+           baseops::DefineGeneric(cx, proto.as<NativeObject>(), toGMTStringId, toUTCStringFun,
                                   JS_PropertyStub, JS_StrictPropertyStub, 0);
 }