Bug 1107496 - Add PlainObject subclass, r=luke.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 05 Dec 2014 18:52:28 -0700
changeset 244442 516d97ff2d4dec871cc394ea91735b9aaf6a42de
parent 244441 06c2fd99ff90f8bc4f0a3f9d80f41bc821f9c895
child 244443 42df82421ca2e675834d1a03bd735d95b5bd4611
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1107496
milestone37.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 1107496 - Add PlainObject subclass, r=luke.
js/src/asmjs/AsmJSLink.cpp
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/Object.h
js/src/builtin/RegExp.cpp
js/src/builtin/SymbolObject.cpp
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/WeakSetObject.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/gc/Barrier.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/Rooting.h
js/src/jit-test/tests/TypedObject/function-names.js
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/Recover.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jsapi-tests/testNewObject.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsbool.cpp
js/src/jscntxt.cpp
js/src/jsdate.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsprototypes.h
js/src/jsreflect.cpp
js/src/jsscript.cpp
js/src/proxy/ScriptedDirectProxyHandler.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/ArrayObject-inl.h
js/src/vm/ArrayObject.h
js/src/vm/Debugger.cpp
js/src/vm/DebuggerMemory.cpp
js/src/vm/GeneratorObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/Interpreter.cpp
js/src/vm/JSONParser.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/NativeObject.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/Shape.cpp
js/src/vm/StructuredClone.cpp
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -754,17 +754,17 @@ CallAsmJS(JSContext *cx, unsigned argc, 
             return false;
     }
 
     if (callArgs.isConstructing()) {
         // By spec, when a function is called as a constructor and this function
         // returns a primary type, which is the case for all asm.js exported
         // functions, the returned value is discarded and an empty object is
         // returned instead.
-        JSObject *obj = NewBuiltinClassInstance(cx, &JSObject::class_);
+        PlainObject *obj = NewBuiltinClassInstance<PlainObject>(cx);
         callArgs.rval().set(ObjectValue(*obj));
         return true;
     }
 
     JSObject *simdObj;
     switch (func.returnType()) {
       case AsmJSModule::Return_Void:
         callArgs.rval().set(UndefinedValue());
@@ -992,17 +992,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());
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_, allocKind));
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, 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/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -415,17 +415,17 @@ IntlInitialize(JSContext *cx, HandleObje
     args[2].set(options);
 
     return Invoke(cx, args);
 }
 
 static bool
 CreateDefaultOptions(JSContext *cx, MutableHandleValue defaultOptions)
 {
-    RootedObject options(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, cx->global()));
+    RootedObject options(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, cx->global()));
     if (!options)
         return false;
     defaultOptions.setObject(*options);
     return true;
 }
 
 // CountAvailable and GetAvailable describe the signatures used for ICU API
 // to determine available locales for various functionality.
@@ -434,17 +434,17 @@ typedef int32_t
 
 typedef const char *
 (* GetAvailable)(int32_t localeIndex);
 
 static bool
 intl_availableLocales(JSContext *cx, CountAvailable countAvailable,
                       GetAvailable getAvailable, MutableHandleValue result)
 {
-    RootedObject locales(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, nullptr));
+    RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, nullptr));
     if (!locales)
         return false;
 
 #if ENABLE_INTL_API
     uint32_t count = countAvailable();
     RootedValue t(cx, BooleanValue(true));
     for (uint32_t i = 0; i < count; i++) {
         const char *locale = getAvailable(i);
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -899,18 +899,18 @@ MapIteratorObject::kind() const
 }
 
 bool
 GlobalObject::initMapIteratorProto(JSContext *cx, Handle<GlobalObject *> global)
 {
     JSObject *base = GlobalObject::getOrCreateIteratorPrototype(cx, global);
     if (!base)
         return false;
-    RootedNativeObject proto(cx,
-        NewNativeObjectWithGivenProto(cx, &MapIteratorObject::class_, base, global));
+    Rooted<MapIteratorObject *> proto(cx,
+        NewObjectWithGivenProto<MapIteratorObject>(cx, 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;
 }
@@ -923,25 +923,25 @@ 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;
 
-    NativeObject *iterobj = NewNativeObjectWithGivenProto(cx, &class_, proto, global);
+    MapIteratorObject *iterobj = NewObjectWithGivenProto<MapIteratorObject>(cx, 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);
+    return iterobj;
 }
 
 void
 MapIteratorObject::finalize(FreeOp *fop, JSObject *obj)
 {
     fop->delete_(obj->as<MapIteratorObject>().range());
 }
 
@@ -1202,29 +1202,29 @@ MapObject::set(JSContext *cx, HandleObje
     }
     WriteBarrierPost(cx->runtime(), map, key.get());
     return true;
 }
 
 MapObject*
 MapObject::create(JSContext *cx)
 {
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &class_));
+    Rooted<MapObject *> obj(cx, NewBuiltinClassInstance<MapObject>(cx));
     if (!obj)
         return nullptr;
 
     ValueMap *map = cx->new_<ValueMap>(cx->runtime());
     if (!map || !map->init()) {
         js_delete(map);
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 
     obj->setPrivate(map);
-    return &obj->as<MapObject>();
+    return obj;
 }
 
 void
 MapObject::finalize(FreeOp *fop, JSObject *obj)
 {
     if (ValueMap *map = obj->as<MapObject>().getData())
         fop->delete_(map);
 }
@@ -1581,18 +1581,18 @@ SetIteratorObject::kind() const
 }
 
 bool
 GlobalObject::initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global)
 {
     JSObject *base = GlobalObject::getOrCreateIteratorPrototype(cx, global);
     if (!base)
         return false;
-    RootedNativeObject proto(cx, NewNativeObjectWithGivenProto(cx, &SetIteratorObject::class_,
-                                                               base, global));
+    Rooted<SetIteratorObject *> proto(cx,
+        NewObjectWithGivenProto<SetIteratorObject>(cx, 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;
 }
@@ -1605,25 +1605,25 @@ 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;
 
-    NativeObject *iterobj = NewNativeObjectWithGivenProto(cx, &class_, proto, global);
+    SetIteratorObject *iterobj = NewObjectWithGivenProto<SetIteratorObject>(cx, 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);
+    return iterobj;
 }
 
 void
 SetIteratorObject::finalize(FreeOp *fop, JSObject *obj)
 {
     fop->delete_(obj->as<SetIteratorObject>().range());
 }
 
@@ -1781,28 +1781,28 @@ SetObject::add(JSContext *cx, HandleObje
     }
     WriteBarrierPost(cx->runtime(), set, key.get());
     return true;
 }
 
 SetObject*
 SetObject::create(JSContext *cx)
 {
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &class_));
+    SetObject *obj = NewBuiltinClassInstance<SetObject>(cx);
     if (!obj)
         return nullptr;
 
     ValueSet *set = cx->new_<ValueSet>(cx->runtime());
     if (!set || !set->init()) {
         js_delete(set);
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
     obj->setPrivate(set);
-    return &obj->as<SetObject>();
+    return obj;
 }
 
 void
 SetObject::mark(JSTracer *trc, JSObject *obj)
 {
     SetObject *setobj = static_cast<SetObject *>(obj);
     if (ValueSet *set = setobj->getData()) {
         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront())
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -6,16 +6,17 @@
 
 #include "builtin/Object.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jscntxt.h"
 
+#include "builtin/Eval.h"
 #include "frontend/BytecodeCompiler.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
@@ -316,17 +317,17 @@ js::ObjectToSource(JSContext *cx, Handle
 }
 #endif /* JS_HAS_TOSOURCE */
 
 JSString *
 JS_BasicObjectToString(JSContext *cx, HandleObject obj)
 {
     // Some classes are really common, don't allocate new strings for them.
     // The ordering below is based on the measurements in bug 966264.
-    if (obj->is<JSObject>())
+    if (obj->is<PlainObject>())
         return cx->names().objectObject;
     if (obj->is<StringObject>())
         return cx->names().objectString;
     if (obj->is<ArrayObject>())
         return cx->names().objectArray;
     if (obj->is<JSFunction>())
         return cx->names().objectFunction;
     if (obj->is<NumberObject>())
@@ -424,17 +425,17 @@ DefineAccessor(JSContext *cx, unsigned a
                              Type == GetterAccessor ? js_getter_str : js_setter_str);
         return false;
     }
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[0], &id))
         return false;
 
-    RootedObject descObj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    RootedObject descObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!descObj)
         return false;
 
     JSAtomState &names = cx->names();
     RootedValue trueVal(cx, BooleanValue(true));
 
     /* enumerable: true */
     if (!JSObject::defineProperty(cx, descObj, names.enumerable, trueVal))
@@ -807,17 +808,17 @@ js::obj_create(JSContext *cx, unsigned a
     }
 
     RootedObject proto(cx, v.toObjectOrNull());
 
     /*
      * Use the callee's global as the parent of the new object to avoid dynamic
      * scoping (i.e., using the caller's global).
      */
-    RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, &args.callee().global()));
+    RootedObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, &args.callee().global()));
     if (!obj)
         return false;
 
     /* 15.2.3.5 step 4. */
     if (args.hasDefined(1)) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
             return false;
@@ -1178,18 +1179,17 @@ ProtoSetter(JSContext *cx, unsigned argc
         js_ReportValueError(cx, JSMSG_SETPROTOTYPEOF_FAIL, JSDVG_IGNORE_STACK, thisv, js::NullPtr());
         return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
-
-const JSFunctionSpec js::object_methods[] = {
+static const JSFunctionSpec object_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,             obj_toSource,                0,0),
 #endif
     JS_FN(js_toString_str,             obj_toString,                0,0),
     JS_FN(js_toLocaleString_str,       obj_toLocaleString,          0,0),
     JS_FN(js_valueOf_str,              obj_valueOf,                 0,0),
 #if JS_HAS_OBJ_WATCHPOINT
     JS_FN(js_watch_str,                obj_watch,                   2,0),
@@ -1202,24 +1202,24 @@ const JSFunctionSpec js::object_methods[
     JS_FN(js_defineGetter_str,         js::obj_defineGetter,        2,0),
     JS_FN(js_defineSetter_str,         js::obj_defineSetter,        2,0),
     JS_FN(js_lookupGetter_str,         obj_lookupGetter,            1,0),
     JS_FN(js_lookupSetter_str,         obj_lookupSetter,            1,0),
 #endif
     JS_FS_END
 };
 
-const JSPropertySpec js::object_properties[] = {
+static const JSPropertySpec object_properties[] = {
 #if JS_HAS_OBJ_PROTO_PROP
     JS_PSGS("__proto__", ProtoGetter, ProtoSetter, 0),
 #endif
     JS_PS_END
 };
 
-const JSFunctionSpec js::object_static_methods[] = {
+static const JSFunctionSpec object_static_methods[] = {
     JS_FN("getPrototypeOf",            obj_getPrototypeOf,          1,0),
     JS_FN("setPrototypeOf",            obj_setPrototypeOf,          2,0),
     JS_FN("getOwnPropertyDescriptor",  obj_getOwnPropertyDescriptor,2,0),
     JS_FN("keys",                      obj_keys,                    1,0),
     JS_FN("is",                        obj_is,                      2,0),
     JS_FN("defineProperty",            obj_defineProperty,          3,0),
     JS_FN("defineProperties",          obj_defineProperties,        2,0),
     JS_FN("create",                    obj_create,                  2,0),
@@ -1234,13 +1234,139 @@ const JSFunctionSpec js::object_static_m
     JS_FS_END
 };
 
 /*
  * For Object, self-hosted functions have to be done at a different
  * time, after the intrinsic holder has been set, so we put them
  * in a different array.
  */
-const JSFunctionSpec js::object_static_selfhosted_methods[] = {
+static const JSFunctionSpec object_static_selfhosted_methods[] = {
     JS_SELF_HOSTED_FN("assign",        "ObjectStaticAssign",        2,0),
     JS_FS_END
 };
 
+static JSObject *
+CreateObjectConstructor(JSContext *cx, JSProtoKey key)
+{
+    Rooted<GlobalObject*> self(cx, cx->global());
+    if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
+        return nullptr;
+
+    RootedObject functionProto(cx, &self->getPrototype(JSProto_Function).toObject());
+
+    /* Create the Object function now that we have a [[Prototype]] for it. */
+    RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto,
+                                                  self, SingletonObject));
+    if (!ctor)
+        return nullptr;
+    return NewFunction(cx, ctor, obj_construct, 1, JSFunction::NATIVE_CTOR, self,
+                       HandlePropertyName(cx->names().Object));
+}
+
+static JSObject *
+CreateObjectPrototype(JSContext *cx, JSProtoKey key)
+{
+    Rooted<GlobalObject*> self(cx, cx->global());
+
+    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    MOZ_ASSERT(self->isNative());
+
+    /*
+     * Create |Object.prototype| first, mirroring CreateBlankProto but for the
+     * prototype of the created object.
+     */
+    RootedPlainObject objectProto(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
+                                                                           self, SingletonObject));
+    if (!objectProto)
+        return nullptr;
+
+    /*
+     * The default 'new' type of Object.prototype is required by type inference
+     * to have unknown properties, to simplify handling of e.g. heterogenous
+     * objects in JSON and script literals.
+     */
+    if (!JSObject::setNewTypeUnknown(cx, &PlainObject::class_, objectProto))
+        return nullptr;
+
+    return objectProto;
+}
+
+static bool
+FinishObjectClassInit(JSContext *cx, JS::HandleObject ctor, JS::HandleObject proto)
+{
+    Rooted<GlobalObject*> self(cx, cx->global());
+
+    /* ES5 15.1.2.1. */
+    RootedId evalId(cx, NameToId(cx->names().eval));
+    JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
+    if (!evalobj)
+        return false;
+    self->setOriginalEval(evalobj);
+
+    RootedObject intrinsicsHolder(cx);
+    bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(self);
+    if (isSelfHostingGlobal) {
+        intrinsicsHolder = self;
+    } else {
+        intrinsicsHolder = NewObjectWithGivenProto<PlainObject>(cx, proto, self, TenuredObject);
+        if (!intrinsicsHolder)
+            return false;
+    }
+    self->setIntrinsicsHolder(intrinsicsHolder);
+    /* Define a property 'global' with the current global as its value. */
+    RootedValue global(cx, ObjectValue(*self));
+    if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global,
+                                  global, JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_PERMANENT | JSPROP_READONLY))
+    {
+        return false;
+    }
+
+    /*
+     * Define self-hosted functions after setting the intrinsics holder
+     * (which is needed to define self-hosted functions)
+     */
+    if (!isSelfHostingGlobal) {
+        if (!JS_DefineFunctions(cx, ctor, object_static_selfhosted_methods))
+            return false;
+    }
+
+    /*
+     * The global object should have |Object.prototype| as its [[Prototype]].
+     * Eventually we'd like to have standard classes be there from the start,
+     * and thus we would know we were always setting what had previously been a
+     * null [[Prototype]], but right now some code assumes it can set the
+     * [[Prototype]] before standard classes have been initialized.  For now,
+     * only set the [[Prototype]] if it hasn't already been set.
+     */
+    Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
+    if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->getClass(), tagged))
+        return false;
+    return true;
+}
+
+const Class PlainObject::class_ = {
+    js_Object_str,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
+    JS_PropertyStub,         /* addProperty */
+    JS_DeletePropertyStub,   /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,                 /* finalize */
+    nullptr,                 /* call */
+    nullptr,                 /* hasInstance */
+    nullptr,                 /* construct */
+    nullptr,                 /* trace */
+    {
+        CreateObjectConstructor,
+        CreateObjectPrototype,
+        object_static_methods,
+        object_methods,
+        object_properties,
+        FinishObjectClassInit
+    }
+};
+
+const Class* const js::ObjectClassPtr = &PlainObject::class_;
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -11,21 +11,16 @@
 
 namespace JS {
 class CallArgs;
 class Value;
 }
 
 namespace js {
 
-extern const JSFunctionSpec object_methods[];
-extern const JSPropertySpec object_properties[];
-extern const JSFunctionSpec object_static_methods[];
-extern const JSFunctionSpec object_static_selfhosted_methods[];
-
 // Object constructor native. Exposed only so the JIT can know its address.
 bool
 obj_construct(JSContext *cx, unsigned argc, JS::Value *vp);
 
 bool
 obj_valueOf(JSContext *cx, unsigned argc, JS::Value *vp);
 
 // Object methods exposed so they can be installed in the self-hosting global.
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -502,23 +502,23 @@ static const JSPropertySpec regexp_stati
 
 JSObject *
 js_InitRegExpClass(JSContext *cx, HandleObject obj)
 {
     MOZ_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
-    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &RegExpObject::class_));
+    Rooted<RegExpObject*> proto(cx, global->createBlankPrototype<RegExpObject>(cx));
     if (!proto)
         return nullptr;
-    proto->setPrivate(nullptr);
+    proto->NativeObject::setPrivate(nullptr);
 
     HandlePropertyName empty = cx->names().empty;
-    RegExpObjectBuilder builder(cx, &proto->as<RegExpObject>());
+    RegExpObjectBuilder builder(cx, proto);
     if (!builder.build(empty, RegExpFlag(0)))
         return nullptr;
 
     if (!DefinePropertiesAndFunctions(cx, proto, nullptr, regexp_methods))
         return nullptr;
 
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, regexp_construct, cx->names().RegExp, 2);
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -58,17 +58,17 @@ const JSFunctionSpec SymbolObject::stati
 JSObject *
 SymbolObject::initClass(JSContext *cx, HandleObject obj)
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     // This uses &JSObject::class_ because: "The Symbol prototype object is an
     // ordinary object. It is not a Symbol instance and does not have a
     // [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
-    RootedObject proto(cx, global->createBlankPrototype(cx, &JSObject::class_));
+    RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
     if (!proto)
         return nullptr;
 
     RootedFunction ctor(cx, global->createConstructor(cx, construct,
                                                       ClassName(JSProto_Symbol, cx), 1));
     if (!ctor)
         return nullptr;
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1207,17 +1207,17 @@ js::testingFunc_inParallelSection(JSCont
     MOZ_ASSERT(!InParallelSection());
     args.rval().setBoolean(false);
     return true;
 }
 
 static bool
 ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
 {
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
         return false;
 
     RootedObject stack(cx, NewDenseEmptyArray(cx));
     if (!stack)
         return false;
 
     static int createdIndex = 0;
@@ -2020,17 +2020,17 @@ FindPath(JSContext *cx, unsigned argc, j
     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.
-        RootedObject obj(cx, NewBuiltinClassInstance<JSObject>(cx));
+        RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
 
         if (!JS_DefineProperty(cx, obj, "node", nodes[i],
                                JSPROP_ENUMERATE, nullptr, nullptr))
             return false;
 
         heaptools::EdgeName edgeName = Move(edges[i]);
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -803,21 +803,21 @@ StructMetaTypeDescr::create(JSContext *c
     AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
     AutoValueVector fieldOffsets(cx);  // Offset of each field field.
     RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
     RootedObject userFieldTypes(cx);   // User-exposed {f:descr} object.
     CheckedInt32 sizeSoFar(0);         // Size of struct thus far.
     int32_t alignment = 1;             // Alignment of struct.
     bool opaque = false;               // Opacity of struct.
 
-    userFieldOffsets = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
+    userFieldOffsets = NewObjectWithProto<PlainObject>(cx, nullptr, nullptr, TenuredObject);
     if (!userFieldOffsets)
         return nullptr;
 
-    userFieldTypes = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
+    userFieldTypes = NewObjectWithProto<PlainObject>(cx, nullptr, nullptr, TenuredObject);
     if (!userFieldTypes)
         return nullptr;
 
     if (!stringBuffer.append("new StructType({")) {
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 
@@ -1062,17 +1062,17 @@ size_t
 StructTypeDescr::maybeForwardedFieldCount() const
 {
     return maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseInitializedLength();
 }
 
 bool
 StructTypeDescr::fieldIndex(jsid id, size_t *out) const
 {
-    NativeObject &fieldNames = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES);
+    ArrayObject &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;
         }
     }
@@ -1083,41 +1083,41 @@ JSAtom &
 StructTypeDescr::fieldName(size_t index) const
 {
     return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseElement(index).toString()->asAtom();
 }
 
 size_t
 StructTypeDescr::fieldOffset(size_t index) const
 {
-    NativeObject &fieldOffsets = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
+    ArrayObject &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
 {
-    NativeObject &fieldOffsets = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
+    ArrayObject &fieldOffsets = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
     MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
     return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
 }
 
 TypeDescr&
 StructTypeDescr::fieldDescr(size_t index) const
 {
-    NativeObject &fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
+    ArrayObject &fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
     return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
 }
 
 TypeDescr&
 StructTypeDescr::maybeForwardedFieldDescr(size_t index) const
 {
-    NativeObject &fieldDescrs = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
+    ArrayObject &fieldDescrs = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
     JSObject &descr =
         *MaybeForwarded(&fieldDescrs.getDenseElement(index).toObject());
     return descr.as<TypeDescr>();
 }
 
 /******************************************************************************
  * Creating the TypedObject "module"
@@ -1223,44 +1223,44 @@ DefineSimpleTypeDescr(JSContext *cx,
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
 template<typename T>
 static JSObject *
 DefineMetaTypeDescr(JSContext *cx,
+                    const char *name,
                     Handle<GlobalObject*> global,
-                    HandleNativeObject module,
+                    Handle<TypedObjectModuleObject*> module,
                     TypedObjectModuleObject::Slot protoSlot)
 {
-    RootedAtom className(cx, Atomize(cx, T::class_.name,
-                                     strlen(T::class_.name)));
+    RootedAtom className(cx, Atomize(cx, name, strlen(name)));
     if (!className)
         return nullptr;
 
     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
     if (!funcProto)
         return nullptr;
 
     // Create ctor.prototype, which inherits from Function.__proto__
 
-    RootedObject proto(cx, NewObjectWithProto<JSObject>(cx, funcProto, global,
-                                                        SingletonObject));
+    RootedObject proto(cx, NewObjectWithProto<PlainObject>(cx, funcProto, global,
+                                                           SingletonObject));
     if (!proto)
         return nullptr;
 
     // Create ctor.prototype.prototype, which inherits from Object.__proto__
 
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
         return nullptr;
     RootedObject protoProto(cx);
-    protoProto = NewObjectWithProto<JSObject>(cx, objProto,
-                                              global, SingletonObject);
+    protoProto = NewObjectWithProto<PlainObject>(cx, objProto,
+                                                 global, SingletonObject);
     if (!protoProto)
         return nullptr;
 
     RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
     if (!JSObject::defineProperty(cx, proto, cx->names().prototype,
                                   protoProtoValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
@@ -1324,32 +1324,32 @@ GlobalObject::initTypedObjectModule(JSCo
         return false;
     JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE)
 #undef BINARYDATA_REFERENCE_DEFINE
 
     // ArrayType.
 
     RootedObject arrayType(cx);
     arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
-        cx, global, module, TypedObjectModuleObject::ArrayTypePrototype);
+        cx, "ArrayType", global, module, TypedObjectModuleObject::ArrayTypePrototype);
     if (!arrayType)
         return false;
 
     RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
     if (!JSObject::defineProperty(cx, module, cx->names().ArrayType,
                                   arrayTypeValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return false;
 
     // StructType.
 
     RootedObject structType(cx);
     structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
-        cx, global, module, TypedObjectModuleObject::StructTypePrototype);
+        cx, "StructType", global, module, TypedObjectModuleObject::StructTypePrototype);
     if (!structType)
         return false;
 
     RootedValue structTypeValue(cx, ObjectValue(*structType));
     if (!JSObject::defineProperty(cx, module, cx->names().StructType,
                                   structTypeValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -158,25 +158,16 @@ class TypedProto : public NativeObject
     static int32_t offsetOfTypeDescr() {
         return getFixedSlotOffset(JS_TYPROTO_SLOT_DESCR);
     }
 };
 
 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.
-    static const Class class_;
-
-  public:
     TypedProto &typedProto() const {
         return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
     }
 
     JSAtom &stringRepr() const {
         return getReservedSlot(JS_DESCR_SLOT_STRING_REPR).toString()->asAtom();
     }
 
@@ -493,22 +484,22 @@ class StructTypeDescr : public ComplexTy
     TypeDescr &fieldDescr(size_t index) const;
     TypeDescr &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>();
+    ArrayObject &fieldInfoObject(size_t slot) const {
+        return getReservedSlot(slot).toObject().as<ArrayObject>();
     }
 
-    NativeObject &maybeForwardedFieldInfoObject(size_t slot) const {
-        return MaybeForwarded(&getReservedSlot(slot).toObject())->as<NativeObject>();
+    ArrayObject &maybeForwardedFieldInfoObject(size_t slot) const {
+        return MaybeForwarded(&getReservedSlot(slot).toObject())->as<ArrayObject>();
     }
 };
 
 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.
--- a/js/src/builtin/WeakSetObject.cpp
+++ b/js/src/builtin/WeakSetObject.cpp
@@ -46,17 +46,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!
-    RootedNativeObject proto(cx, global->createBlankPrototype(cx, &class_));
+    Rooted<WeakSetObject*> proto(cx, global->createBlankPrototype<WeakSetObject>(cx));
     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) ||
@@ -65,26 +65,26 @@ WeakSetObject::initClass(JSContext *cx, 
         return nullptr;
     }
     return proto;
 }
 
 WeakSetObject*
 WeakSetObject::create(JSContext *cx)
 {
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &class_));
+    Rooted<WeakSetObject *> obj(cx, NewBuiltinClassInstance<WeakSetObject>(cx));
     if (!obj)
         return nullptr;
 
     RootedObject map(cx, JS::NewWeakMapObject(cx));
     if (!map)
         return nullptr;
 
     obj->setReservedSlot(WEAKSET_MAP_SLOT, ObjectValue(*map));
-    return &obj->as<WeakSetObject>();
+    return obj;
 }
 
 bool
 WeakSetObject::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx));
     if (!obj)
         return false;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2260,19 +2260,19 @@ EmitNewInit(ExclusiveContext *cx, Byteco
     return true;
 }
 
 static bool
 IteratorResultShape(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned *shape)
 {
     MOZ_ASSERT(bce->script->compileAndGo());
 
-    RootedNativeObject obj(cx);
+    RootedPlainObject obj(cx);
     gc::AllocKind kind = GuessObjectGCKind(2);
-    obj = NewNativeBuiltinClassInstance(cx, &JSObject::class_, kind);
+    obj = NewBuiltinClassInstance<PlainObject>(cx, 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;
@@ -4242,18 +4242,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);
-        RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_,
-                                                                 kind, MaybeSingletonObject));
+        RootedPlainObject obj(cx,
+            NewBuiltinClassInstance<PlainObject>(cx, 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;
 
@@ -6489,20 +6489,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.
      */
-    RootedNativeObject obj(cx);
+    RootedPlainObject obj(cx);
     if (bce->script->compileAndGo()) {
         gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
-        obj = NewNativeBuiltinClassInstance(cx, &JSObject::class_, kind, TenuredObject);
+        obj = NewBuiltinClassInstance<PlainObject>(cx, 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;
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -171,16 +171,17 @@ class SharedArrayBufferObject;
 class SharedTypedArrayObject;
 class BaseShape;
 class DebugScopeObject;
 class GlobalObject;
 class LazyScript;
 class NativeObject;
 class NestedScopeObject;
 class Nursery;
+class PlainObject;
 class PropertyName;
 class SavedFrame;
 class ScopeObject;
 class ScriptSourceObject;
 class Shape;
 class UnownedBaseShape;
 
 namespace types {
@@ -218,16 +219,17 @@ 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<PlainObject>      { 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<AccessorShape>    { 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; };
@@ -816,16 +818,17 @@ typedef HeapPtr<ArrayBufferObject*> Heap
 typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
 typedef HeapPtr<JSAtom*> HeapPtrAtom;
 typedef HeapPtr<JSFlatString*> HeapPtrFlatString;
 typedef HeapPtr<JSFunction*> HeapPtrFunction;
 typedef HeapPtr<JSLinearString*> HeapPtrLinearString;
 typedef HeapPtr<JSObject*> HeapPtrObject;
 typedef HeapPtr<JSScript*> HeapPtrScript;
 typedef HeapPtr<JSString*> HeapPtrString;
+typedef HeapPtr<PlainObject*> HeapPtrPlainObject;
 typedef HeapPtr<PropertyName*> HeapPtrPropertyName;
 typedef HeapPtr<Shape*> HeapPtrShape;
 typedef HeapPtr<UnownedBaseShape*> HeapPtrUnownedBaseShape;
 typedef HeapPtr<jit::JitCode*> HeapPtrJitCode;
 typedef HeapPtr<types::TypeObject*> HeapPtrTypeObject;
 typedef HeapPtr<types::TypeNewScript*> HeapPtrTypeNewScript;
 
 typedef PreBarriered<Value> PreBarrieredValue;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -651,16 +651,17 @@ 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, PlainObject)
 DeclMarkerImpl(Object, SavedFrame)
 DeclMarkerImpl(Object, ScopeObject)
 DeclMarkerImpl(Object, SharedArrayBufferObject)
 DeclMarkerImpl(Object, SharedTypedArrayObject)
 DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(LazyScript, LazyScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -112,16 +112,17 @@ DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, ArrayBufferObject)
 DeclMarker(Object, ArrayBufferObjectMaybeShared)
 DeclMarker(Object, ArrayBufferViewObject)
 DeclMarker(Object, DebugScopeObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
 DeclMarker(Object, NestedScopeObject)
+DeclMarker(Object, PlainObject)
 DeclMarker(Object, SavedFrame)
 DeclMarker(Object, ScopeObject)
 DeclMarker(Object, SharedArrayBufferObject)
 DeclMarker(Object, SharedTypedArrayObject)
 DeclMarker(Script, JSScript)
 DeclMarker(LazyScript, LazyScript)
 DeclMarker(Shape, Shape)
 DeclMarker(String, JSAtom)
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -12,40 +12,43 @@
 class JSAtom;
 class JSLinearString;
 
 namespace js {
 
 class PropertyName;
 class NativeObject;
 class ArrayObject;
+class PlainObject;
 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<PlainObject*>       HandlePlainObject;
 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<PlainObject*>       RootedPlainObject;
 typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/function-names.js
@@ -0,0 +1,8 @@
+
+if (typeof TypedObject === "undefined")
+    quit();
+
+// Make sure some builtin TypedObject functions are given sensible names.
+assertEq(TypedObject.ArrayType.name, "ArrayType");
+assertEq(TypedObject.StructType.name, "StructType");
+assertEq(TypedObject.storage.name, "storage");
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1679,17 +1679,17 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
     ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     frame.push(R0);
     return true;
 }
 
-typedef JSObject *(*NewArrayCopyOnWriteFn)(JSContext *, HandleNativeObject, gc::InitialHeap);
+typedef JSObject *(*NewArrayCopyOnWriteFn)(JSContext *, HandleArrayObject, 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);
@@ -1737,18 +1737,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;
     }
 
-    RootedNativeObject baseObject(cx, script->getObject(pc));
-    RootedNativeObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject));
+    RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>());
+    RootedPlainObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject));
     if (!templateObject)
         return false;
 
     if (type) {
         templateObject->setType(type);
     } else {
         if (!JSObject::setSingletonType(cx, templateObject))
             return false;
@@ -1804,18 +1804,18 @@ BaselineCompiler::emit_JSOP_NEWINIT()
         templateObject->setType(type);
 
         ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject);
         if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
             return false;
     } else {
         MOZ_ASSERT(key == JSProto_Object);
 
-        RootedNativeObject templateObject(cx);
-        templateObject = NewNativeBuiltinClassInstance(cx, &JSObject::class_, TenuredObject);
+        RootedPlainObject templateObject(cx,
+            NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
         if (!templateObject)
             return false;
 
         if (type) {
             templateObject->setType(type);
         } else {
             if (!JSObject::setSingletonType(cx, templateObject))
                 return false;
@@ -1852,17 +1852,17 @@ BaselineCompiler::emit_JSOP_INITELEM()
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     // Pop the rhs, so that the object is on the top of the stack.
     frame.pop();
     return true;
 }
 
-typedef bool (*MutateProtoFn)(JSContext *cx, HandleObject obj, HandleValue newProto);
+typedef bool (*MutateProtoFn)(JSContext *cx, HandlePlainObject obj, HandleValue newProto);
 static const VMFunction MutateProtoInfo = FunctionInfo<MutateProtoFn>(MutatePrototype);
 
 bool
 BaselineCompiler::emit_JSOP_MUTATEPROTO()
 {
     // Keep values on the stack for the decompiler.
     frame.syncStack(0);
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1782,17 +1782,17 @@ ICNewArray_Fallback::Compiler::generateS
 // NewObject_Fallback
 //
 
 static bool
 DoNewObject(JSContext *cx, ICNewObject_Fallback *stub, MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "NewObject");
 
-    RootedNativeObject templateObject(cx, stub->templateObject());
+    RootedPlainObject templateObject(cx, stub->templateObject());
     JSObject *obj = NewInitObject(cx, templateObject);
     if (!obj)
         return false;
 
     res.setObject(*obj);
     return true;
 }
 
@@ -8243,18 +8243,18 @@ DoSetPropFallback(JSContext *cx, Baselin
         lhs.isObject() &&
         !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldShape, name, id,
                                       rhs, &attached, &isTemporarilyUnoptimizable))
     {
         return false;
     }
 
     if (op == JSOP_INITPROP) {
-        MOZ_ASSERT(obj->is<JSObject>());
-        if (!DefineNativeProperty(cx, obj.as<NativeObject>(), id, rhs,
+        MOZ_ASSERT(obj->is<PlainObject>());
+        if (!DefineNativeProperty(cx, obj.as<PlainObject>(), id, rhs,
                                   nullptr, nullptr, JSPROP_ENUMERATE))
         {
             return false;
         }
     } else if (op == JSOP_SETNAME ||
                op == JSOP_STRICTSETNAME ||
                op == JSOP_SETGNAME ||
                op == JSOP_STRICTSETGNAME)
@@ -9197,17 +9197,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.
-        RootedNativeObject templateObject(cx);
+        RootedPlainObject 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
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1922,46 +1922,46 @@ class ICNewArray_Fallback : public ICFal
         return templateObject_;
     }
 };
 
 class ICNewObject_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
-    HeapPtrNativeObject templateObject_;
-
-    ICNewObject_Fallback(JitCode *stubCode, NativeObject *templateObject)
+    HeapPtrPlainObject templateObject_;
+
+    ICNewObject_Fallback(JitCode *stubCode, PlainObject *templateObject)
       : ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(templateObject)
     {}
 
   public:
     static inline ICNewObject_Fallback *New(ICStubSpace *space, JitCode *code,
-                                            NativeObject *templateObject) {
+                                            PlainObject *templateObject) {
         if (!code)
             return nullptr;
         return space->allocate<ICNewObject_Fallback>(code, templateObject);
     }
 
     class Compiler : public ICStubCompiler {
-        RootedNativeObject templateObject;
+        RootedPlainObject templateObject;
         bool generateStubCode(MacroAssembler &masm);
 
       public:
-        Compiler(JSContext *cx, NativeObject *templateObject)
+        Compiler(JSContext *cx, PlainObject *templateObject)
           : ICStubCompiler(cx, ICStub::NewObject_Fallback),
             templateObject(cx, templateObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             return ICNewObject_Fallback::New(space, getStubCode(), templateObject);
         }
     };
 
-    HeapPtrNativeObject &templateObject() {
+    HeapPtrPlainObject &templateObject() {
         return templateObject_;
     }
 };
 
 // Compare
 //      JSOP_LT
 //      JSOP_GT
 
@@ -6366,29 +6366,29 @@ class ICCall_ScriptedFunCall : public IC
 class ICCall_StringSplit : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
   protected:
     uint32_t pcOffset_;
     HeapPtrString expectedThis_;
     HeapPtrString expectedArg_;
-    HeapPtrNativeObject templateObject_;
+    HeapPtrArrayObject templateObject_;
 
     ICCall_StringSplit(JitCode *stubCode, ICStub *firstMonitorStub, uint32_t pcOffset, HandleString thisString,
-                       HandleString argString, HandleNativeObject templateObject)
+                       HandleString argString, HandleArrayObject templateObject)
       : ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
         pcOffset_(pcOffset), expectedThis_(thisString), expectedArg_(argString),
         templateObject_(templateObject)
     { }
 
   public:
     static inline ICCall_StringSplit *New(ICStubSpace *space, JitCode *code,
                                           ICStub *firstMonitorStub, uint32_t pcOffset, HandleString thisString,
-                                          HandleString argString, HandleNativeObject templateObject)
+                                          HandleString argString, HandleArrayObject templateObject)
     {
         if (!code)
             return nullptr;
         return space->allocate<ICCall_StringSplit>(code, firstMonitorStub, pcOffset, thisString,
                                                    argString, templateObject);
     }
 
     static size_t offsetOfExpectedThis() {
@@ -6406,43 +6406,43 @@ class ICCall_StringSplit : public ICMoni
     HeapPtrString &expectedThis() {
         return expectedThis_;
     }
 
     HeapPtrString &expectedArg() {
         return expectedArg_;
     }
 
-    HeapPtrNativeObject &templateObject() {
+    HeapPtrArrayObject &templateObject() {
         return templateObject_;
     }
 
     class Compiler : public ICCallStubCompiler {
       protected:
         ICStub *firstMonitorStub_;
         uint32_t pcOffset_;
         RootedString expectedThis_;
         RootedString expectedArg_;
-        RootedNativeObject templateObject_;
+        RootedArrayObject templateObject_;
 
         bool generateStubCode(MacroAssembler &masm);
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind);
         }
 
       public:
         Compiler(JSContext *cx, ICStub *firstMonitorStub, uint32_t pcOffset, HandleString thisString,
                  HandleString argString, HandleValue templateObject)
           : ICCallStubCompiler(cx, ICStub::Call_StringSplit),
             firstMonitorStub_(firstMonitorStub),
             pcOffset_(pcOffset),
             expectedThis_(cx, thisString),
             expectedArg_(cx, argString),
-            templateObject_(cx, &templateObject.toObject().as<NativeObject>())
+            templateObject_(cx, &templateObject.toObject().as<ArrayObject>())
         { }
 
         ICStub *getStub(ICStubSpace *space) {
             return ICCall_StringSplit::New(space, getStubCode(), firstMonitorStub_,
                                            pcOffset_, expectedThis_, expectedArg_,
                                            templateObject_);
         }
    };
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4192,20 +4192,20 @@ class OutOfLineNewObject : public OutOfL
         codegen->visitOutOfLineNewObject(this);
     }
 
     LNewObject *lir() const {
         return lir_;
     }
 };
 
-typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleNativeObject);
+typedef JSObject *(*NewInitObjectFn)(JSContext *, HandlePlainObject);
 static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
 
-typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandleObject);
+typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandlePlainObject);
 static const VMFunction NewInitObjectWithClassPrototypeInfo =
     FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
 
 void
 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
 {
     MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
 
@@ -4316,17 +4316,17 @@ ShouldInitFixedSlots(LInstruction *lir, 
 }
 
 void
 CodeGenerator::visitNewObject(LNewObject *lir)
 {
     MOZ_ASSERT(gen->info().executionMode() == SequentialExecution);
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
-    NativeObject *templateObject = lir->mir()->templateObject();
+    PlainObject *templateObject = lir->mir()->templateObject();
 
     if (lir->mir()->shouldUseVM()) {
         visitNewObjectVMCall(lir);
         return;
     }
 
     OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
     addOutOfLineCode(ool, lir->mir());
@@ -4370,17 +4370,17 @@ typedef js::DeclEnvObject *(*NewDeclEnvO
 static const VMFunction NewDeclEnvObjectInfo =
     FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
 
 void
 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
-    NativeObject *templateObj = lir->mir()->templateObj();
+    DeclEnvObject *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));
 
@@ -4396,17 +4396,17 @@ static const VMFunction NewCallObjectInf
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 void
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
-    NativeObject *templateObj = lir->mir()->templateObject();
+    CallObject *templateObj = lir->mir()->templateObject();
 
     JSScript *script = lir->mir()->block()->info().script();
     uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin();
     OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(templateObj->lastProperty()),
                                                ImmGCPtr(templateObj->type()),
                                                Imm32(lexicalBegin)),
                                    StoreRegisterTo(objReg));
@@ -4447,17 +4447,17 @@ CodeGenerator::visitNewSingletonCallObje
 
 void
 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
-    NativeObject *templateObj = lir->mir()->templateObj();
+    CallObject *templateObj = lir->mir()->templateObj();
 
     emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
 }
 
 typedef ArrayObject *(*ExtendArrayParFn)(ForkJoinContext*, ArrayObject*, uint32_t);
 static const VMFunction ExtendArrayParInfo =
     FunctionInfo<ExtendArrayParFn>(ExtendArrayPar);
 
@@ -4619,17 +4619,17 @@ CodeGenerator::visitInitElemGetterSetter
     pushArg(value);
     pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex));
     pushArg(obj);
     pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
 
     callVM(InitElemGetterSetterInfo, lir);
 }
 
-typedef bool(*MutatePrototypeFn)(JSContext *cx, HandleObject obj, HandleValue value);
+typedef bool(*MutatePrototypeFn)(JSContext *cx, HandlePlainObject obj, HandleValue value);
 static const VMFunction MutatePrototypeInfo =
     FunctionInfo<MutatePrototypeFn>(MutatePrototype);
 
 void
 CodeGenerator::visitMutateProto(LMutateProto *lir)
 {
     Register objReg = ToRegister(lir->getObject());
 
@@ -4723,17 +4723,17 @@ CodeGenerator::visitCreateThisWithProto(
 typedef JSObject *(*NewGCObjectFn)(JSContext *cx, gc::AllocKind allocKind,
                                    gc::InitialHeap initialHeap);
 static const VMFunction NewGCObjectInfo =
     FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject);
 
 void
 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
 {
-    NativeObject *templateObject = lir->mir()->templateObject();
+    PlainObject *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));
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2813,17 +2813,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,
-                  HandleNativeObject baseobj,
+                  HandlePlainObject 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()) {
@@ -2947,17 +2947,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, HandleNativeObject baseobj,
+                                        types::TypeObject *type, HandlePlainObject baseobj,
                                         Vector<types::TypeNewScript::Initializer> *initializerList)
 {
     MOZ_ASSERT(cx->zone()->types.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
@@ -165,17 +165,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, HandleNativeObject baseobj,
+                                   types::TypeObject *type, HandlePlainObject baseobj,
                                    Vector<types::TypeNewScript::Initializer> *initializerList);
 
 bool
 AnalyzeArgumentsUsage(JSContext *cx, JSScript *script);
 
 bool
 DeadIfUnused(const MDefinition *def);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5296,17 +5296,17 @@ MDefinition *
 IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
 {
     // Get the singleton prototype (if exists)
     JSObject *proto = getSingletonPrototype(target);
     if (!proto)
         return nullptr;
 
     JSObject *templateObject = inspector->getTemplateObject(pc);
-    if (!templateObject || !templateObject->is<JSObject>())
+    if (!templateObject || !templateObject->is<PlainObject>())
         return nullptr;
     if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
         return nullptr;
 
     types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript());
     if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject)))
         return nullptr;
 
@@ -6032,17 +6032,17 @@ IonBuilder::jsop_newobject()
             MUnknownValue *unknown = MUnknownValue::New(alloc());
             current->add(unknown);
             current->push(unknown);
             return true;
         }
         return abort("No template object for NEWOBJECT");
     }
 
-    MOZ_ASSERT(templateObject->is<JSObject>());
+    MOZ_ASSERT(templateObject->is<PlainObject>());
     MConstant *templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     current->add(templateConst);
     MNewObject *ins = MNewObject::New(alloc(), constraints(), templateConst,
                                       templateObject->hasSingletonType()
                                       ? gc::TenuredHeap
                                       : templateObject->type()->initialHeap(constraints()),
                                       /* templateObjectIsClassPrototype = */ false);
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3369,34 +3369,34 @@ MBeta::printOpcode(FILE *fp) const
     } else {
         fprintf(fp, " ???");
     }
 }
 
 bool
 MNewObject::shouldUseVM() const
 {
-    NativeObject *obj = templateObject();
+    PlainObject *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();
-    NativeObject *templateObject = nullptr;
+    PlainObject *templateObject = nullptr;
     if (obj->isNewObject())
         templateObject = obj->toNewObject()->templateObject();
     else
         templateObject = obj->toCreateThisWithTemplate()->templateObject();
     numSlots_ = templateObject->slotSpan();
     numFixedSlots_ = templateObject->numFixedSlots();
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2704,17 +2704,17 @@ class MNewObject : public MUnaryInstruct
     bool templateObjectIsClassPrototype_;
 
     MNewObject(types::CompilerConstraintList *constraints, MConstant *templateConst,
                gc::InitialHeap initialHeap, bool templateObjectIsClassPrototype)
       : MUnaryInstruction(templateConst),
         initialHeap_(initialHeap),
         templateObjectIsClassPrototype_(templateObjectIsClassPrototype)
     {
-        JSObject *obj = templateObject();
+        PlainObject *obj = templateObject();
         MOZ_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM());
         setResultType(MIRType_Object);
         if (!obj->hasSingletonType())
             setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
 
         // The constant is kept separated in a MConstant, this way we can safely
         // mark it during GC if we recover the object allocation.  Otherwise, by
         // making it emittedAtUses, we do not produce register allocations for
@@ -2737,18 +2737,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_;
     }
 
-    NativeObject *templateObject() const {
-        return &getOperand(0)->toConstant()->value().toObject().as<NativeObject>();
+    PlainObject *templateObject() const {
+        return &getOperand(0)->toConstant()->value().toObject().as<PlainObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const;
     bool canRecoverOnBailout() const {
@@ -4076,18 +4076,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.
-    NativeObject *templateObject() const {
-        return &getOperand(0)->toConstant()->value().toObject().as<NativeObject>();
+    PlainObject *templateObject() const {
+        return &getOperand(0)->toConstant()->value().toObject().as<PlainObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     // Although creation of |this| modifies global state, it is safely repeatable.
     AliasSet getAliasSet() const {
@@ -11415,98 +11415,98 @@ class MPostWriteBarrier : public MBinary
     }
 #endif
 
     ALLOW_CLONE(MPostWriteBarrier)
 };
 
 class MNewDeclEnvObject : public MNullaryInstruction
 {
-    AlwaysTenuredNativeObject templateObj_;
-
-    explicit MNewDeclEnvObject(NativeObject *templateObj)
+    AlwaysTenured<DeclEnvObject*> templateObj_;
+
+    explicit MNewDeclEnvObject(DeclEnvObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewDeclEnvObject);
 
-    static MNewDeclEnvObject *New(TempAllocator &alloc, NativeObject *templateObj) {
+    static MNewDeclEnvObject *New(TempAllocator &alloc, DeclEnvObject *templateObj) {
         return new(alloc) MNewDeclEnvObject(templateObj);
     }
 
-    NativeObject *templateObj() {
+    DeclEnvObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MNewCallObjectBase : public MNullaryInstruction
 {
-    AlwaysTenuredNativeObject templateObj_;
+    AlwaysTenured<CallObject*> templateObj_;
 
   protected:
-    explicit MNewCallObjectBase(NativeObject *templateObj)
+    explicit MNewCallObjectBase(CallObject *templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
-    NativeObject *templateObject() {
+    CallObject *templateObject() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MNewCallObject : public MNewCallObjectBase
 {
   public:
     INSTRUCTION_HEADER(NewCallObject)
 
-    explicit MNewCallObject(NativeObject *templateObj)
+    explicit MNewCallObject(CallObject *templateObj)
       : MNewCallObjectBase(templateObj)
     {}
 
     static MNewCallObject *
-    New(TempAllocator &alloc, NativeObject *templateObj)
+    New(TempAllocator &alloc, CallObject *templateObj)
     {
         return new(alloc) MNewCallObject(templateObj);
     }
 };
 
 class MNewRunOnceCallObject : public MNewCallObjectBase
 {
   public:
     INSTRUCTION_HEADER(NewRunOnceCallObject)
 
-    explicit MNewRunOnceCallObject(NativeObject *templateObj)
+    explicit MNewRunOnceCallObject(CallObject *templateObj)
       : MNewCallObjectBase(templateObj)
     {}
 
     static MNewRunOnceCallObject *
-    New(TempAllocator &alloc, NativeObject *templateObj)
+    New(TempAllocator &alloc, CallObject *templateObj)
     {
         return new(alloc) MNewRunOnceCallObject(templateObj);
     }
 };
 
 class MNewCallObjectPar : public MUnaryInstruction
 {
-    AlwaysTenuredNativeObject templateObj_;
-
-    MNewCallObjectPar(MDefinition *cx, NativeObject *templateObj)
+    AlwaysTenured<CallObject*> templateObj_;
+
+    MNewCallObjectPar(MDefinition *cx, CallObject *templateObj)
         : MUnaryInstruction(cx),
           templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObjectPar);
@@ -11514,17 +11514,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);
     }
 
-    NativeObject *templateObj() const {
+    CallObject *templateObj() const {
         return templateObj_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1100,17 +1100,17 @@ MNewObject::writeRecoverData(CompactBuff
 RNewObject::RNewObject(CompactBufferReader &reader)
 {
     templateObjectIsClassPrototype_ = reader.readByte();
 }
 
 bool
 RNewObject::recover(JSContext *cx, SnapshotIterator &iter) const
 {
-    RootedNativeObject templateObject(cx, &iter.read().toObject().as<NativeObject>());
+    RootedPlainObject templateObject(cx, &iter.read().toObject().as<PlainObject>());
     RootedValue result(cx);
     JSObject *resultObject = nullptr;
 
     // See CodeGenerator::visitNewObjectVMCall
     if (templateObjectIsClassPrototype_)
         resultObject = NewInitObjectWithClassPrototype(cx, templateObject);
     else
         resultObject = NewInitObject(cx, templateObject);
@@ -1198,17 +1198,17 @@ MCreateThisWithTemplate::writeRecoverDat
 RCreateThisWithTemplate::RCreateThisWithTemplate(CompactBufferReader &reader)
 {
     tenuredHeap_ = reader.readByte();
 }
 
 bool
 RCreateThisWithTemplate::recover(JSContext *cx, SnapshotIterator &iter) const
 {
-    RootedNativeObject templateObject(cx, &iter.read().toObject().as<NativeObject>());
+    RootedPlainObject templateObject(cx, &iter.read().toObject().as<PlainObject>());
 
     // See CodeGenerator::visitCreateThisWithTemplate
     gc::AllocKind allocKind = templateObject->asTenured().getAllocKind();
     gc::InitialHeap initialHeap = tenuredHeap_ ? gc::TenuredHeap : gc::DefaultHeap;
     JSObject *resultObject = NativeObject::copy(cx, allocKind, initialHeap, templateObject);
     if (!resultObject)
         return false;
 
@@ -1230,17 +1230,17 @@ MObjectState::writeRecoverData(CompactBu
 RObjectState::RObjectState(CompactBufferReader &reader)
 {
     numSlots_ = reader.readUnsigned();
 }
 
 bool
 RObjectState::recover(JSContext *cx, SnapshotIterator &iter) const
 {
-    RootedNativeObject object(cx, &iter.read().toObject().as<NativeObject>());
+    RootedPlainObject object(cx, &iter.read().toObject().as<PlainObject>());
     MOZ_ASSERT(object->slotSpan() == numSlots());
 
     RootedValue val(cx);
     for (size_t i = 0; i < numSlots(); i++) {
         val = iter.read();
         object->setSlot(i, val);
     }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -189,19 +189,18 @@ SetConst(JSContext *cx, HandlePropertyNa
     RootedObject obj(cx, scopeChain);
     while (!obj->isQualifiedVarObj())
         obj = obj->enclosingScope();
 
     return SetConstOperation(cx, obj, name, rval);
 }
 
 bool
-MutatePrototype(JSContext *cx, HandleObject obj, HandleValue value)
+MutatePrototype(JSContext *cx, HandlePlainObject obj, HandleValue value)
 {
-    MOZ_ASSERT(obj->is<JSObject>(), "must only be used with object literals");
     if (!value.isObjectOrNull())
         return true;
 
     RootedObject newProto(cx, value.toObjectOrNull());
 
     bool succeeded;
     if (!JSObject::setProto(cx, obj, newProto, &succeeded))
         return false;
@@ -278,46 +277,45 @@ 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, HandleNativeObject templateObject)
+NewInitObject(JSContext *cx, HandlePlainObject 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;
 
     if (!templateObject->hasSingletonType())
         obj->setType(templateObject->type());
 
     return obj;
 }
 
 JSObject *
-NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject)
+NewInitObjectWithClassPrototype(JSContext *cx, HandlePlainObject templateObject)
 {
     MOZ_ASSERT(!templateObject->hasSingletonType());
     MOZ_ASSERT(!templateObject->hasLazyType());
 
     NewObjectKind newKind = templateObject->type()->shouldPreTenure()
                             ? TenuredObject
                             : GenericObject;
-    JSObject *obj = NewObjectWithGivenProto(cx,
-                                            templateObject->getClass(),
-                                            templateObject->getProto(),
-                                            cx->global(),
-                                            newKind);
+    PlainObject *obj = NewObjectWithGivenProto<PlainObject>(cx,
+                                                            templateObject->getProto(),
+                                                            cx->global(),
+                                                            newKind);
     if (!obj)
         return nullptr;
 
     obj->setType(templateObject->type());
 
     return obj;
 }
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -309,16 +309,17 @@ template <> struct TypeToDataType<JSStri
 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<InlineTypedObject *> > { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<Handle<ArrayObject *> > { static const DataType result = Type_Handle; };
+template <> struct TypeToDataType<Handle<PlainObject *> > { 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 {
@@ -344,16 +345,19 @@ template <> struct TypeToArgProperties<H
     static const uint32_t result = TypeToArgProperties<NativeObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<InlineTypedObject *> > {
     static const uint32_t result = TypeToArgProperties<InlineTypedObject *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<Handle<ArrayObject *> > {
     static const uint32_t result = TypeToArgProperties<ArrayObject *>::result | VMFunction::ByRef;
 };
+template <> struct TypeToArgProperties<Handle<PlainObject *> > {
+    static const uint32_t result = TypeToArgProperties<PlainObject *>::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;
@@ -415,16 +419,19 @@ template <> struct TypeToRootType<Handle
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<InlineTypedObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
 template <> struct TypeToRootType<Handle<ArrayObject *> > {
     static const uint32_t result = VMFunction::RootObject;
 };
+template <> struct TypeToRootType<Handle<PlainObject *> > {
+    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.
@@ -660,37 +667,35 @@ bool InvokeFunction(JSContext *cx, Handl
 JSObject *NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap);
 
 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 MutatePrototype(JSContext *cx, HandlePlainObject obj, 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);
 bool LessThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
 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, HandleNativeObject templateObject);
-JSObject *NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject);
+JSObject *NewInitObject(JSContext *cx, HandlePlainObject templateObject);
+JSObject *NewInitObjectWithClassPrototype(JSContext *cx, HandlePlainObject templateObject);
 
 bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
 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);
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -9,17 +9,17 @@
 
 static bool
 constructHook(JSContext *cx, unsigned argc, jsval *vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
 
     // Check that arguments were passed properly from JS_New.
 
-    JS::RootedObject obj(cx, JS_NewObject(cx, js::Jsvalify(&JSObject::class_), JS::NullPtr(), JS::NullPtr()));
+    JS::RootedObject obj(cx, JS_NewObject(cx, js::Jsvalify(&js::PlainObject::class_), JS::NullPtr(), JS::NullPtr()));
     if (!obj) {
         JS_ReportError(cx, "test failed, could not construct object");
         return false;
     }
     if (strcmp(JS_GetClass(obj)->name, "Object") != 0) {
         JS_ReportError(cx, "test failed, wrong class for 'this'");
         return false;
     }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2467,17 +2467,17 @@ JS_NewObject(JSContext *cx, const JSClas
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent);
 
     const Class *clasp = Valueify(jsclasp);
     if (!clasp)
-        clasp = &JSObject::class_;    /* default class is Object */
+        clasp = &PlainObject::class_;    /* default class is Object */
 
     MOZ_ASSERT(clasp != &JSFunction::class_);
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     JSObject *obj = NewObjectWithClassProto(cx, clasp, proto, parent);
     MOZ_ASSERT_IF(obj, obj->getParent());
     return obj;
 }
@@ -2487,17 +2487,17 @@ JS_NewObjectWithGivenProto(JSContext *cx
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto, parent);
 
     const Class *clasp = Valueify(jsclasp);
     if (!clasp)
-        clasp = &JSObject::class_;    /* default class is Object */
+        clasp = &PlainObject::class_;    /* default class is Object */
 
     MOZ_ASSERT(clasp != &JSFunction::class_);
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     JSObject *obj = NewObjectWithGivenProto(cx, clasp, proto, parent);
     if (obj)
         MarkTypeObjectUnknownProperties(cx, obj->type());
     return obj;
@@ -3193,17 +3193,17 @@ JS_DefineObject(JSContext *cx, HandleObj
                 HandleObject proto, unsigned attrs)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
 
     const Class *clasp = Valueify(jsclasp);
     if (!clasp)
-        clasp = &JSObject::class_;    /* default class is Object */
+        clasp = &PlainObject::class_;    /* default class is Object */
 
     RootedObject nobj(cx, NewObjectWithClassProto(cx, clasp, proto, obj));
     if (!nobj)
         return nullptr;
 
     RootedValue nobjValue(cx, ObjectValue(*nobj));
     if (!DefineProperty(cx, obj, name, nobjValue, NativeOpWrapper(nullptr), NativeOpWrapper(nullptr),
                         attrs, 0)) {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3622,17 +3622,17 @@ js::NewDenseFullyAllocatedArrayWithTempl
         return nullptr;
 
     probes::CreateObject(cx, arr);
 
     return arr;
 }
 
 JSObject *
-js::NewDenseCopyOnWriteArray(JSContext *cx, HandleNativeObject templateObject, gc::InitialHeap heap)
+js::NewDenseCopyOnWriteArray(JSContext *cx, HandleArrayObject templateObject, gc::InitialHeap heap)
 {
     RootedShape shape(cx, templateObject->lastProperty());
 
     MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
 
     JSObject *metadata = nullptr;
     if (!NewObjectMetadata(cx, &metadata))
         return nullptr;
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -94,17 +94,17 @@ NewDenseCopiedArray(JSContext *cx, uint3
                     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, HandleNativeObject templateObject, gc::InitialHeap heap);
+NewDenseCopyOnWriteArray(JSContext *cx, HandleArrayObject 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,
--- 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>());
 
-    RootedNativeObject booleanProto(cx, global->createBlankPrototype(cx, &BooleanObject::class_));
+    Rooted<BooleanObject*> booleanProto(cx, global->createBlankPrototype<BooleanObject>(cx));
     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
@@ -518,17 +518,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->as<NativeObject>().lookup(cx, id));
+    DebugOnly<Shape *> shape = static_cast<Shape *>(callee->as<JSFunction>().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/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3006,18 +3006,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.as<NativeObject>(), toUTCStringId, &toUTCStringFun) &&
-           baseops::DefineGeneric(cx, proto.as<NativeObject>(), toGMTStringId, toUTCStringFun,
+    return baseops::GetProperty(cx, proto.as<DateObject>(), toUTCStringId, &toUTCStringFun) &&
+           baseops::DefineGeneric(cx, proto.as<DateObject>(), toGMTStringId, toUTCStringFun,
                                   JS_PropertyStub, JS_StrictPropertyStub, 0);
 }
 
 const Class DateObject::class_ = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     JS_PropertyStub,         /* addProperty */
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -405,19 +405,19 @@ ResolveInterpretedFunctionPrototype(JSCo
     Rooted<GlobalObject*> global(cx, &obj->global());
     JSObject *objProto;
     if (isStarGenerator)
         objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
     else
         objProto = obj->global().getOrCreateObjectPrototype(cx);
     if (!objProto)
         return nullptr;
-    const Class *clasp = &JSObject::class_;
 
-    RootedObject proto(cx, NewObjectWithGivenProto(cx, clasp, objProto, nullptr, SingletonObject));
+    RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, objProto,
+                                                                     nullptr, SingletonObject));
     if (!proto)
         return nullptr;
 
     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
     // initially non-configurable, non-enumerable, and writable.
     RootedValue protoVal(cx, ObjectValue(*proto));
     if (!JSObject::defineProperty(cx, obj, cx->names().prototype,
                                   protoVal, JS_PropertyStub, JS_StrictPropertyStub,
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2764,36 +2764,33 @@ UpdateObjectTableEntryTypes(ExclusiveCon
                 entry.types[i] = Type::DoubleType();
             }
             AddTypePropertyId(cx, entry.object, IdToTypeId(properties[i].id), ntype);
         }
     }
 }
 
 void
-TypeCompartment::fixObjectType(ExclusiveContext *cx, NativeObject *obj)
+TypeCompartment::fixObjectType(ExclusiveContext *cx, PlainObject *obj)
 {
     AutoEnterAnalysis enter(cx);
 
     if (!objectTypeTable) {
         objectTypeTable = cx->new_<ObjectTypeTable>();
         if (!objectTypeTable || !objectTypeTable->init()) {
             js_delete(objectTypeTable);
             objectTypeTable = nullptr;
             return;
         }
     }
 
     /*
      * Use the same type object for all singleton/JSON objects with the same
      * base shape, i.e. the same fields written in the same order.
-     */
-    MOZ_ASSERT(obj->is<JSObject>());
-
-    /*
+     *
      * Exclude some objects we can't readily associate common types for based on their
      * shape. Objects with metadata are excluded so that the metadata does not need to
      * be included in the table lookup (the metadata object might be in the nursery).
      */
     if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata())
         return;
 
     Vector<IdValuePair> properties(cx);
@@ -2817,17 +2814,17 @@ TypeCompartment::fixObjectType(Exclusive
 
         UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length());
         obj->setType(p->value().object);
         return;
     }
 
     /* Make a new type to use for the object and similar future ones. */
     Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
-    TypeObject *objType = newTypeObject(cx, &JSObject::class_, objProto);
+    TypeObject *objType = newTypeObject(cx, &PlainObject::class_, objProto);
     if (!objType || !objType->addDefiniteProperties(cx, obj->lastProperty()))
         return;
 
     if (obj->isIndexed())
         objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
 
     ScopedJSFreePtr<jsid> ids(objType->zone()->pod_calloc<jsid>(properties.length()));
     if (!ids)
@@ -2890,25 +2887,25 @@ TypeCompartment::newTypedObject(JSContex
      * in the object type table --- fixObjectType populates the table with
      * properties read off its input object, which cannot be duplicates, and
      * ignores objects with dense indexes.
      */
     if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT)
         return nullptr;
 
     gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
-    size_t nfixed = gc::GetGCKindSlots(allocKind, &JSObject::class_);
+    size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_);
 
     ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
     ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
 
     if (!p)
         return nullptr;
 
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_, allocKind));
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
     if (!obj) {
         cx->clearPendingException();
         return nullptr;
     }
     MOZ_ASSERT(obj->getProto() == p->value().object->proto().toObject());
 
     RootedShape shape(cx, p->value().shape);
     if (!NativeObject::setLastProperty(cx, obj, shape)) {
@@ -3771,29 +3768,29 @@ TypeNewScript::make(JSContext *cx, TypeO
         return;
 
     ScopedJSDeletePtr<TypeNewScript> newScript(cx->new_<TypeNewScript>());
     if (!newScript)
         return;
 
     newScript->fun = fun;
 
-    NativeObject **preliminaryObjects =
-        type->zone()->pod_calloc<NativeObject *>(PRELIMINARY_OBJECT_COUNT);
+    PlainObject **preliminaryObjects =
+        type->zone()->pod_calloc<PlainObject *>(PRELIMINARY_OBJECT_COUNT);
     if (!preliminaryObjects)
         return;
 
     newScript->preliminaryObjects = preliminaryObjects;
     type->setNewScript(newScript.forget());
 
     gc::TraceTypeNewScript(type);
 }
 
 void
-TypeNewScript::registerNewObject(NativeObject *res)
+TypeNewScript::registerNewObject(PlainObject *res)
 {
     MOZ_ASSERT(!analyzed());
 
     // The preliminary object pointers are weak, and won't be swept properly
     // during nursery collections, so the preliminary objects need to be
     // initially tenured.
     MOZ_ASSERT(!IsInsideNursery(res));
 
@@ -3808,17 +3805,17 @@ TypeNewScript::registerNewObject(NativeO
             return;
         }
     }
 
     MOZ_CRASH("There should be room for registering the new object");
 }
 
 void
-TypeNewScript::unregisterNewObject(NativeObject *res)
+TypeNewScript::unregisterNewObject(PlainObject *res)
 {
     MOZ_ASSERT(!analyzed());
 
     for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
         if (preliminaryObjects[i] == res) {
             preliminaryObjects[i] = nullptr;
             return;
         }
@@ -3866,17 +3863,17 @@ CommonPrefix(Shape *first, Shape *second
         first = first->previous();
         second = second->previous();
     }
 
     return first;
 }
 
 static bool
-ChangeObjectFixedSlotCount(JSContext *cx, NativeObject *obj, gc::AllocKind allocKind)
+ChangeObjectFixedSlotCount(JSContext *cx, PlainObject *obj, gc::AllocKind allocKind)
 {
     MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty()));
 
     // Make a clone of the object, with the new allocation kind.
     RootedShape oldShape(cx, obj->lastProperty());
     RootedTypeObject type(cx, obj->type());
     JSObject *clone = NewReshapedObject(cx, type, obj->getParent(), allocKind, oldShape);
     if (!clone)
@@ -3940,17 +3937,17 @@ TypeNewScript::maybeAnalyze(JSContext *c
     // Any failures after this point will clear out this TypeNewScript.
     DestroyTypeNewScript destroyNewScript(cx, type);
 
     // Compute the greatest common shape prefix and the largest slot span of
     // the preliminary objects.
     Shape *prefixShape = nullptr;
     size_t maxSlotSpan = 0;
     for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
-        NativeObject *obj = preliminaryObjects[i];
+        PlainObject *obj = preliminaryObjects[i];
         if (!obj)
             continue;
 
         // For now, we require all preliminary objects to have only simple
         // lineages of plain data properties.
         Shape *shape = obj->lastProperty();
         if (shape->inDictionary() || !OnlyHasDataProperties(shape))
             return true;
@@ -3979,40 +3976,40 @@ TypeNewScript::maybeAnalyze(JSContext *c
         // definite property accesses requires both that the property is
         // definitely in a particular slot and that the object has a specific
         // number of fixed slots. So, adjust the shape and slot layout of all
         // the preliminary objects so that their structure matches that of the
         // template object. Also recompute the prefix shape, as it reflects the
         // old number of fixed slots.
         Shape *newPrefixShape = nullptr;
         for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
-            NativeObject *obj = preliminaryObjects[i];
+            PlainObject *obj = preliminaryObjects[i];
             if (!obj)
                 continue;
             if (!ChangeObjectFixedSlotCount(cx, obj, kind))
                 return false;
             if (newPrefixShape) {
                 MOZ_ASSERT(CommonPrefix(obj->lastProperty(), newPrefixShape) == newPrefixShape);
             } else {
                 newPrefixShape = obj->lastProperty();
                 while (newPrefixShape->slotSpan() > prefixShape->slotSpan())
                     newPrefixShape = newPrefixShape->previous();
             }
         }
         prefixShape = newPrefixShape;
     }
 
     RootedTypeObject typeRoot(cx, type);
-    templateObject_ = NewNativeObjectWithType(cx, typeRoot, cx->global(), kind, MaybeSingletonObject);
+    templateObject_ = NewObjectWithType<PlainObject>(cx, typeRoot, cx->global(), kind, MaybeSingletonObject);
     if (!templateObject_)
         return false;
 
     Vector<Initializer> initializerVector(cx);
 
-    RootedNativeObject templateRoot(cx, templateObject());
+    RootedPlainObject templateRoot(cx, templateObject());
     if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, type, templateRoot, &initializerVector))
         return false;
 
     if (!type->newScript())
         return true;
 
     MOZ_ASSERT(OnlyHasDataProperties(templateObject()->lastProperty()));
 
@@ -4136,17 +4133,17 @@ TypeNewScript::rollbackPartiallyInitiali
         if (!thisv.isObject() ||
             thisv.toObject().hasLazyType() ||
             thisv.toObject().type() != type)
         {
             continue;
         }
 
         // Found a matching frame.
-        RootedNativeObject obj(cx, &thisv.toObject().as<NativeObject>());
+        RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
 
         // Whether all identified 'new' properties have been initialized.
         bool finished = false;
 
         // If not finished, number of properties that have been added.
         uint32_t numProperties = 0;
 
         // Whether the current SETPROP is within an inner frame which has
@@ -4214,17 +4211,17 @@ TypeNewScript::trace(JSTracer *trc)
 
 void
 TypeNewScript::sweep()
 {
     // preliminaryObjects only holds weak pointers, so clear any objects that
     // are about to be destroyed.
     if (preliminaryObjects) {
         for (size_t i = 0; i < PRELIMINARY_OBJECT_COUNT; i++) {
-            NativeObject **ptr = &preliminaryObjects[i];
+            PlainObject **ptr = &preliminaryObjects[i];
             if (*ptr && IsObjectAboutToBeFinalized(ptr))
                 *ptr = nullptr;
         }
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // JSObject
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -934,23 +934,24 @@ class TypeNewScript
     HeapPtrFunction fun;
 
     // If fewer than PRELIMINARY_OBJECT_COUNT instances of the type are
     // created, this array holds pointers to each of those objects. When the
     // threshold has been reached, the definite and acquired properties
     // analyses are performed and this array is cleared. The pointers in this
     // array are weak.
     static const uint32_t PRELIMINARY_OBJECT_COUNT = 20;
-    NativeObject **preliminaryObjects;
+    PlainObject **preliminaryObjects;
 
     // After the new script properties analyses have been performed, a template
     // object to use for newly constructed objects. The shape of this object
     // reflects all definite properties the object will have, and the
-    // allocation kind to use.
-    HeapPtrNativeObject templateObject_;
+    // allocation kind to use. Note that this is actually a PlainObject, but is
+    // JSObject here to avoid cyclic include dependencies.
+    HeapPtrPlainObject templateObject_;
 
     // Order in which definite properties become initialized. We need this in
     // case the definite properties are invalidated (such as by adding a setter
     // to an object on the prototype chain) while an object is in the middle of
     // being initialized, so we can walk the stack and fixup any objects which
     // look for in-progress objects which were prematurely set with an incorrect
     // shape. Property assignments in inner frames are preceded by a series of
     // SETPROP_FRAME entries specifying the stack down to the frame containing
@@ -985,17 +986,17 @@ class TypeNewScript
             MOZ_ASSERT(!initializedShape());
             MOZ_ASSERT(!initializedType());
             return false;
         }
         MOZ_ASSERT(templateObject());
         return true;
     }
 
-    NativeObject *templateObject() const {
+    PlainObject *templateObject() const {
         return templateObject_;
     }
 
     Shape *initializedShape() const {
         return initializedShape_;
     }
 
     TypeObject *initializedType() const {
@@ -1004,18 +1005,18 @@ class TypeNewScript
 
     void trace(JSTracer *trc);
     void sweep();
 
 #ifdef JSGC_COMPACTING
     void fixupAfterMovingGC();
 #endif
 
-    void registerNewObject(NativeObject *res);
-    void unregisterNewObject(NativeObject *res);
+    void registerNewObject(PlainObject *res);
+    void unregisterNewObject(PlainObject *res);
     bool maybeAnalyze(JSContext *cx, TypeObject *type, bool *regenerate, bool force = false);
 
     void rollbackPartiallyInitializedObjects(JSContext *cx, TypeObject *type);
 
     static void make(JSContext *cx, TypeObject *type, JSFunction *fun);
 };
 
 /*
@@ -1683,17 +1684,17 @@ struct TypeCompartment
     ArrayTypeTable *arrayTypeTable;
     ObjectTypeTable *objectTypeTable;
 
   private:
     void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type);
 
   public:
     void fixArrayType(ExclusiveContext *cx, ArrayObject *obj);
-    void fixObjectType(ExclusiveContext *cx, NativeObject *obj);
+    void fixObjectType(ExclusiveContext *cx, PlainObject *obj);
     void fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj);
 
     JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
 
     TypeCompartment();
     ~TypeCompartment();
 
     inline JSCompartment *compartment();
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -306,17 +306,17 @@ struct AutoEnterAnalysis
 // Interface functions
 /////////////////////////////////////////////////////////////////////
 
 inline const Class *
 GetClassForProtoKey(JSProtoKey key)
 {
     switch (key) {
       case JSProto_Object:
-        return &JSObject::class_;
+        return &PlainObject::class_;
       case JSProto_Array:
         return &ArrayObject::class_;
 
       case JSProto_Number:
         return &NumberObject::class_;
       case JSProto_Boolean:
         return &BooleanObject::class_;
       case JSProto_String:
@@ -568,17 +568,17 @@ MarkObjectStateChange(ExclusiveContext *
 
 inline void
 FixArrayType(ExclusiveContext *cx, ArrayObject *obj)
 {
     cx->compartment()->types.fixArrayType(cx, obj);
 }
 
 inline void
-FixObjectType(ExclusiveContext *cx, NativeObject *obj)
+FixObjectType(ExclusiveContext *cx, PlainObject *obj)
 {
     cx->compartment()->types.fixObjectType(cx, obj);
 }
 
 /* Interface helpers for JSScript*. */
 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               const js::Value &rval);
 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -479,31 +479,27 @@ NewPropertyIteratorObject(JSContext *cx,
             return nullptr;
 
         const Class *clasp = &PropertyIteratorObject::class_;
         RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr), nullptr, metadata,
                                                           ITERATOR_FINALIZE_KIND));
         if (!shape)
             return nullptr;
 
-        NativeObject *obj =
-            MaybeNativeObject(JSObject::create(cx, ITERATOR_FINALIZE_KIND,
-                                               GetInitialHeap(GenericObject, clasp), shape, type));
+        JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
+                                         GetInitialHeap(GenericObject, clasp), shape, type);
         if (!obj)
             return nullptr;
+        PropertyIteratorObject *res = &obj->as<PropertyIteratorObject>();
 
-        MOZ_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
-        return &obj->as<PropertyIteratorObject>();
+        MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
+        return res;
     }
 
-    JSObject *obj = NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<PropertyIteratorObject>();
+    return NewBuiltinClassInstance<PropertyIteratorObject>(cx);
 }
 
 NativeIterator *
 NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
 {
     size_t plength = props.length();
     NativeIterator *ni = cx->zone()->pod_malloc_with_extra<NativeIterator, void *>(plength + slength);
     if (!ni)
@@ -804,17 +800,17 @@ js::CreateItrResultObject(JSContext *cx,
 {
     // FIXME: We can cache the iterator result object shape somewhere.
     AssertHeapIsIdle(cx);
 
     RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
 
-    RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
+    RootedPlainObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, cx->global()));
     if (!obj)
         return nullptr;
 
     if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
         return nullptr;
 
     RootedValue doneBool(cx, BooleanValue(done));
     if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -67,145 +67,16 @@
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 
-static JSObject *
-CreateObjectConstructor(JSContext *cx, JSProtoKey key)
-{
-    Rooted<GlobalObject*> self(cx, cx->global());
-    if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
-        return nullptr;
-
-    RootedObject functionProto(cx, &self->getPrototype(JSProto_Function).toObject());
-
-    /* Create the Object function now that we have a [[Prototype]] for it. */
-    RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto,
-                                                  self, SingletonObject));
-    if (!ctor)
-        return nullptr;
-    return NewFunction(cx, ctor, obj_construct, 1, JSFunction::NATIVE_CTOR, self,
-                       HandlePropertyName(cx->names().Object));
-}
-
-static JSObject *
-CreateObjectPrototype(JSContext *cx, JSProtoKey key)
-{
-    Rooted<GlobalObject*> self(cx, cx->global());
-
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
-    MOZ_ASSERT(self->isNative());
-
-    /*
-     * Create |Object.prototype| first, mirroring CreateBlankProto but for the
-     * prototype of the created object.
-     */
-    RootedObject objectProto(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr,
-                                                         self, SingletonObject));
-    if (!objectProto)
-        return nullptr;
-
-    /*
-     * The default 'new' type of Object.prototype is required by type inference
-     * to have unknown properties, to simplify handling of e.g. heterogenous
-     * objects in JSON and script literals.
-     */
-    if (!JSObject::setNewTypeUnknown(cx, &JSObject::class_, objectProto))
-        return nullptr;
-
-    return objectProto;
-}
-
-static bool
-FinishObjectClassInit(JSContext *cx, JS::HandleObject ctor, JS::HandleObject proto)
-{
-    Rooted<GlobalObject*> self(cx, cx->global());
-
-    /* ES5 15.1.2.1. */
-    RootedId evalId(cx, NameToId(cx->names().eval));
-    JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
-    if (!evalobj)
-        return false;
-    self->setOriginalEval(evalobj);
-
-    RootedObject intrinsicsHolder(cx);
-    bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(self);
-    if (isSelfHostingGlobal) {
-        intrinsicsHolder = self;
-    } else {
-        intrinsicsHolder = NewObjectWithGivenProto(cx, &JSObject::class_, proto, self,
-                                                   TenuredObject);
-        if (!intrinsicsHolder)
-            return false;
-    }
-    self->setIntrinsicsHolder(intrinsicsHolder);
-    /* Define a property 'global' with the current global as its value. */
-    RootedValue global(cx, ObjectValue(*self));
-    if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global,
-                                  global, JS_PropertyStub, JS_StrictPropertyStub,
-                                  JSPROP_PERMANENT | JSPROP_READONLY))
-    {
-        return false;
-    }
-
-    /*
-     * Define self-hosted functions after setting the intrinsics holder
-     * (which is needed to define self-hosted functions)
-     */
-    if (!isSelfHostingGlobal) {
-        if (!JS_DefineFunctions(cx, ctor, object_static_selfhosted_methods))
-            return false;
-    }
-
-    /*
-     * The global object should have |Object.prototype| as its [[Prototype]].
-     * Eventually we'd like to have standard classes be there from the start,
-     * and thus we would know we were always setting what had previously been a
-     * null [[Prototype]], but right now some code assumes it can set the
-     * [[Prototype]] before standard classes have been initialized.  For now,
-     * only set the [[Prototype]] if it hasn't already been set.
-     */
-    Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
-    if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->getClass(), tagged))
-        return false;
-    return true;
-}
-
-
-const Class JSObject::class_ = {
-    js_Object_str,
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    JS_PropertyStub,         /* addProperty */
-    JS_DeletePropertyStub,   /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    nullptr,                 /* finalize */
-    nullptr,                 /* call */
-    nullptr,                 /* hasInstance */
-    nullptr,                 /* construct */
-    nullptr,                 /* trace */
-    {
-        CreateObjectConstructor,
-        CreateObjectPrototype,
-        object_static_methods,
-        object_methods,
-        object_properties,
-        FinishObjectClassInit
-    }
-};
-
-const Class* const js::ObjectClassPtr = &JSObject::class_;
-
 JS_FRIEND_API(JSObject *)
 JS_ObjectToInnerObject(JSContext *cx, HandleObject obj)
 {
     if (!obj) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INACTIVE);
         return nullptr;
     }
     return GetInnerObject(obj);
@@ -330,17 +201,17 @@ PropDesc::populatePropertyDescriptor(Han
     desc.object().set(obj);
 }
 
 bool
 PropDesc::makeObject(JSContext *cx, MutableHandleObject obj)
 {
     MOZ_ASSERT(!isUndefined());
 
-    obj.set(NewBuiltinClassInstance(cx, &JSObject::class_));
+    obj.set(NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
         return false;
 
     const JSAtomState &names = cx->names();
     RootedValue configurableVal(cx, BooleanValue((attrs & JSPROP_PERMANENT) == 0));
     RootedValue enumerableVal(cx, BooleanValue((attrs & JSPROP_ENUMERATE) != 0));
     RootedValue writableVal(cx, BooleanValue((attrs & JSPROP_READONLY) == 0));
     if ((hasConfigurable() &&
@@ -1643,18 +1514,18 @@ js::NewObjectWithClassProtoCommon(Exclus
     return obj;
 }
 
 /*
  * Create a plain object with the specified type. This bypasses getNewType to
  * avoid losing creation site information for objects made by scripted 'new'.
  */
 JSObject *
-js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
-                      NewObjectKind newKind)
+js::NewObjectWithTypeCommon(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
+                            NewObjectKind newKind)
 {
     MOZ_ASSERT(parent);
 
     MOZ_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST);
     if (CanBeFinalizedInBackground(allocKind, type->clasp()))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     NewObjectCache &cache = cx->runtime()->newObjectCache;
@@ -1686,21 +1557,21 @@ js::NewObjectWithType(JSContext *cx, Han
     return obj;
 }
 
 bool
 js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj)
 {
     jsbytecode *pc;
     RootedScript script(cx, cx->currentScript(&pc));
-    gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
+    gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
     NewObjectKind newKind = script
-                            ? UseNewTypeForInitializer(script, pc, &JSObject::class_)
+                            ? UseNewTypeForInitializer(script, pc, &PlainObject::class_)
                             : GenericObject;
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind));
+    RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
     if (!obj)
         return false;
 
     if (script) {
         /* Try to specialize the type of the object to the scripted call site. */
         if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
             return false;
     }
@@ -1717,120 +1588,121 @@ js::CreateThis(JSContext *cx, const Clas
         return nullptr;
 
     JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr;
     JSObject *parent = callee->getParent();
     gc::AllocKind kind = NewObjectGCKind(newclasp);
     return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
 }
 
-static inline NativeObject *
+static inline PlainObject *
 CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
                               NewObjectKind newKind)
 {
     if (types::TypeNewScript *newScript = type->newScript()) {
         if (newScript->analyzed()) {
             // The definite properties analysis has been performed for this
             // type, so get the shape and finalize kind to use from the
             // TypeNewScript's template.
-            RootedNativeObject templateObject(cx, newScript->templateObject());
+            RootedPlainObject templateObject(cx, newScript->templateObject());
             MOZ_ASSERT(templateObject->type() == type);
 
-            RootedNativeObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
+            RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
             if (!res)
                 return nullptr;
 
             if (newKind == SingletonObject) {
                 Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->getProto()));
-                if (!res->splicePrototype(cx, &JSObject::class_, proto))
+                if (!res->splicePrototype(cx, &PlainObject::class_, proto))
                     return nullptr;
             } else {
                 res->setType(type);
             }
             return res;
         }
 
         // The initial objects registered with a TypeNewScript can't be in the
         // nursery.
         if (newKind == GenericObject)
             newKind = MaybeSingletonObject;
 
         // Not enough objects with this type have been created yet, so make a
         // plain object and register it with the type. Use the maximum number
         // of fixed slots, as is also required by the TypeNewScript.
         gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS);
-        NativeObject *res = NewNativeObjectWithType(cx, type, parent, allocKind, newKind);
+        PlainObject *res = NewObjectWithType<PlainObject>(cx, type, parent, allocKind, newKind);
         if (!res)
             return nullptr;
 
         if (newKind != SingletonObject)
             newScript->registerNewObject(res);
 
         return res;
     }
 
-    gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
-    return NewNativeObjectWithType(cx, type, parent, allocKind, newKind);
+    gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
+    return NewObjectWithType<PlainObject>(cx, type, parent, allocKind, newKind);
 }
 
-NativeObject *
+PlainObject *
 js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
                                    NewObjectKind newKind /* = GenericObject */)
 {
-    RootedNativeObject res(cx);
+    RootedPlainObject res(cx);
 
     if (proto) {
-        RootedTypeObject type(cx, cx->getNewType(&JSObject::class_, TaggedProto(proto), &callee->as<JSFunction>()));
+        RootedTypeObject type(cx, cx->getNewType(&PlainObject::class_, TaggedProto(proto),
+                                                 &callee->as<JSFunction>()));
         if (!type)
             return nullptr;
 
         if (type->newScript() && !type->newScript()->analyzed()) {
             bool regenerate;
             if (!type->newScript()->maybeAnalyze(cx, type, &regenerate))
                 return nullptr;
             if (regenerate) {
                 // The script was analyzed successfully and may have changed
                 // the new type table, so refetch the type.
-                type = cx->getNewType(&JSObject::class_, TaggedProto(proto), &callee->as<JSFunction>());
+                type = cx->getNewType(&PlainObject::class_, TaggedProto(proto),
+                                      &callee->as<JSFunction>());
                 MOZ_ASSERT(type && type->newScript());
             }
         }
 
         res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind);
     } else {
-        gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
-        res = NewNativeObjectWithClassProto(cx, &JSObject::class_, proto,
-                                            callee->getParent(), allocKind, newKind);
+        gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
+        res = NewObjectWithProto<PlainObject>(cx, proto, callee->getParent(), allocKind, newKind);
     }
 
     if (res) {
         JSScript *script = callee->as<JSFunction>().getOrCreateScript(cx);
         if (!script)
             return nullptr;
         TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
     }
 
     return res;
 }
 
-NativeObject *
+PlainObject *
 js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind)
 {
     RootedValue protov(cx);
     if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
         return nullptr;
     JSObject *proto;
     if (protov.isObject())
         proto = &protov.toObject();
     else
         proto = nullptr;
-    NativeObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
+    PlainObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
 
     if (obj && newKind == SingletonObject) {
-        RootedNativeObject nobj(cx, obj);
+        RootedPlainObject nobj(cx, obj);
 
         /* Reshape the singleton before passing it as the 'this' value. */
         NativeObject::clear(cx, nobj);
 
         JSScript *calleeScript = callee->as<JSFunction>().nonLazyScript();
         TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
 
         return nobj;
@@ -1985,17 +1857,17 @@ js::CloneObject(JSContext *cx, HandleObj
 }
 
 NativeObject *
 js::DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind)
 {
     /* NB: Keep this in sync with XDRObjectLiteral. */
     MOZ_ASSERT_IF(obj->hasSingletonType(),
                   JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
-    MOZ_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
+    MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
 
     // Result of the clone function.
     RootedNativeObject clone(cx);
 
     // Temporary element/slot which would be stored in the cloned object.
     RootedValue v(cx);
     RootedNativeObject deepObj(cx);
 
@@ -2004,17 +1876,17 @@ js::DeepCloneObjectLiteral(JSContext *cx
     } else {
         // Object literals are tenured by default as holded by the JSScript.
         MOZ_ASSERT(obj->isTenured());
         AllocKind kind = obj->asTenured().getAllocKind();
         Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
         if (!typeObj)
             return nullptr;
         RootedObject parent(cx, obj->getParent());
-        clone = NewNativeObjectWithGivenProto(cx, &JSObject::class_,
+        clone = NewNativeObjectWithGivenProto(cx, &PlainObject::class_,
                                               TaggedProto(typeObj->proto().toObject()),
                                               parent, kind, newKind);
     }
 
     // Allocate the same number of slots.
     if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
         return nullptr;
 
@@ -2056,17 +1928,17 @@ js::DeepCloneObjectLiteral(JSContext *cx
     }
 
     if (obj->hasSingletonType()) {
         if (!JSObject::setSingletonType(cx, clone))
             return nullptr;
     } else if (obj->is<ArrayObject>()) {
         FixArrayType(cx, &clone->as<ArrayObject>());
     } else {
-        FixObjectType(cx, clone);
+        FixObjectType(cx, &clone->as<PlainObject>());
     }
 
     if (obj->is<ArrayObject>() && obj->denseElementsAreCopyOnWrite()) {
         if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
             return nullptr;
     }
 
     return clone;
@@ -2081,17 +1953,17 @@ js::XDRObjectLiteral(XDRState<mode> *xdr
     JSContext *cx = xdr->cx();
     MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->hasSingletonType(),
                   JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
 
     // Distinguish between objects and array classes.
     uint32_t isArray = 0;
     {
         if (mode == XDR_ENCODE) {
-            MOZ_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
+            MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
             isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
         }
 
         if (!xdr->codeUint32(&isArray))
             return false;
     }
 
     if (isArray) {
@@ -2106,27 +1978,26 @@ js::XDRObjectLiteral(XDRState<mode> *xdr
         if (mode == XDR_DECODE)
             obj.set(NewDenseUnallocatedArray(cx, length, NULL, js::MaybeSingletonObject));
 
     } else {
         // Code the alloc kind of the object.
         AllocKind kind;
         {
             if (mode == XDR_ENCODE) {
-                MOZ_ASSERT(obj->getClass() == &JSObject::class_);
+                MOZ_ASSERT(obj->is<PlainObject>());
                 MOZ_ASSERT(obj->isTenured());
                 kind = obj->asTenured().getAllocKind();
             }
 
             if (!xdr->codeEnum32(&kind))
                 return false;
 
             if (mode == XDR_DECODE) {
-                obj.set(NewNativeBuiltinClassInstance(cx, &JSObject::class_, kind,
-                                                      MaybeSingletonObject));
+                obj.set(NewBuiltinClassInstance<PlainObject>(cx, kind, MaybeSingletonObject));
                 if (!obj)
                     return false;
             }
         }
     }
 
     {
         uint32_t capacity;
@@ -2264,17 +2135,17 @@ js::XDRObjectLiteral(XDRState<mode> *xdr
 
     if (mode == XDR_DECODE) {
         if (isSingletonTyped) {
             if (!JSObject::setSingletonType(cx, obj))
                 return false;
         } else if (isArray) {
             FixArrayType(cx, &obj->as<ArrayObject>());
         } else {
-            FixObjectType(cx, obj);
+            FixObjectType(cx, &obj->as<PlainObject>());
         }
     }
 
     {
         uint32_t frozen;
         bool extensible;
         if (mode == XDR_ENCODE) {
             if (!JSObject::isExtensible(cx, obj, &extensible))
@@ -2308,24 +2179,24 @@ template bool
 js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleNativeObject obj);
 
 template bool
 js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleNativeObject obj);
 
 JSObject *
 js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
 {
-    if (srcObj->getClass() == &JSObject::class_) {
-        AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as<NativeObject>().numFixedSlots()));
+    if (srcObj->is<PlainObject>()) {
+        AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as<PlainObject>().numFixedSlots()));
         MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind());
 
         JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx);
         if (!proto)
             return nullptr;
-        Rooted<TypeObject*> typeObj(cx, cx->getNewType(&JSObject::class_, TaggedProto(proto)));
+        Rooted<TypeObject*> typeObj(cx, cx->getNewType(&PlainObject::class_, TaggedProto(proto)));
         if (!typeObj)
             return nullptr;
 
         RootedShape shape(cx, srcObj->lastProperty());
         return NewReshapedObject(cx, typeObj, parent, kind, shape);
     }
 
     RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
@@ -3892,17 +3763,17 @@ dumpValue(const Value &v)
                     script->filename() ? script->filename() : "", (int) script->lineno());
         }
         fprintf(stderr, " at %p>", (void *) fun);
     } else if (v.isObject()) {
         JSObject *obj = &v.toObject();
         const Class *clasp = obj->getClass();
         fprintf(stderr, "<%s%s at %p>",
                 clasp->name,
-                (clasp == &JSObject::class_) ? "" : " object",
+                (clasp == &PlainObject::class_) ? "" : " object",
                 (void *) obj);
     } else if (v.isBoolean()) {
         if (v.toBoolean())
             fprintf(stderr, "true");
         else
             fprintf(stderr, "false");
     } else if (v.isMagic()) {
         fprintf(stderr, "<invalid");
@@ -4181,17 +4052,17 @@ JSObject::addSizeOfExcludingThis(mozilla
     if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
         js::ObjectElements *elements = as<NativeObject>().getElementsHeader();
         if (!elements->isCopyOnWrite() || elements->ownerObject() == this)
             info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
     }
 
     // Other things may be measured in the future if DMD indicates it is worthwhile.
     if (is<JSFunction>() ||
-        is<JSObject>() ||
+        is<PlainObject>() ||
         is<ArrayObject>() ||
         is<CallObject>() ||
         is<RegExpObject>() ||
         is<ProxyObject>())
     {
         // Do nothing.  But this function is hot, and we win by getting the
         // common cases out of the way early.  Some stats on the most common
         // classes, as measured during a vanilla browser session:
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -118,18 +118,16 @@ class JSObject : public js::gc::Cell
     friend class js::NewObjectCache;
     friend class js::Nursery;
     friend class js::gc::ForkJoinNursery;
 
     /* Make the type object to use for LAZY_TYPE objects. */
     static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj);
 
   public:
-    static const js::Class class_;
-
     js::Shape * lastProperty() const {
         MOZ_ASSERT(shape_);
         return shape_;
     }
 
     bool isNative() const {
         return lastProperty()->isNative();
     }
@@ -1025,22 +1023,22 @@ GetInitialHeap(NewObjectKind newKind, co
 {
     if (clasp->finalize || newKind != GenericObject)
         return gc::TenuredHeap;
     return gc::DefaultHeap;
 }
 
 // Specialized call for constructing |this| with a known function callee,
 // and a known prototype.
-extern NativeObject *
+extern PlainObject *
 CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto,
                                NewObjectKind newKind = GenericObject);
 
 // Specialized call for constructing |this| with a known function callee.
-extern NativeObject *
+extern PlainObject *
 CreateThisForFunction(JSContext *cx, js::HandleObject callee, NewObjectKind newKind);
 
 // Generic call for constructing |this|.
 extern JSObject *
 CreateThis(JSContext *cx, const js::Class *clasp, js::HandleObject callee);
 
 extern JSObject *
 CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -609,16 +609,33 @@ NewObjectWithGivenProto(ExclusiveContext
 
 inline JSObject *
 NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
                         NewObjectKind newKind = GenericObject)
 {
     return NewObjectWithGivenProto(cx, clasp, TaggedProto(proto), parent, newKind);
 }
 
+template <typename T>
+inline T *
+NewObjectWithGivenProto(ExclusiveContext *cx, TaggedProto proto, JSObject *parent,
+                        NewObjectKind newKind = GenericObject)
+{
+    JSObject *obj = NewObjectWithGivenProto(cx, &T::class_, proto, parent, newKind);
+    return obj ? &obj->as<T>() : nullptr;
+}
+
+template <typename T>
+inline T *
+NewObjectWithGivenProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
+                        NewObjectKind newKind = GenericObject)
+{
+    return NewObjectWithGivenProto<T>(cx, TaggedProto(proto), parent, newKind);
+}
+
 inline bool
 FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto)
 {
     if (!FindClassPrototype(cx, proto, clasp))
         return false;
 
     if (!proto) {
         // We're looking for the prototype of a class that is currently being
@@ -665,23 +682,29 @@ NewObjectWithClassProto(ExclusiveContext
 {
     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
     return NewObjectWithClassProto(cx, clasp, proto, parent, allocKind, newKind);
 }
 
 template<typename T>
 inline T *
 NewObjectWithProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
+                   gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
+{
+    JSObject *obj = NewObjectWithClassProto(cx, &T::class_, proto, parent, allocKind, newKind);
+    return obj ? &obj->as<T>() : nullptr;
+}
+
+template<typename T>
+inline T *
+NewObjectWithProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent,
                    NewObjectKind newKind = GenericObject)
 {
     JSObject *obj = NewObjectWithClassProto(cx, &T::class_, proto, parent, newKind);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<T>();
+    return obj ? &obj->as<T>() : nullptr;
 }
 
 /*
  * Create a native instance of the given class with parent and proto set
  * according to the context's active global.
  */
 inline JSObject *
 NewBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp, gc::AllocKind allocKind,
@@ -697,47 +720,51 @@ NewBuiltinClassInstance(ExclusiveContext
     return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
 }
 
 template<typename T>
 inline T *
 NewBuiltinClassInstance(ExclusiveContext *cx, NewObjectKind newKind = GenericObject)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<T>();
+    return obj ? &obj->as<T>() : nullptr;
 }
 
 template<typename T>
 inline T *
 NewBuiltinClassInstance(ExclusiveContext *cx, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
-    if (!obj)
-        return nullptr;
-
-    return &obj->as<T>();
+    return obj ? &obj->as<T>() : nullptr;
 }
 
 // Used to optimize calls to (new Object())
 bool
 NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj);
 
 JSObject *
-NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
-                  NewObjectKind newKind = GenericObject);
+NewObjectWithTypeCommon(JSContext *cx, HandleTypeObject type, JSObject *parent,
+                        gc::AllocKind allocKind, NewObjectKind newKind);
 
-inline JSObject *
+template <typename T>
+inline T *
+NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
+                  gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
+{
+    JSObject *obj = NewObjectWithTypeCommon(cx, type, parent, allocKind, newKind);
+    return obj ? &obj->as<T>() : nullptr;
+}
+
+template <typename T>
+inline T *
 NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
                   NewObjectKind newKind = GenericObject)
 {
     gc::AllocKind allocKind = gc::GetGCObjectKind(type->clasp());
-    return NewObjectWithType(cx, type, parent, allocKind, newKind);
+    return NewObjectWithType<T>(cx, type, parent, allocKind, newKind);
 }
 
 JSObject *
 NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
                   gc::AllocKind allocKind, HandleShape shape,
                   NewObjectKind newKind = GenericObject);
 
 /*
@@ -763,17 +790,17 @@ GuessArrayGCKind(size_t numSlots)
 
 inline bool
 ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
 {
     if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
         return Proxy::objectClassIs(obj, classValue, cx);
 
     switch (classValue) {
-      case ESClass_Object: return obj->is<JSObject>();
+      case ESClass_Object: return obj->is<PlainObject>();
       case ESClass_Array: return obj->is<ArrayObject>();
       case ESClass_Number: return obj->is<NumberObject>();
       case ESClass_String: return obj->is<StringObject>();
       case ESClass_Boolean: return obj->is<BooleanObject>();
       case ESClass_RegExp: return obj->is<RegExpObject>();
       case ESClass_ArrayBuffer: return obj->is<ArrayBufferObject>();
       case ESClass_SharedArrayBuffer: return obj->is<SharedArrayBufferObject>();
       case ESClass_Date: return obj->is<DateObject>();
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -660,17 +660,17 @@ js_Stringify(JSContext *cx, MutableHandl
         if (!gap.appendSubstring(str, 0, len))
             return false;
     } else {
         /* Step 8. */
         MOZ_ASSERT(gap.empty());
     }
 
     /* Step 9. */
-    RootedNativeObject wrapper(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_));
+    RootedPlainObject wrapper(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!wrapper)
         return false;
 
     /* Step 10. */
     RootedId emptyId(cx, NameToId(cx->names().empty));
     if (!DefineNativeProperty(cx, wrapper, emptyId, vp, JS_PropertyStub, JS_StrictPropertyStub,
                               JSPROP_ENUMERATE))
     {
@@ -781,17 +781,17 @@ Walk(JSContext *cx, HandleObject holder,
         return false;
     vp.set(args.rval());
     return true;
 }
 
 static bool
 Revive(JSContext *cx, HandleValue reviver, MutableHandleValue vp)
 {
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
         return false;
 
     if (!JSObject::defineProperty(cx, obj, cx->names().empty, vp))
         return false;
 
     Rooted<jsid> id(cx, NameToId(cx->names().empty));
     return Walk(cx, obj, id, reviver, vp);
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -65,17 +65,17 @@
 #ifdef JS_HAS_SYMBOLS
 #define IF_SYMBOLS(real,imaginary) real
 #else
 #define IF_SYMBOLS(real,imaginary) imaginary
 #endif
 
 #define JS_FOR_PROTOTYPES(real,imaginary) \
     imaginary(Null,              0,     js_InitNullClass,          dummy) \
-    real(Object,                 1,     js_InitViaClassSpec,       &JSObject::class_) \
+    real(Object,                 1,     js_InitViaClassSpec,       OCLASP(Plain)) \
     real(Function,               2,     js_InitViaClassSpec,       &JSFunction::class_) \
     real(Array,                  3,     js_InitViaClassSpec,       OCLASP(Array)) \
     real(Boolean,                4,     js_InitBooleanClass,       OCLASP(Boolean)) \
     real(JSON,                   5,     js_InitJSONClass,          CLASP(JSON)) \
     real(Date,                   6,     js_InitViaClassSpec,       OCLASP(Date)) \
     real(Math,                   7,     js_InitMathClass,          CLASP(Math)) \
     real(Number,                 8,     js_InitNumberClass,        OCLASP(Number)) \
     real(String,                 9,     js_InitStringClass,        OCLASP(String)) \
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -352,17 +352,17 @@ class NodeBuilder
         if (!atom)
             return false;
 
         dst.setString(atom);
         return true;
     }
 
     bool newObject(MutableHandleObject dst) {
-        RootedObject nobj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+        RootedPlainObject nobj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!nobj)
             return false;
 
         dst.set(nobj);
         return true;
     }
 
     bool newArray(NodeVector &elts, MutableHandleValue dst);
@@ -699,17 +699,17 @@ class NodeBuilder
 } /* anonymous namespace */
 
 bool
 NodeBuilder::newNode(ASTType type, TokenPos *pos, MutableHandleObject dst)
 {
     MOZ_ASSERT(type > AST_ERROR && type < AST_LIMIT);
 
     RootedValue tv(cx);
-    RootedObject node(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    RootedPlainObject node(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!node ||
         !setNodeLoc(node, pos) ||
         !atomValue(nodeTypeNames[type], &tv) ||
         !setProperty(node, "type", tv)) {
         return false;
     }
 
     dst.set(node);
@@ -3532,18 +3532,18 @@ JS_InitReflect(JSContext *cx, HandleObje
     static const JSFunctionSpec static_methods[] = {
         JS_FN("parse", reflect_parse, 1, 0),
         JS_FS_END
     };
 
     RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
-    RootedObject Reflect(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto,
-                                                     obj, SingletonObject));
+    RootedPlainObject Reflect(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, obj,
+                                                                       SingletonObject));
     if (!Reflect)
         return nullptr;
 
     if (!JS_DefineProperty(cx, obj, "Reflect", Reflect, 0,
                            JS_STUBGETTER, JS_STUBSETTER)) {
         return nullptr;
     }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -916,17 +916,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
         if (mode == XDR_ENCODE) {
             JSObject *obj = *objp;
             if (obj->is<BlockObject>())
                 classk = CK_BlockObject;
             else if (obj->is<StaticWithObject>())
                 classk = CK_WithObject;
             else if (obj->is<JSFunction>())
                 classk = CK_JSFunction;
-            else if (obj->is<JSObject>() || obj->is<ArrayObject>())
+            else if (obj->is<PlainObject>() || obj->is<ArrayObject>())
                 classk = CK_JSObject;
             else
                 MOZ_CRASH("Cannot encode this class of object.");
         }
 
         if (!xdr->codeEnum32(&classk))
             return false;
 
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -1196,17 +1196,17 @@ js::proxy_revocable(JSContext *cx, unsig
 
     RootedObject revoker(cx, NewFunctionByIdWithReserved(cx, RevokeProxy, 0, 0, cx->global(),
                          AtomToId(cx->names().revoke)));
     if (!revoker)
         return false;
 
     revoker->as<JSFunction>().initExtendedSlot(ScriptedDirectProxyHandler::REVOKE_SLOT, proxyVal);
 
-    RootedObject result(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!result)
         return false;
 
     RootedValue revokeVal(cx, ObjectValue(*revoker));
     if (!JSObject::defineProperty(cx, result, cx->names().proxy, proxyVal) ||
         !JSObject::defineProperty(cx, result, cx->names().revoke, revokeVal))
     {
         return false;
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -188,24 +188,25 @@ ArgumentsObject::create(JSContext *cx, H
                         numDeletedWords * sizeof(size_t) +
                         numArgs * sizeof(Value);
 
     ArgumentsData *data = reinterpret_cast<ArgumentsData *>(
             cx->zone()->pod_malloc<uint8_t>(numBytes));
     if (!data)
         return nullptr;
 
-    RootedNativeObject obj(cx);
-    obj = MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND,
-                                             GetInitialHeap(GenericObject, clasp),
-                                             shape, type));
-    if (!obj) {
+    Rooted<ArgumentsObject *> obj(cx);
+    JSObject *base = JSObject::create(cx, FINALIZE_KIND,
+                                      GetInitialHeap(GenericObject, clasp),
+                                      shape, type);
+    if (!base) {
         js_free(data);
         return nullptr;
     }
+    obj = &base->as<ArgumentsObject>();
 
     data->numArgs = numArgs;
     data->callee.init(ObjectValue(*callee.get()));
     data->script = script;
 
     // Initialize with dummy UndefinedValue, and attach it to the argument
     // object such as the GC can trace ArgumentsData as they are recovered.
     HeapValue *dst = data->args, *dstEnd = data->args + numArgs;
@@ -217,22 +218,21 @@ ArgumentsObject::create(JSContext *cx, H
     /* Copy [0, numArgs) into data->slots. */
     copy.copyArgs(cx, dst, numArgs);
 
     data->deletedBits = reinterpret_cast<size_t *>(dstEnd);
     ClearAllBitArrayElements(data->deletedBits, numDeletedWords);
 
     obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
 
-    copy.maybeForwardToCallObject(&obj->as<ArgumentsObject>(), data);
+    copy.maybeForwardToCallObject(obj, data);
 
-    ArgumentsObject &argsobj = obj->as<ArgumentsObject>();
-    MOZ_ASSERT(argsobj.initialLength() == numActuals);
-    MOZ_ASSERT(!argsobj.hasOverriddenLength());
-    return &argsobj;
+    MOZ_ASSERT(obj->initialLength() == numActuals);
+    MOZ_ASSERT(!obj->hasOverriddenLength());
+    return obj;
 }
 
 ArgumentsObject *
 ArgumentsObject::createExpected(JSContext *cx, AbstractFramePtr frame)
 {
     MOZ_ASSERT(frame.script()->needsArgsObj());
     RootedScript script(cx, frame.script());
     RootedFunction callee(cx, frame.callee());
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -99,17 +99,17 @@ ArrayObject::createArray(ExclusiveContex
     obj->elements_ = elements;
 
     return finishCreateArray(obj, shape);
 }
 
 /* static */ inline ArrayObject *
 ArrayObject::createCopyOnWriteArray(ExclusiveContext *cx, gc::InitialHeap heap,
                                     HandleShape shape,
-                                    HandleNativeObject sharedElementsOwner)
+                                    HandleArrayObject sharedElementsOwner)
 {
     MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->isCopyOnWrite());
     MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->ownerObject() == sharedElementsOwner);
 
     // Use the smallest allocation kind for the array, as it can't have any
     // fixed slots (see the assert in createArrayInternal) and will not be using
     // its fixed elements.
     gc::AllocKind kind = gc::FINALIZE_OBJECT0_BACKGROUND;
--- a/js/src/vm/ArrayObject.h
+++ b/js/src/vm/ArrayObject.h
@@ -56,17 +56,17 @@ class ArrayObject : public NativeObject
                 HeapSlot *elements);
 
     // Make a copy-on-write array object which shares the elements of an
     // existing object.
     static inline ArrayObject *
     createCopyOnWriteArray(ExclusiveContext *cx,
                            gc::InitialHeap heap,
                            HandleShape shape,
-                           HandleNativeObject sharedElementsOwner);
+                           HandleArrayObject sharedElementsOwner);
 
   private:
     // Helper for the above methods.
     static inline ArrayObject *
     createArrayInternal(ExclusiveContext *cx,
                         gc::AllocKind kind,
                         gc::InitialHeap heap,
                         HandleShape shape,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -842,17 +842,17 @@ Debugger::wrapDebuggeeValue(JSContext *c
                     js_ReportOutOfMemory(cx);
                     return false;
                 }
             }
 
             vp.setObject(*dobj);
         }
     } else if (vp.isMagic()) {
-        RootedObject optObj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+        RootedPlainObject optObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!optObj)
             return false;
 
         // We handle three sentinel values: missing arguments (overloading
         // JS_OPTIMIZED_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
         // and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
         //
         // Other magic values should not have escaped.
@@ -1045,17 +1045,17 @@ Debugger::newCompletionValue(JSContext *
         result.setNull();
         return true;
 
       default:
         MOZ_CRASH("bad status passed to Debugger::newCompletionValue");
     }
 
     /* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_));
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj ||
         !wrapDebuggeeValue(cx, &value) ||
         !DefineNativeProperty(cx, obj, key, value, JS_PropertyStub, JS_StrictPropertyStub,
                               JSPROP_ENUMERATE))
     {
         return false;
     }
 
@@ -1097,17 +1097,17 @@ Debugger::parseResumptionValue(Maybe<Aut
     JSContext *cx = ac->context()->asJSContext();
     Rooted<JSObject*> obj(cx);
     RootedShape shape(cx);
     RootedId returnId(cx, NameToId(cx->names().return_));
     RootedId throwId(cx, NameToId(cx->names().throw_));
     bool okResumption = rv.isObject();
     if (okResumption) {
         obj = &rv.toObject();
-        okResumption = obj->is<JSObject>();
+        okResumption = obj->is<PlainObject>();
     }
     if (okResumption) {
         shape = obj->lastProperty();
         okResumption = shape->previous() &&
              !shape->previous()->previous() &&
              (shape->propid() == returnId || shape->propid() == throwId) &&
              shape->isDataDescriptor();
     }
@@ -4397,17 +4397,17 @@ DebuggerScript_getAllColumnOffsets(JSCon
         size_t lineno = r.frontLineNumber();
         size_t column = r.frontColumnNumber();
         size_t offset = r.frontOffset();
 
         /* Make a note, if the current instruction is an entry point for the current position. */
         if (!flowData[offset].hasNoEdges() &&
             (flowData[offset].lineno() != lineno ||
              flowData[offset].column() != column)) {
-            RootedObject entry(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+            RootedPlainObject entry(cx, NewBuiltinClassInstance<PlainObject>(cx));
             if (!entry)
                 return false;
 
             RootedId id(cx, NameToId(cx->names().lineNumber));
             RootedValue value(cx, NumberValue(lineno));
             if (!JSObject::defineGeneric(cx, entry, id, value))
                 return false;
 
@@ -5743,18 +5743,17 @@ DebuggerGenericEval(JSContext *cx, const
             return false;
         thisv = ObjectValue(*thisObj);
         env = scope;
     }
 
     /* If evalWithBindings, create the inner environment. */
     if (evalWithBindings) {
         /* TODO - This should probably be a Call object, like ES5 strict eval. */
-        RootedNativeObject nenv(cx, NewNativeObjectWithGivenProto(cx, &JSObject::class_,
-                                                                  nullptr, env));
+        RootedPlainObject nenv(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, env));
         if (!nenv)
             return false;
         RootedId id(cx);
         for (size_t i = 0; i < keys.length(); i++) {
             id = keys[i];
             MutableHandleValue val = values[i];
             if (!cx->compartment()->wrap(cx, val) ||
                 !DefineNativeProperty(cx, nenv, id, val, nullptr, nullptr, 0))
@@ -7170,17 +7169,17 @@ Builder::Object::defineProperty(JSContex
     return definePropertyToTrusted(cx, name, &propval);
 }
 
 Builder::Object
 Builder::newObject(JSContext *cx)
 {
     AutoCompartment ac(cx, debuggerObject);
 
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
 
     // If the allocation failed, this will return a false Object, as the spec promises.
     return Object(cx, *this, obj);
 }
 
 
 /*** Glue ****************************************************************************************/
 
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -196,17 +196,17 @@ DebuggerMemory::drainAllocationsLog(JSCo
     size_t length = dbg->allocationsLogLength;
 
     RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
     if (!result)
         return false;
     result->ensureDenseInitializedLength(cx, 0, length);
 
     for (size_t i = 0; i < length; i++) {
-        RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+        RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
 
         mozilla::UniquePtr<Debugger::AllocationSite, JS::DeletePolicy<Debugger::AllocationSite> >
             allocSite(dbg->allocationsLog.popFirst());
         RootedValue frame(cx, ObjectOrNullValue(allocSite->frame));
         if (!JSObject::defineProperty(cx, obj, cx->names().frame, frame))
             return false;
@@ -358,17 +358,17 @@ class Tally {
     bool count(Census &census, const Node &node) {
         total_++;
         return true;
     }
 
     size_t total() const { return total_; }
 
     bool report(Census &census, MutableHandleValue report) {
-        RootedObject obj(census.cx, NewBuiltinClassInstance(census.cx, &JSObject::class_));
+        RootedPlainObject obj(census.cx, NewBuiltinClassInstance<PlainObject>(census.cx));
         RootedValue countValue(census.cx, NumberValue(total_));
         if (!obj ||
             !JSObject::defineProperty(census.cx, obj, census.cx->names().count, countValue))
         {
             return false;
         }
         report.setObject(*obj);
         return true;
@@ -430,17 +430,17 @@ class ByJSType {
         if (node.is<JSString>())
             return strings.count(census, node);
         return other.count(census, node);
     }
 
     bool report(Census &census, MutableHandleValue report) {
         JSContext *cx = census.cx;
 
-        RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+        RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
 
         RootedValue objectsReport(cx);
         if (!objects.report(census, &objectsReport) ||
             !JSObject::defineProperty(cx, obj, cx->names().objects, objectsReport))
             return false;
 
@@ -547,17 +547,17 @@ class ByObjectClass {
         mozilla::Vector<Entry *> entries;
         if (!entries.reserve(table.count()))
             return false;
         for (typename Table::Range r = table.all(); !r.empty(); r.popFront())
             entries.infallibleAppend(&r.front());
         qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), compareEntries);
 
         // Now build the result by iterating over the sorted vector.
-        RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+        RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
         for (Entry **entryPtr = entries.begin(); entryPtr < entries.end(); entryPtr++) {
             Entry &entry = **entryPtr;
             EachClass &assorter = entry.value();
             RootedValue assorterReport(cx);
             if (!assorter.report(census, &assorterReport))
                 return false;
@@ -656,17 +656,17 @@ class ByUbinodeType {
         mozilla::Vector<Entry *> entries;
         if (!entries.reserve(table.count()))
             return false;
         for (typename Table::Range r = table.all(); !r.empty(); r.popFront())
             entries.infallibleAppend(&r.front());
         qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), compareEntries);
 
         // Now build the result by iterating over the sorted vector.
-        RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
+        RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!obj)
             return false;
         for (Entry **entryPtr = entries.begin(); entryPtr < entries.end(); entryPtr++) {
             Entry &entry = **entryPtr;
             EachType &assorter = entry.value();
             RootedValue assorterReport(cx);
             if (!assorter.report(census, &assorterReport))
                 return false;
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -256,26 +256,26 @@ static const JSFunctionSpec legacy_gener
 #undef JSPROP_ROPERM
 
 static JSObject*
 NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
 {
     JSObject *proto = global->getOrCreateObjectPrototype(cx);
     if (!proto)
         return nullptr;
-    return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
+    return NewObjectWithGivenProto<PlainObject>(cx, proto, global, SingletonObject);
 }
 
 static JSObject*
 NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
 {
     JSObject *proto = global->getOrCreateFunctionPrototype(cx);
     if (!proto)
         return nullptr;
-    return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
+    return NewObjectWithGivenProto<PlainObject>(cx, proto, global, SingletonObject);
 }
 
 /* static */ bool
 GlobalObject::initGeneratorClasses(JSContext *cx, Handle<GlobalObject *> global)
 {
     if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
         RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
         if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -281,16 +281,22 @@ class GlobalObject : public NativeObject
     NativeObject *createBlankPrototype(JSContext *cx, const js::Class *clasp);
 
     /*
      * Identical to createBlankPrototype, but uses proto as the [[Prototype]]
      * of the returned blank prototype.
      */
     NativeObject *createBlankPrototypeInheriting(JSContext *cx, const js::Class *clasp, JSObject &proto);
 
+    template <typename T>
+    T *createBlankPrototype(JSContext *cx) {
+        NativeObject *res = createBlankPrototype(cx, &T::class_);
+        return res ? &res->template as<T>() : nullptr;
+    }
+
     NativeObject *getOrCreateObjectPrototype(JSContext *cx) {
         if (functionObjectClassesInitialized())
             return &getPrototype(JSProto_Object).toObject().as<NativeObject>();
         Rooted<GlobalObject*> self(cx, this);
         if (!ensureConstructor(cx, self, JSProto_Object))
             return nullptr;
         return &self->getPrototype(JSProto_Object).toObject().as<NativeObject>();
     }
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3069,18 +3069,18 @@ CASE(JSOP_NEWINIT)
 
     RootedObject &obj = rootObject0;
     NewObjectKind newKind;
     if (i == JSProto_Array) {
         newKind = UseNewTypeForInitializer(script, REGS.pc, &ArrayObject::class_);
         obj = NewDenseEmptyArray(cx, nullptr, newKind);
     } else {
         gc::AllocKind allocKind = GuessObjectGCKind(0);
-        newKind = UseNewTypeForInitializer(script, REGS.pc, &JSObject::class_);
-        obj = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind);
+        newKind = UseNewTypeForInitializer(script, REGS.pc, &PlainObject::class_);
+        obj = NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind);
     }
     if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWINIT)
 
@@ -3094,38 +3094,38 @@ CASE(JSOP_NEWARRAY)
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWARRAY)
 
 CASE(JSOP_NEWARRAY_COPYONWRITE)
 {
-    RootedNativeObject &baseobj = rootNativeObject0;
+    RootedObject &baseobj = rootObject0;
     baseobj = types::GetOrFixupCopyOnWriteObject(cx, script, REGS.pc);
     if (!baseobj)
         goto error;
 
     RootedObject &obj = rootObject1;
-    obj = NewDenseCopyOnWriteArray(cx, baseobj, gc::DefaultHeap);
+    obj = NewDenseCopyOnWriteArray(cx, baseobj.as<ArrayObject>(), gc::DefaultHeap);
     if (!obj)
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWARRAY_COPYONWRITE)
 
 CASE(JSOP_NEWOBJECT)
 {
-    RootedNativeObject &baseobj = rootNativeObject0;
+    RootedObject &baseobj = rootObject0;
     baseobj = script->getObject(REGS.pc);
 
     RootedObject &obj = rootObject1;
     NewObjectKind newKind = UseNewTypeForInitializer(script, REGS.pc, baseobj->getClass());
-    obj = CopyInitializerObject(cx, baseobj, newKind);
+    obj = CopyInitializerObject(cx, baseobj.as<PlainObject>(), newKind);
     if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWOBJECT)
 
 CASE(JSOP_MUTATEPROTO)
@@ -3133,17 +3133,17 @@ CASE(JSOP_MUTATEPROTO)
     MOZ_ASSERT(REGS.stackDepth() >= 2);
 
     if (REGS.sp[-1].isObjectOrNull()) {
         RootedObject &newProto = rootObject1;
         rootObject1 = REGS.sp[-1].toObjectOrNull();
 
         RootedObject &obj = rootObject0;
         obj = &REGS.sp[-2].toObject();
-        MOZ_ASSERT(obj->is<JSObject>());
+        MOZ_ASSERT(obj->is<PlainObject>());
 
         bool succeeded;
         if (!JSObject::setProto(cx, obj, newProto, &succeeded))
             goto error;
         MOZ_ASSERT(succeeded);
     }
 
     REGS.sp--;
@@ -3154,18 +3154,17 @@ CASE(JSOP_INITPROP)
 {
     /* Load the property's initial value into rval. */
     MOZ_ASSERT(REGS.stackDepth() >= 2);
     RootedValue &rval = rootValue0;
     rval = REGS.sp[-1];
 
     /* Load the object being initialized into lval/obj. */
     RootedNativeObject &obj = rootNativeObject0;
-    obj = &REGS.sp[-2].toObject().as<NativeObject>();
-    MOZ_ASSERT(obj->is<JSObject>());
+    obj = &REGS.sp[-2].toObject().as<PlainObject>();
 
     PropertyName *name = script->getName(REGS.pc);
 
     RootedId &id = rootId0;
     id = NameToId(name);
 
     if (!DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE))
         goto error;
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -591,17 +591,17 @@ JSONParserBase::createFinishedObject(Pro
             return obj;
     }
 
     /*
      * Make a new object sized for the given number of properties and fill its
      * shape in manually.
      */
     gc::AllocKind allocKind = gc::GetGCObjectKind(properties.length());
-    RootedNativeObject obj(cx, NewNativeBuiltinClassInstance(cx, &JSObject::class_, allocKind));
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
     if (!obj)
         return nullptr;
 
     RootedId propid(cx);
     RootedValue value(cx);
 
     for (size_t i = 0; i < properties.length(); i++) {
         propid = properties[i].id;
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -365,29 +365,27 @@ NativeObject::setSlotWithType(ExclusiveC
 
     if (overwriting)
         shape->setOverwritten();
 
     types::AddTypePropertyId(cx, this, shape->propid(), value);
 }
 
 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
-static inline NativeObject *
-CopyInitializerObject(JSContext *cx, HandleNativeObject baseobj, NewObjectKind newKind = GenericObject)
+static inline PlainObject *
+CopyInitializerObject(JSContext *cx, HandlePlainObject baseobj, NewObjectKind newKind = GenericObject)
 {
-    MOZ_ASSERT(baseobj->getClass() == &JSObject::class_);
     MOZ_ASSERT(!baseobj->inDictionaryMode());
 
     gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
     allocKind = gc::GetBackgroundAllocKind(allocKind);
     MOZ_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->asTenured().getAllocKind());
-    JSObject *baseObj = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind);
-    if (!baseObj)
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
+    if (!obj)
         return nullptr;
-    RootedNativeObject obj(cx, &baseObj->as<NativeObject>());
 
     RootedObject metadata(cx, obj->getMetadata());
     RootedShape lastProp(cx, baseobj->lastProperty());
     if (!NativeObject::setLastProperty(cx, obj, lastProp))
         return nullptr;
     if (metadata && !JSObject::setMetadata(cx, obj, metadata))
         return nullptr;
 
@@ -414,57 +412,29 @@ inline NativeObject *
 NewNativeObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp,
                               JSObject *proto, JSObject *parent,
                               NewObjectKind newKind = GenericObject)
 {
     return MaybeNativeObject(NewObjectWithGivenProto(cx, clasp, proto, parent, newKind));
 }
 
 inline NativeObject *
-NewNativeBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp,
-                              gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
-{
-    return MaybeNativeObject(NewBuiltinClassInstance(cx, clasp, allocKind, newKind));
-}
-
-inline NativeObject *
-NewNativeBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp,
-                              NewObjectKind newKind = GenericObject)
-{
-    return MaybeNativeObject(NewBuiltinClassInstance(cx, clasp, newKind));
-}
-
-inline NativeObject *
 NewNativeObjectWithClassProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
                               gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
 {
     return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, parent, allocKind, newKind));
 }
 
 inline NativeObject *
 NewNativeObjectWithClassProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent,
                               NewObjectKind newKind = GenericObject)
 {
     return MaybeNativeObject(NewObjectWithClassProto(cx, clasp, proto, parent, newKind));
 }
 
-inline NativeObject *
-NewNativeObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
-                        NewObjectKind newKind = GenericObject)
-{
-    return MaybeNativeObject(NewObjectWithType(cx, type, parent, allocKind, newKind));
-}
-
-inline NativeObject *
-NewNativeObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
-                        NewObjectKind newKind = GenericObject)
-{
-    return MaybeNativeObject(NewObjectWithType(cx, type, parent, newKind));
-}
-
 /*
  * Call obj's resolve hook.
  *
  * cx, id, and flags are the parameters initially passed to the ongoing lookup;
  * objp and propp are its out parameters. obj is an object along the prototype
  * chain from where the lookup started.
  *
  * There are four possible outcomes:
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1232,16 +1232,24 @@ class NativeObject : public JSObject
 
     static size_t getFixedSlotOffset(size_t slot) {
         return sizeof(NativeObject) + slot * sizeof(Value);
     }
     static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); }
     static size_t offsetOfSlots() { return offsetof(NativeObject, slots_); }
 };
 
+// Object class for plain native objects created using '{}' object literals,
+// 'new Object()', 'Object.create', etc.
+class PlainObject : public NativeObject
+{
+  public:
+    static const js::Class class_;
+};
+
 inline void
 NativeObject::privateWriteBarrierPre(void **oldval)
 {
 #ifdef JSGC_INCREMENTAL
     JS::shadow::Zone *shadowZone = this->shadowZoneFromAnyThread();
     if (shadowZone->needsIncrementalBarrier()) {
         if (*oldval && getClass()->trace)
             getClass()->trace(shadowZone->barrierTracer(), this);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -48,41 +48,39 @@ RegExpObjectBuilder::RegExpObjectBuilder
 bool
 RegExpObjectBuilder::getOrCreate()
 {
     if (reobj_)
         return true;
 
     // Note: RegExp objects are always allocated in the tenured heap. This is
     // not strictly required, but simplifies embedding them in jitcode.
-    NativeObject *obj = NewNativeBuiltinClassInstance(cx, &RegExpObject::class_, TenuredObject);
-    if (!obj)
+    reobj_ = NewBuiltinClassInstance<RegExpObject>(cx, TenuredObject);
+    if (!reobj_)
         return false;
-    obj->initPrivate(nullptr);
+    reobj_->initPrivate(nullptr);
 
-    reobj_ = &obj->as<RegExpObject>();
     return true;
 }
 
 bool
 RegExpObjectBuilder::getOrCreateClone(HandleTypeObject type)
 {
     MOZ_ASSERT(!reobj_);
     MOZ_ASSERT(type->clasp() == &RegExpObject::class_);
 
     JSObject *parent = type->proto().toObject()->getParent();
 
     // Note: RegExp objects are always allocated in the tenured heap. This is
     // not strictly required, but simplifies embedding them in jitcode.
-    NativeObject *clone = NewNativeObjectWithType(cx->asJSContext(), type, parent, TenuredObject);
-    if (!clone)
+    reobj_ = NewObjectWithType<RegExpObject>(cx->asJSContext(), type, parent, TenuredObject);
+    if (!reobj_)
         return false;
-    clone->initPrivate(nullptr);
+    reobj_->initPrivate(nullptr);
 
-    reobj_ = &clone->as<RegExpObject>();
     return true;
 }
 
 RegExpObject *
 RegExpObjectBuilder::build(HandleAtom source, RegExpShared &shared)
 {
     if (!getOrCreate())
         return nullptr;
@@ -250,17 +248,17 @@ RegExpObject::trace(JSTracer *trc, JSObj
     //   1. During TraceRuntime, isHeapBusy() is true, but the tracer might not
     //      be a marking tracer.
     //   2. When a write barrier executes, IS_GC_MARKING_TRACER is true, but
     //      isHeapBusy() will be false.
     if (trc->runtime()->isHeapBusy() &&
         IS_GC_MARKING_TRACER(trc) &&
         !obj->asTenured().zone()->isPreservingCode())
     {
-        obj->as<NativeObject>().setPrivate(nullptr);
+        obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
     } else {
         shared->trace(trc);
     }
 }
 
 const Class RegExpObject::class_ = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -17,24 +17,24 @@ using namespace js;
  * per-global and not leak, we create a js::Class to wrap the C++ instance and
  * provide an appropriate finalizer. We lazily create and store an instance of
  * that js::Class in a global reserved slot.
  */
 
 static void
 resc_finalize(FreeOp *fop, JSObject *obj)
 {
-    RegExpStatics *res = static_cast<RegExpStatics *>(obj->as<NativeObject>().getPrivate());
+    RegExpStatics *res = static_cast<RegExpStatics *>(obj->as<RegExpStaticsObject>().getPrivate());
     fop->delete_(res);
 }
 
 static void
 resc_trace(JSTracer *trc, JSObject *obj)
 {
-    void *pdata = obj->as<NativeObject>().getPrivate();
+    void *pdata = obj->as<RegExpStaticsObject>().getPrivate();
     MOZ_ASSERT(pdata);
     RegExpStatics *res = static_cast<RegExpStatics *>(pdata);
     res->mark(trc);
 }
 
 const Class RegExpStaticsObject::class_ = {
     "RegExpStatics",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
@@ -50,24 +50,24 @@ const Class RegExpStaticsObject::class_ 
     nullptr,                 /* hasInstance */
     nullptr,                 /* construct   */
     resc_trace
 };
 
 RegExpStaticsObject *
 RegExpStatics::create(ExclusiveContext *cx, GlobalObject *parent)
 {
-    NativeObject *obj = NewNativeObjectWithGivenProto(cx, &RegExpStaticsObject::class_, nullptr, parent);
+    RegExpStaticsObject *obj = NewObjectWithGivenProto<RegExpStaticsObject>(cx, nullptr, parent);
     if (!obj)
         return nullptr;
     RegExpStatics *res = cx->new_<RegExpStatics>();
     if (!res)
         return nullptr;
     obj->setPrivate(static_cast<void *>(res));
-    return &obj->as<RegExpStaticsObject>();
+    return obj;
 }
 
 void
 RegExpStatics::markFlagsSet(JSContext *cx)
 {
     // Flags set on the RegExp function get propagated to constructed RegExp
     // objects, which interferes with optimizations that inline RegExp cloning
     // or avoid cloning entirely. Scripts making this assumption listen to
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -594,20 +594,18 @@ SavedStacks::getOrCreateSavedFrameProtot
 {
     if (savedFrameProto)
         return savedFrameProto;
 
     Rooted<GlobalObject *> global(cx, cx->compartment()->maybeGlobal());
     if (!global)
         return nullptr;
 
-    RootedNativeObject proto(cx,
-        NewNativeObjectWithGivenProto(cx, &SavedFrame::class_,
-                                      global->getOrCreateObjectPrototype(cx),
-                                      global));
+    Rooted<SavedFrame *> proto(cx,
+        NewObjectWithGivenProto<SavedFrame>(cx, global->getOrCreateObjectPrototype(cx), global));
     if (!proto
         || !JS_DefineProperties(cx, proto, SavedFrame::properties)
         || !JS_DefineFunctions(cx, proto, SavedFrame::methods)
         || !JSObject::freeze(cx, proto))
     {
         return nullptr;
     }
 
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -676,17 +676,17 @@ NativeObject::addPropertyInternal<Parall
                                                      uint32_t slot, unsigned attrs,
                                                      unsigned flags, Shape **spp,
                                                      bool allowDictionary);
 
 JSObject *
 js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
                       gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind)
 {
-    RootedNativeObject res(cx, NewNativeObjectWithType(cx, type, parent, allocKind, newKind));
+    RootedPlainObject res(cx, NewObjectWithType<PlainObject>(cx, type, parent, allocKind, newKind));
     if (!res)
         return nullptr;
 
     if (shape->isEmptyShape())
         return res;
 
     /* Get all the ids in the object, in order. */
     js::AutoIdVector ids(cx);
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1627,18 +1627,18 @@ JSStructuredCloneReader::startRead(Mutab
             return false;
         vp.setObject(*reobj);
         break;
       }
 
       case SCTAG_ARRAY_OBJECT:
       case SCTAG_OBJECT_OBJECT: {
         JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
-                        ? NewDenseEmptyArray(context())
-                        : NewBuiltinClassInstance(context(), &JSObject::class_);
+                        ? (JSObject *) NewDenseEmptyArray(context())
+                        : (JSObject *) NewBuiltinClassInstance<PlainObject>(context());
         if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp.setObject(*obj);
         break;
       }
 
       case SCTAG_BACK_REFERENCE_OBJECT: {
         if (data >= allObjs.length()) {