Backed out changeset 94f1fc3d9ec8 (bug 1135897) for LSAN leaks.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Mar 2015 15:02:08 -0400
changeset 233330 aaf2125ffc1cdef0ef786fb3d8667f844a437550
parent 233329 98e5e14b05e4bfc6671cc9942638723d57f9fbc9
child 233331 43eb116a884d328529fe9137f8dd65437c742fc8
push id56827
push userryanvm@gmail.com
push dateThu, 12 Mar 2015 19:02:15 +0000
treeherdermozilla-inbound@aaf2125ffc1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1135897
milestone39.0a1
backs out94f1fc3d9ec8820a89be01b98100a7ce935c8571
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
Backed out changeset 94f1fc3d9ec8 (bug 1135897) for LSAN leaks. CLOSED TREE
js/public/Id.h
js/src/asmjs/AsmJSLink.cpp
js/src/ds/IdValuePair.h
js/src/frontend/BytecodeEmitter.cpp
js/src/gc/RootMarking.cpp
js/src/irregexp/NativeRegExpMacroAssembler.cpp
js/src/irregexp/RegExpEngine.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/JitCommon.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/RegisterSets.h
js/src/jsapi.h
js/src/jsatominlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jspubtd.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Interpreter.cpp
js/src/vm/JSONParser.cpp
js/src/vm/JSONParser.h
js/src/vm/NativeObject.h
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
js/src/vm/Shape.cpp
js/src/vm/TypeInference.cpp
js/src/vm/TypeInference.h
js/src/vm/UnboxedObject.cpp
js/src/vm/UnboxedObject.h
js/src/vm/Xdr.h
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -3,26 +3,26 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_Id_h
 #define js_Id_h
 
 // A jsid is an identifier for a property or method of an object which is
-// either a 31-bit unsigned integer, interned string or symbol.
+// either a 31-bit signed integer, interned string or object.
 //
 // Also, there is an additional jsid value, JSID_VOID, which does not occur in
 // JS scripts but may be used to indicate the absence of a valid jsid.  A void
 // jsid is not a valid id and only arises as an exceptional API return value,
 // such as in JS_NextProperty. Embeddings must not pass JSID_VOID into JSAPI
 // entry points expecting a jsid and do not need to handle JSID_VOID in hooks
 // receiving a jsid except when explicitly noted in the API contract.
 //
-// A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
+// A jsid is not implicitly convertible to or from a jsval; JS_ValueToId or
 // JS_IdToValue must be used instead.
 
 #include "jstypes.h"
 
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -746,17 +746,17 @@ CallAsmJS(JSContext *cx, unsigned argc, 
         // that the optimized asm.js-to-Ion FFI call path (which we want to be
         // very fast) can avoid doing so. The JitActivation is marked as
         // inactive so stack iteration will skip over it.
         AsmJSActivation activation(cx, module);
         JitActivation jitActivation(cx, /* active */ false);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
         AsmJSModule::CodePtr enter = module.entryTrampoline(func);
-        if (!CALL_GENERATED_2(enter, coercedArgs.begin(), module.globalData()))
+        if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData()))
             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.
--- a/js/src/ds/IdValuePair.h
+++ b/js/src/ds/IdValuePair.h
@@ -13,35 +13,17 @@
 
 namespace js {
 
 struct IdValuePair
 {
     jsid id;
     Value value;
 
-    IdValuePair()
-      : id(JSID_EMPTY), value(UndefinedValue())
-    {}
+    IdValuePair() {}
     explicit IdValuePair(jsid idArg)
       : id(idArg), value(UndefinedValue())
     {}
-    IdValuePair(jsid idArg, Value valueArg)
-      : id(idArg), value(valueArg)
-    {}
-};
-
-class MOZ_STACK_CLASS AutoIdValueVector : public AutoVectorRooter<IdValuePair>
-{
-  public:
-    explicit AutoIdValueVector(ContextFriendFields *cx
-                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-        : AutoVectorRooter<IdValuePair>(cx, IDVALVECTOR)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } /* namespace js */
 
 #endif /* ds_IdValuePair_h */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4314,17 +4314,21 @@ ParseNode::getConstantValue(ExclusiveCon
 
         if (allowObjects == DontAllowObjects) {
             vp.setMagic(JS_GENERIC_MAGIC);
             return true;
         }
         if (allowObjects == DontAllowNestedObjects)
             allowObjects = DontAllowObjects;
 
-        AutoIdValueVector properties(cx);
+        gc::AllocKind kind = GuessObjectGCKind(pn_count);
+        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;
             if (value.isMagic(JS_GENERIC_MAGIC)) {
                 vp.setMagic(JS_GENERIC_MAGIC);
                 return true;
@@ -4334,29 +4338,41 @@ ParseNode::getConstantValue(ExclusiveCon
             if (pnid->isKind(PNK_NUMBER)) {
                 idvalue = NumberValue(pnid->pn_dval);
             } else {
                 MOZ_ASSERT(pnid->isKind(PNK_OBJECT_PROPERTY_NAME) || pnid->isKind(PNK_STRING));
                 MOZ_ASSERT(pnid->pn_atom != cx->names().proto);
                 idvalue = StringValue(pnid->pn_atom);
             }
 
-            RootedId id(cx);
-            if (!ValueToId<CanGC>(cx, idvalue, &id))
-                return false;
-
-            if (!properties.append(IdValuePair(id, value)))
-                return false;
+            uint32_t index;
+            if (IsDefinitelyIndex(idvalue, &index)) {
+                if (!DefineElement(cx, obj, index, value, nullptr, nullptr, JSPROP_ENUMERATE))
+                    return false;
+
+                continue;
+            }
+
+            JSAtom *name = ToAtom<CanGC>(cx, idvalue);
+            if (!name)
+                return false;
+
+            if (name->isIndex(&index)) {
+                if (!DefineElement(cx, obj, index, value, nullptr, nullptr, JSPROP_ENUMERATE))
+                    return false;
+            } else {
+                if (!DefineProperty(cx, obj, name->asPropertyName(), value,
+                                    nullptr, nullptr, JSPROP_ENUMERATE))
+                {
+                    return false;
+                }
+            }
         }
 
-        JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(),
-                                                    TenuredObject);
-        if (!obj)
-            return false;
-
+        ObjectGroup::fixPlainObjectGroup(cx, obj);
         vp.setObject(*obj);
         return true;
       }
       default:
         MOZ_CRASH("Unexpected node");
     }
     return false;
 }
@@ -7773,17 +7789,17 @@ CGObjectList::indexOf(JSObject *obj)
 }
 
 void
 CGObjectList::finish(ObjectArray *array)
 {
     MOZ_ASSERT(length <= INDEX_LIMIT);
     MOZ_ASSERT(length == array->length);
 
-    js::HeapPtrObject *cursor = array->vector + array->length;
+    js::HeapPtrNativeObject *cursor = array->vector + array->length;
     ObjectBox *objbox = lastbox;
     do {
         --cursor;
         MOZ_ASSERT(!*cursor);
         *cursor = objbox->object;
     } while ((objbox = objbox->emitLink) != nullptr);
     MOZ_ASSERT(cursor == array->vector);
 }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -163,25 +163,16 @@ AutoGCRooter::trace(JSTracer *trc)
       }
 
       case IDVECTOR: {
         AutoIdVector::VectorImpl &vector = static_cast<AutoIdVector *>(this)->vector;
         MarkIdRootRange(trc, vector.length(), vector.begin(), "js::AutoIdVector.vector");
         return;
       }
 
-      case IDVALVECTOR: {
-        AutoIdValueVector::VectorImpl &vector = static_cast<AutoIdValueVector *>(this)->vector;
-        for (size_t i = 0; i < vector.length(); i++) {
-            MarkIdRoot(trc, &vector[i].id, "js::AutoIdValueVector id");
-            MarkValueRoot(trc, &vector[i].value, "js::AutoIdValueVector value");
-        }
-        return;
-      }
-
       case SHAPEVECTOR: {
         AutoShapeVector::VectorImpl &vector = static_cast<js::AutoShapeVector *>(this)->vector;
         MarkShapeRootRange(trc, vector.length(), const_cast<Shape **>(vector.begin()),
                            "js::AutoShapeVector.vector");
         return;
       }
 
       case OBJVECTOR: {
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -84,17 +84,29 @@ NativeRegExpMacroAssembler::NativeRegExp
             input_end_pointer.name(),
             current_character.name(),
             current_position.name(),
             backtrack_stack_pointer.name(),
             temp0.name(),
             temp1.name(),
             temp2.name());
 
-    savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
+    // Determine the non-volatile registers which might be modified by jitcode.
+    for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) {
+        Register reg = *iter;
+        if (!regs.has(reg))
+            savedNonVolatileRegisters.add(reg);
+    }
+
+#if defined(JS_CODEGEN_ARM)
+    // ARM additionally requires that the link register be saved.
+    savedNonVolatileRegisters.add(Register::FromCode(Registers::lr));
+#elif defined(JS_CODEGEN_MIPS)
+    savedNonVolatileRegisters.add(Register::FromCode(Registers::ra));
+#endif
 
     masm.jump(&entry_label_);
     masm.bind(&start_label_);
 }
 
 #define SPEW_PREFIX JitSpew_Codegen, "!!! "
 
 // The signature of the code which this generates is:
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1766,17 +1766,17 @@ irregexp::ExecuteCode(JSContext *cx, jit
     typedef void (*RegExpCodeSignature)(InputOutputData *);
 
     InputOutputData data(chars, chars + length, start, matches);
 
     RegExpCodeSignature function = reinterpret_cast<RegExpCodeSignature>(codeBlock->raw());
 
     {
         JS::AutoSuppressGCAnalysis nogc;
-        CALL_GENERATED_1(function, &data);
+        CALL_GENERATED_REGEXP(function, &data);
     }
 
     return (RegExpRunStatus) data.result;
 }
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext *cx, jit::JitCode *codeBlock, const Latin1Char *chars, size_t start,
                       size_t length, MatchPairs *matches);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1356,17 +1356,17 @@ bool
 BaselineCompiler::emit_JSOP_SYMBOL()
 {
     unsigned which = GET_UINT8(pc);
     JS::Symbol *sym = cx->runtime()->wellKnownSymbols->get(which);
     frame.push(SymbolValue(sym));
     return true;
 }
 
-typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
+typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 bool
 BaselineCompiler::emit_JSOP_OBJECT()
 {
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1937,17 +1937,17 @@ CodeGenerator::visitTableSwitchV(LTableS
     masm.bind(&unboxInt);
     masm.unboxInt32(value, index);
 
     masm.bind(&isInt);
 
     emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
 }
 
-typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
+typedef NativeObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleNativeObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 void
 CodeGenerator::visitCloneLiteral(LCloneLiteral *lir)
 {
     pushArg(ImmWord(js::MaybeSingletonObject));
     pushArg(ToRegister(lir->getObjectLiteral()));
@@ -4378,19 +4378,19 @@ CodeGenerator::visitNewObject(LNewObject
     if (lir->mir()->shouldUseVM()) {
         visitNewObjectVMCall(lir);
         return;
     }
 
     OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
     addOutOfLineCode(ool, lir->mir());
 
-    bool initContents = ShouldInitFixedSlots(lir, templateObject);
+    bool initFixedSlots = ShouldInitFixedSlots(lir, templateObject);
     masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry(),
-                        initContents);
+                        initFixedSlots);
 
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
 {
     visitNewObjectVMCall(ool->lir());
@@ -4555,19 +4555,19 @@ CodeGenerator::visitNewDeclEnvObject(LNe
     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));
 
-    bool initContents = ShouldInitFixedSlots(lir, templateObj);
+    bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
     masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
-                        initContents);
+                        initFixedSlots);
 
     masm.bind(ool->rejoin());
 }
 
 typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleObjectGroup, uint32_t);
 static const VMFunction NewCallObjectInfo =
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
@@ -4583,19 +4583,19 @@ CodeGenerator::visitNewCallObject(LNewCa
     uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin();
     OutOfLineCode *ool = oolCallVM(NewCallObjectInfo, lir,
                                    (ArgList(), ImmGCPtr(templateObj->lastProperty()),
                                                ImmGCPtr(templateObj->group()),
                                                Imm32(lexicalBegin)),
                                    StoreRegisterTo(objReg));
 
     // Inline call object creation, using the OOL path only for tricky cases.
-    bool initContents = ShouldInitFixedSlots(lir, templateObj);
+    bool initFixedSlots = ShouldInitFixedSlots(lir, templateObj);
     masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, ool->entry(),
-                        initContents);
+                        initFixedSlots);
 
     masm.bind(ool->rejoin());
 }
 
 typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape, uint32_t);
 static const VMFunction NewSingletonCallObjectInfo =
     FunctionInfo<NewSingletonCallObjectFn>(NewSingletonCallObject);
 
@@ -4802,19 +4802,19 @@ CodeGenerator::visitCreateThisWithTempla
                                    StoreRegisterTo(objReg));
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
     masm.newGCThing(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
 
     // Initialize based on the templateObject.
     masm.bind(ool->rejoin());
 
-    bool initContents = !templateObject->is<PlainObject>() ||
+    bool initFixedSlots = !templateObject->is<PlainObject>() ||
                           ShouldInitFixedSlots(lir, &templateObject->as<PlainObject>());
-    masm.initGCThing(objReg, tempReg, templateObject, initContents);
+    masm.initGCThing(objReg, tempReg, templateObject, initFixedSlots);
 }
 
 typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, JitFrameLayout *frame, HandleObject);
 static const VMFunction NewIonArgumentsObjectInfo =
     FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
 
 void
 CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject *lir)
--- a/js/src/jit/JitCommon.h
+++ b/js/src/jit/JitCommon.h
@@ -16,28 +16,29 @@
 #endif
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
 // Call into cross-jitted code by following the ABI of the simulated architecture.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7)     \
     (js::jit::Simulator::Current()->call(                              \
         JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 8, p0, p1, p2, p3, p4, p5, p6, p7) & 0xffffffff)
 
-#define CALL_GENERATED_1(entry, p0)                     \
-    (js::jit::Simulator::Current()->call(               \
-        JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 1, p0) & 0xffffffff)
+#define CALL_GENERATED_REGEXP(entry, p0)                 \
+    js::jit::Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 1, p0)
 
-#define CALL_GENERATED_2(entry, p0, p1)                                 \
-    (js::jit::Simulator::Current()->call(                               \
-        JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 2, p0, p1) & 0xffffffff)
+#define CALL_GENERATED_ASMJS(entry, p0, p1)              \
+    (Simulator::Current()->call(JS_FUNC_TO_DATA_PTR(uint8_t *, entry), 2, p0, p1) & 0xffffffff)
 
 #else
 
 // Call into jitted code by following the ABI of the native architecture.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7)   \
   entry(p0, p1, p2, p3, p4, p5, p6, p7)
 
-#define CALL_GENERATED_1(entry, p0)      entry(p0)
-#define CALL_GENERATED_2(entry, p0, p1)  entry(p0, p1)
+#define CALL_GENERATED_REGEXP(entry, p0)                             \
+  entry(p0)
+
+#define CALL_GENERATED_ASMJS(entry, p0, p1)                          \
+  entry(p0, p1)
 
 #endif
 
 #endif // jit_JitCommon_h
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1034,34 +1034,34 @@ MacroAssembler::newGCThing(Register resu
     size_t ndynamic = 0;
     if (templateObj->isNative())
         ndynamic = templateObj->as<NativeObject>().numDynamicSlots();
     allocateObject(result, temp, allocKind, ndynamic, initialHeap, fail);
 }
 
 void
 MacroAssembler::createGCObject(Register obj, Register temp, JSObject *templateObj,
-                               gc::InitialHeap initialHeap, Label *fail, bool initContents)
+                               gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots)
 {
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
     MOZ_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
 
     uint32_t nDynamicSlots = 0;
     if (templateObj->isNative()) {
         nDynamicSlots = templateObj->as<NativeObject>().numDynamicSlots();
 
         // Arrays with copy on write elements do not need fixed space for an
         // elements header. The template object, which owns the original
         // elements, might have another allocation kind.
         if (templateObj->as<NativeObject>().denseElementsAreCopyOnWrite())
             allocKind = gc::FINALIZE_OBJECT0_BACKGROUND;
     }
 
     allocateObject(obj, temp, allocKind, nDynamicSlots, initialHeap, fail);
-    initGCThing(obj, temp, templateObj, initContents);
+    initGCThing(obj, temp, templateObj, initFixedSlots);
 }
 
 
 // Inlined equivalent of gc::AllocateNonObject, without failure case handling.
 // Non-object allocation does not need to worry about slots, so can take a
 // simpler path.
 void
 MacroAssembler::allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail)
@@ -1151,17 +1151,17 @@ FindStartOfUndefinedAndUninitializedSlot
             return;
         }
     }
     *startOfUndefined = 0;
 }
 
 void
 MacroAssembler::initGCSlots(Register obj, Register temp, NativeObject *templateObj,
-                            bool initContents)
+                            bool initFixedSlots)
 {
     // Slots of non-array objects are required to be initialized.
     // Use the values currently in the template object.
     uint32_t nslots = templateObj->lastProperty()->slotSpan(templateObj->getClass());
     if (nslots == 0)
         return;
 
     uint32_t nfixed = templateObj->numUsedFixedSlots();
@@ -1186,17 +1186,17 @@ MacroAssembler::initGCSlots(Register obj
     MOZ_ASSERT(startOfUndefined <= nfixed); // Reserved slots must be fixed.
     MOZ_ASSERT_IF(startOfUndefined != nfixed, startOfUndefined <= startOfUninitialized);
     MOZ_ASSERT_IF(!templateObj->is<CallObject>(), startOfUninitialized == nslots);
 
     // Copy over any preserved reserved slots.
     copySlotsFromTemplate(obj, templateObj, 0, startOfUndefined);
 
     // Fill the rest of the fixed slots with undefined and uninitialized.
-    if (initContents) {
+    if (initFixedSlots) {
         fillSlotsWithUndefined(Address(obj, NativeObject::getFixedSlotOffset(startOfUndefined)), temp,
                                startOfUndefined, Min(startOfUninitialized, nfixed));
         size_t offset = NativeObject::getFixedSlotOffset(startOfUninitialized);
         fillSlotsWithUninitialized(Address(obj, offset), temp, startOfUninitialized, nfixed);
     }
 
     if (ndynamic) {
         // We are short one register to do this elegantly. Borrow the obj
@@ -1212,17 +1212,17 @@ MacroAssembler::initGCSlots(Register obj
                                    nslots - startOfUninitialized);
 
         pop(obj);
     }
 }
 
 void
 MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj,
-                            bool initContents)
+                            bool initFixedSlots)
 {
     // Fast initialization of an empty object returned by allocateObject().
 
     storePtr(ImmGCPtr(templateObj->group()), Address(obj, JSObject::offsetOfGroup()));
 
     if (Shape *shape = templateObj->maybeShape())
         storePtr(ImmGCPtr(shape), Address(obj, JSObject::offsetOfShape()));
 
@@ -1254,17 +1254,17 @@ MacroAssembler::initGCThing(Register obj
             store32(Imm32(ntemplate->shouldConvertDoubleElements()
                           ? ObjectElements::CONVERT_DOUBLE_ELEMENTS
                           : 0),
                     Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
             MOZ_ASSERT(!ntemplate->hasPrivate());
         } else {
             storePtr(ImmPtr(emptyObjectElements), Address(obj, NativeObject::offsetOfElements()));
 
-            initGCSlots(obj, temp, ntemplate, initContents);
+            initGCSlots(obj, temp, ntemplate, initFixedSlots);
 
             if (ntemplate->hasPrivate()) {
                 uint32_t nfixed = ntemplate->numFixedSlots();
                 storePtr(ImmPtr(ntemplate->getPrivate()),
                          Address(obj, NativeObject::getPrivateDataOffset(nfixed)));
             }
         }
     } else if (templateObj->is<InlineTypedObject>()) {
@@ -1276,20 +1276,36 @@ MacroAssembler::initGCThing(Register obj
         while (nbytes) {
             uintptr_t value = *(uintptr_t *)(memory + offset);
             storePtr(ImmWord(value),
                      Address(obj, InlineTypedObject::offsetOfDataStart() + offset));
             nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
             offset += sizeof(uintptr_t);
         }
     } else if (templateObj->is<UnboxedPlainObject>()) {
+        const UnboxedLayout &layout = templateObj->as<UnboxedPlainObject>().layout();
+
         storePtr(ImmWord(0), Address(obj, JSObject::offsetOfShape()));
 
-        if (initContents)
-            initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
+        // Initialize reference fields of the object, per UnboxedPlainObject::create.
+        if (const int32_t *list = layout.traceList()) {
+            while (*list != -1) {
+                storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
+                         Address(obj, UnboxedPlainObject::offsetOfData() + *list));
+                list++;
+            }
+            list++;
+            while (*list != -1) {
+                storePtr(ImmWord(0),
+                         Address(obj, UnboxedPlainObject::offsetOfData() + *list));
+                list++;
+            }
+            // Unboxed objects don't have Values to initialize.
+            MOZ_ASSERT(*(list + 1) == -1);
+        }
     } else {
         MOZ_CRASH("Unknown object");
     }
 
 #ifdef JS_GC_TRACE
     RegisterSet regs = RegisterSet::Volatile();
     PushRegsInMask(regs);
     regs.takeUnchecked(obj);
@@ -1301,39 +1317,16 @@ MacroAssembler::initGCThing(Register obj
     passABIArg(temp);
     callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::gc::TraceCreateObject));
 
     PopRegsInMask(RegisterSet::Volatile());
 #endif
 }
 
 void
-MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject *templateObject)
-{
-    const UnboxedLayout &layout = templateObject->layout();
-
-    // Initialize reference fields of the object, per UnboxedPlainObject::create.
-    if (const int32_t *list = layout.traceList()) {
-        while (*list != -1) {
-            storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
-                     Address(object, UnboxedPlainObject::offsetOfData() + *list));
-            list++;
-        }
-        list++;
-        while (*list != -1) {
-            storePtr(ImmWord(0),
-                     Address(object, UnboxedPlainObject::offsetOfData() + *list));
-            list++;
-        }
-        // Unboxed objects don't have Values to initialize.
-        MOZ_ASSERT(*(list + 1) == -1);
-    }
-}
-
-void
 MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
                                Label *fail)
 {
     MOZ_ASSERT(IsEqualityOp(op));
 
     Label done;
     Label notPointerEqual;
     // Fast path for identical strings.
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -25,17 +25,16 @@
 # error "Unknown architecture!"
 #endif
 #include "jit/AtomicOp.h"
 #include "jit/IonInstrumentation.h"
 #include "jit/JitCompartment.h"
 #include "jit/VMFunctions.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
-#include "vm/UnboxedObject.h"
 
 #ifdef IS_LITTLE_ENDIAN
 #define IMM32_16ADJ(X) X << 16
 #else
 #define IMM32_16ADJ(X) X
 #endif
 
 namespace js {
@@ -825,30 +824,28 @@ class MacroAssembler : public MacroAssem
                         uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label *fail);
     void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label *fail);
     void copySlotsFromTemplate(Register obj, const NativeObject *templateObj,
                                uint32_t start, uint32_t end);
     void fillSlotsWithConstantValue(Address addr, Register temp, uint32_t start, uint32_t end,
                                     const Value &v);
     void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
     void fillSlotsWithUninitialized(Address addr, Register temp, uint32_t start, uint32_t end);
-    void initGCSlots(Register obj, Register temp, NativeObject *templateObj, bool initContents);
+    void initGCSlots(Register obj, Register temp, NativeObject *templateObj, bool initFixedSlots);
 
   public:
     void callMallocStub(size_t nbytes, Register result, Label *fail);
     void callFreeStub(Register slots);
     void createGCObject(Register result, Register temp, JSObject *templateObj,
-                        gc::InitialHeap initialHeap, Label *fail, bool initContents = true);
+                        gc::InitialHeap initialHeap, Label *fail, bool initFixedSlots = true);
 
     void newGCThing(Register result, Register temp, JSObject *templateObj,
                      gc::InitialHeap initialHeap, Label *fail);
     void initGCThing(Register obj, Register temp, JSObject *templateObj,
-                     bool initContents = true);
-
-    void initUnboxedObjectContents(Register object, UnboxedPlainObject *templateObject);
+                     bool initFixedSlots = true);
 
     void newGCString(Register result, Register temp, Label *fail);
     void newGCFatInlineString(Register result, Register temp, Label *fail);
 
     // Compares two strings for equality based on the JSOP.
     // This checks for identical pointers, atoms and length and fails for everything else.
     void compareStrings(JSOp op, Register left, Register right, Register result,
                         Label *fail);
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -884,36 +884,12 @@ class ABIArg
     Register gpr() const { MOZ_ASSERT(kind() == GPR); return Register::FromCode(u.gpr_); }
     FloatRegister fpu() const { MOZ_ASSERT(kind() == FPU); return FloatRegister::FromCode(u.fpu_); }
     uint32_t offsetFromArgBase() const { MOZ_ASSERT(kind() == Stack); return u.offset_; }
 
     bool argInRegister() const { return kind() != Stack; }
     AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); }
 };
 
-// Get the set of registers which should be saved by a block of code which
-// clobbers all registers besides |unused|, but does not clobber floating point
-// registers.
-inline GeneralRegisterSet
-SavedNonVolatileRegisters(GeneralRegisterSet unused)
-{
-    GeneralRegisterSet result;
-
-    for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) {
-        Register reg = *iter;
-        if (!unused.has(reg))
-            result.add(reg);
-    }
-
-    // ARM and MIPS require an additional register to be saved, if calls can be made.
-#if defined(JS_CODEGEN_ARM)
-    result.add(Register::FromCode(Registers::lr));
-#elif defined(JS_CODEGEN_MIPS)
-    result.add(Register::FromCode(Registers::ra));
-#endif
-
-    return result;
-}
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_RegisterSets_h */
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -117,17 +117,16 @@ class AutoVectorRooter : protected AutoG
 
     typedef T ElementType;
     typedef typename VectorImpl::Range Range;
 
     size_t length() const { return vector.length(); }
     bool empty() const { return vector.empty(); }
 
     bool append(const T &v) { return vector.append(v); }
-    bool appendN(const T &v, size_t len) { return vector.appendN(v, len); }
     bool append(const T *ptr, size_t len) { return vector.append(ptr, len); }
     bool appendAll(const AutoVectorRooter<T> &other) {
         return vector.appendAll(other.vector);
     }
 
     bool insert(T *p, const T &val) { return vector.insert(p, val); }
 
     /* For use when space has already been reserved. */
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -58,17 +58,17 @@ ValueToIdPure(const Value &v, jsid *id)
         return false;
 
     *id = AtomToId(&v.toString()->asAtom());
     return true;
 }
 
 template <AllowGC allowGC>
 inline bool
-ValueToId(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
+ValueToId(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v,
           typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
 {
     int32_t i;
     if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
         idp.set(INT_TO_JSID(i));
         return true;
     }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1432,60 +1432,59 @@ js::NewObjectWithClassProtoCommon(Exclus
         cache.fillGlobal(entry, clasp, &parent->as<GlobalObject>(), allocKind,
                          &obj->as<NativeObject>());
     }
 
     return obj;
 }
 
 static bool
-NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
+NewObjectWithGroupIsCachable(JSContext *cx, HandleObjectGroup group, HandleObject parent,
                              NewObjectKind newKind)
 {
     return group->proto().isObject() &&
            parent == group->proto().toObject()->getParent() &&
            newKind == GenericObject &&
            group->clasp()->isNative() &&
            (!group->newScript() || group->newScript()->analyzed()) &&
-           !cx->compartment()->hasObjectMetadataCallback() &&
-           cx->isJSContext();
+           !cx->compartment()->hasObjectMetadataCallback();
 }
 
 /*
  * Create a plain object with the specified group. This bypasses getNewGroup to
  * avoid losing creation site information for objects made by scripted 'new'.
  */
 JSObject *
-js::NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
+js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObject parent,
                              gc::AllocKind allocKind, NewObjectKind newKind)
 {
     MOZ_ASSERT(parent);
 
     MOZ_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST);
     if (CanBeFinalizedInBackground(allocKind, group->clasp()))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     bool isCachable = NewObjectWithGroupIsCachable(cx, group, parent, newKind);
     if (isCachable) {
-        NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache &cache = cx->runtime()->newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         if (cache.lookupGroup(group, allocKind, &entry)) {
-            JSObject *obj = cache.newObjectFromHit(cx->asJSContext(), entry,
+            JSObject *obj = cache.newObjectFromHit(cx, entry,
                                                    GetInitialHeap(newKind, group->clasp()));
             if (obj)
                 return obj;
         }
     }
 
     JSObject *obj = NewObject(cx, group, parent, allocKind, newKind);
     if (!obj)
         return nullptr;
 
     if (isCachable && !obj->as<NativeObject>().hasDynamicSlots()) {
-        NewObjectCache &cache = cx->asJSContext()->runtime()->newObjectCache;
+        NewObjectCache &cache = cx->runtime()->newObjectCache;
         NewObjectCache::EntryIndex entry = -1;
         cache.lookupGroup(group, allocKind, &entry);
         cache.fillGroup(entry, group, allocKind, &obj->as<NativeObject>());
     }
 
     return obj;
 }
 
@@ -1780,172 +1779,100 @@ js::CloneObject(JSContext *cx, HandleObj
 
         if (!CopyProxyObject(cx, obj.as<ProxyObject>(), clone.as<ProxyObject>()))
             return nullptr;
     }
 
     return clone;
 }
 
-static bool
-GetScriptArrayObjectElements(JSContext *cx, HandleArrayObject obj, AutoValueVector &values)
-{
-    MOZ_ASSERT(!obj->isSingleton());
-
-    if (obj->nonProxyIsExtensible()) {
-        MOZ_ASSERT(obj->slotSpan() == 0);
-
-        if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), obj->getDenseInitializedLength()))
-            return false;
-
-        for (size_t i = 0; i < obj->getDenseInitializedLength(); i++)
-            values[i].set(obj->getDenseElement(i));
-    } else {
-        // Call site objects are frozen before they escape to script, which
-        // converts their dense elements into data properties.
-
-        for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
-            Shape &shape = r.front();
-            if (shape.propid() == NameToId(cx->names().length))
-                continue;
-            MOZ_ASSERT(shape.isDataDescriptor());
-
-            // The 'raw' property is added before freezing call site objects.
-            // After an XDR or deep clone the script object will no longer be
-            // frozen, and the two objects will be connected again the first
-            // time the JSOP_CALLSITEOBJ executes.
-            if (shape.propid() == NameToId(cx->names().raw))
-                continue;
-
-            uint32_t index = JSID_TO_INT(shape.propid());
-            while (index >= values.length()) {
-                if (!values.append(MagicValue(JS_ELEMENTS_HOLE)))
-                    return false;
-            }
-
-            values[index].set(obj->getSlot(shape.slot()));
-        }
-    }
-
-    return true;
-}
-
-static bool
-GetScriptPlainObjectProperties(JSContext *cx, HandleObject obj, AutoIdValueVector &properties)
-{
-    if (obj->is<PlainObject>()) {
-        PlainObject *nobj = &obj->as<PlainObject>();
-
-        if (!properties.appendN(IdValuePair(), nobj->slotSpan()))
-            return false;
-
-        for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) {
-            Shape &shape = r.front();
-            MOZ_ASSERT(shape.isDataDescriptor());
-            uint32_t slot = shape.slot();
-            properties[slot].get().id = shape.propid();
-            properties[slot].get().value = nobj->getSlot(slot);
-        }
-
-        for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) {
-            Value v = nobj->getDenseElement(i);
-            if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v)))
-                return false;
-        }
-
-        return true;
-    }
-
-    if (obj->is<UnboxedPlainObject>()) {
-        UnboxedPlainObject *nobj = &obj->as<UnboxedPlainObject>();
-
-        const UnboxedLayout &layout = nobj->layout();
-        if (!properties.appendN(IdValuePair(), layout.properties().length()))
-            return false;
-
-        for (size_t i = 0; i < layout.properties().length(); i++) {
-            const UnboxedLayout::Property &property = layout.properties()[i];
-            properties[i].get().id = NameToId(property.name);
-            properties[i].get().value = nobj->getValue(property);
-        }
-
-        return true;
-    }
-
-    MOZ_CRASH("Bad object kind");
-}
-
-static bool
-DeepCloneValue(JSContext *cx, Value *vp, NewObjectKind newKind)
-{
-    if (vp->isObject()) {
-        RootedObject obj(cx, &vp->toObject());
-        obj = DeepCloneObjectLiteral(cx, obj, newKind);
-        if (!obj)
-            return false;
-        vp->setObject(*obj);
-    }
-    return true;
-}
-
-JSObject *
-js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind)
+NativeObject *
+js::DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind)
 {
     /* NB: Keep this in sync with XDRObjectLiteral. */
     MOZ_ASSERT_IF(obj->isSingleton(),
                   JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
-    MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() || obj->is<ArrayObject>());
-    MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
-    MOZ_ASSERT(newKind != SingletonObject);
+    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);
 
     if (obj->is<ArrayObject>()) {
-        HandleArrayObject aobj = obj.as<ArrayObject>();
-
-        AutoValueVector values(cx);
-        if (!GetScriptArrayObjectElements(cx, aobj, values))
-            return nullptr;
-
-        // Deep clone any elements.
-        uint32_t initialized = aobj->getDenseInitializedLength();
-        for (uint32_t i = 0; i < initialized; ++i) {
-            if (!DeepCloneValue(cx, values[i].address(), newKind))
-                return nullptr;
-        }
-
-        RootedArrayObject clone(cx, NewDenseUnallocatedArray(cx, aobj->length(),
-                                                             NullPtr(), newKind));
-        if (!clone || !clone->ensureElements(cx, values.length()))
+        clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), NullPtr(), newKind);
+    } else {
+        // Object literals are tenured by default as holded by the JSScript.
+        MOZ_ASSERT(obj->isTenured());
+        AllocKind kind = obj->asTenured().getAllocKind();
+        RootedObjectGroup group(cx, obj->getGroup(cx));
+        if (!group)
             return nullptr;
-
-        clone->setDenseInitializedLength(values.length());
-        clone->initDenseElements(0, values.begin(), values.length());
-
-        if (aobj->denseElementsAreCopyOnWrite()) {
-            if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
+        RootedObject proto(cx, group->proto().toObject());
+        obj->assertParentIs(cx->global());
+        clone = NewNativeObjectWithGivenProto(cx, &PlainObject::class_, proto,
+                                              kind, newKind);
+    }
+
+    // Allocate the same number of slots.
+    if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
+        return nullptr;
+
+    // Recursive copy of dense element.
+    uint32_t initialized = obj->getDenseInitializedLength();
+    for (uint32_t i = 0; i < initialized; ++i) {
+        v = obj->getDenseElement(i);
+        if (v.isObject()) {
+            deepObj = &v.toObject().as<NativeObject>();
+            deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
+            if (!deepObj) {
+                JS_ReportOutOfMemory(cx);
                 return nullptr;
-        } else {
-            ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>());
+            }
+            v.setObject(*deepObj);
         }
-
-        return clone;
+        clone->setDenseInitializedLength(i + 1);
+        clone->initDenseElement(i, v);
     }
 
-    AutoIdValueVector properties(cx);
-    if (!GetScriptPlainObjectProperties(cx, obj, properties))
+    MOZ_ASSERT(obj->compartment() == clone->compartment());
+    MOZ_ASSERT(!obj->hasPrivate());
+    RootedShape shape(cx, obj->lastProperty());
+    size_t span = shape->slotSpan();
+    if (!clone->setLastProperty(cx, shape))
         return nullptr;
-
-    for (size_t i = 0; i < properties.length(); i++) {
-        if (!DeepCloneValue(cx, &properties[i].get().value, newKind))
+    for (size_t i = 0; i < span; i++) {
+        v = obj->getSlot(i);
+        if (v.isObject()) {
+            deepObj = &v.toObject().as<NativeObject>();
+            deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
+            if (!deepObj)
+                return nullptr;
+            v.setObject(*deepObj);
+        }
+        clone->setSlot(i, v);
+    }
+
+    if (obj->isSingleton()) {
+        if (!JSObject::setSingleton(cx, clone))
+            return nullptr;
+    } else if (obj->is<ArrayObject>()) {
+        ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>());
+    } else {
+        ObjectGroup::fixPlainObjectGroup(cx, &clone->as<PlainObject>());
+    }
+
+    if (obj->is<ArrayObject>() && obj->denseElementsAreCopyOnWrite()) {
+        if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
             return nullptr;
     }
 
-    if (obj->isSingleton())
-        newKind = SingletonObject;
-
-    return ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind);
+    return clone;
 }
 
 static bool
 InitializePropertiesFromCompatibleNativeObject(JSContext *cx,
                                                HandleNativeObject dst,
                                                HandleNativeObject src)
 {
     assertSameCompartment(cx, src, dst);
@@ -1992,161 +1919,253 @@ JS_InitializePropertiesFromCompatibleNat
 {
     return InitializePropertiesFromCompatibleNativeObject(cx,
                                                           dst.as<NativeObject>(),
                                                           src.as<NativeObject>());
 }
 
 template<XDRMode mode>
 bool
-js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
+js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj)
 {
     /* NB: Keep this in sync with DeepCloneObjectLiteral. */
 
     JSContext *cx = xdr->cx();
     MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
                   JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
 
     // Distinguish between objects and array classes.
     uint32_t isArray = 0;
     {
         if (mode == XDR_ENCODE) {
-            MOZ_ASSERT(obj->is<PlainObject>() ||
-                       obj->is<UnboxedPlainObject>() ||
-                       obj->is<ArrayObject>());
-            isArray = obj->is<ArrayObject>() ? 1 : 0;
+            MOZ_ASSERT(obj->is<PlainObject>() || obj->is<ArrayObject>());
+            isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
         }
 
         if (!xdr->codeUint32(&isArray))
             return false;
     }
 
-    RootedValue tmpValue(cx), tmpIdValue(cx);
-    RootedId tmpId(cx);
-
     if (isArray) {
         uint32_t length;
-        RootedArrayObject aobj(cx);
-
-        if (mode == XDR_ENCODE) {
-            aobj = &obj->as<ArrayObject>();
-            length = aobj->length();
-        }
+
+        if (mode == XDR_ENCODE)
+            length = obj->as<ArrayObject>().length();
 
         if (!xdr->codeUint32(&length))
             return false;
 
-        if (mode == XDR_DECODE) {
-            obj.set(NewDenseUnallocatedArray(cx, length, NullPtr(), TenuredObject));
-            if (!obj)
+        if (mode == XDR_DECODE)
+            obj.set(NewDenseUnallocatedArray(cx, length, NullPtr(), js::MaybeSingletonObject));
+
+    } else {
+        // Code the alloc kind of the object.
+        AllocKind kind;
+        {
+            if (mode == XDR_ENCODE) {
+                MOZ_ASSERT(obj->is<PlainObject>());
+                MOZ_ASSERT(obj->isTenured());
+                kind = obj->asTenured().getAllocKind();
+            }
+
+            if (!xdr->codeEnum32(&kind))
                 return false;
-            aobj = &obj->as<ArrayObject>();
+
+            if (mode == XDR_DECODE) {
+                obj.set(NewBuiltinClassInstance<PlainObject>(cx, kind, MaybeSingletonObject));
+                if (!obj)
+                    return false;
+            }
         }
-
-        AutoValueVector values(cx);
-        if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, aobj, values))
-            return nullptr;
-
-        uint32_t initialized;
-        {
-            if (mode == XDR_ENCODE)
-                initialized = values.length();
-            if (!xdr->codeUint32(&initialized))
+    }
+
+    {
+        uint32_t capacity;
+        if (mode == XDR_ENCODE)
+            capacity = obj->getDenseCapacity();
+        if (!xdr->codeUint32(&capacity))
+            return false;
+        if (mode == XDR_DECODE) {
+            if (!obj->ensureElements(cx, capacity)) {
+                JS_ReportOutOfMemory(cx);
                 return false;
-            if (mode == XDR_DECODE) {
-                if (initialized) {
-                    if (!aobj->ensureElements(cx, initialized))
-                        return false;
-                }
             }
         }
-
-        // Recursively copy dense elements.
+    }
+
+    uint32_t initialized;
+    {
+        if (mode == XDR_ENCODE)
+            initialized = obj->getDenseInitializedLength();
+        if (!xdr->codeUint32(&initialized))
+            return false;
+        if (mode == XDR_DECODE) {
+            if (initialized)
+                obj->setDenseInitializedLength(initialized);
+        }
+    }
+
+    RootedValue tmpValue(cx);
+
+    // Recursively copy dense elements.
+    {
         for (unsigned i = 0; i < initialized; i++) {
             if (mode == XDR_ENCODE)
-                tmpValue = values[i];
+                tmpValue = obj->getDenseElement(i);
+
+            if (!xdr->codeConstValue(&tmpValue))
+                return false;
+
+            if (mode == XDR_DECODE)
+                obj->initDenseElement(i, tmpValue);
+        }
+    }
+
+    MOZ_ASSERT(!obj->hasPrivate());
+    RootedShape shape(cx, obj->lastProperty());
+
+    // Code the number of slots in the vector.
+    unsigned nslot = 0;
+
+    // Code ids of the object in order. As opposed to DeepCloneObjectLiteral we
+    // cannot just re-use the shape of the original bytecode value and we have
+    // to write down the shape as well as the corresponding values.  Ideally we
+    // would have a mechanism to serialize the shape too.
+    js::AutoIdVector ids(cx);
+    {
+        if (mode == XDR_ENCODE && !shape->isEmptyShape()) {
+            nslot = shape->slotSpan();
+            if (!ids.reserve(nslot))
+                return false;
+
+            for (unsigned i = 0; i < nslot; i++)
+                ids.infallibleAppend(JSID_VOID);
+
+            for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) {
+                // If we have reached the native property of the array class, we
+                // exit as the remaining would only be reserved slots.
+                if (!it.front().hasSlot()) {
+                    MOZ_ASSERT(isArray);
+                    break;
+                }
+
+                MOZ_ASSERT(it.front().hasDefaultGetter());
+                ids[it.front().slot()].set(it.front().propid());
+            }
+        }
+
+        if (!xdr->codeUint32(&nslot))
+            return false;
+
+        RootedAtom atom(cx);
+        RootedId id(cx);
+        uint32_t idType = 0;
+        for (unsigned i = 0; i < nslot; i++) {
+            if (mode == XDR_ENCODE) {
+                id = ids[i];
+                if (JSID_IS_INT(id))
+                    idType = JSID_TYPE_INT;
+                else if (JSID_IS_ATOM(id))
+                    idType = JSID_TYPE_STRING;
+                else
+                    MOZ_CRASH("Symbol property is not yet supported by XDR.");
+
+                tmpValue = obj->getSlot(i);
+            }
+
+            if (!xdr->codeUint32(&idType))
+                return false;
+
+            if (idType == JSID_TYPE_STRING) {
+                if (mode == XDR_ENCODE)
+                    atom = JSID_TO_ATOM(id);
+                if (!XDRAtom(xdr, &atom))
+                    return false;
+                if (mode == XDR_DECODE)
+                    id = AtomToId(atom);
+            } else {
+                MOZ_ASSERT(idType == JSID_TYPE_INT);
+                uint32_t indexVal;
+                if (mode == XDR_ENCODE)
+                    indexVal = uint32_t(JSID_TO_INT(id));
+                if (!xdr->codeUint32(&indexVal))
+                    return false;
+                if (mode == XDR_DECODE)
+                    id = INT_TO_JSID(int32_t(indexVal));
+            }
 
             if (!xdr->codeConstValue(&tmpValue))
                 return false;
 
             if (mode == XDR_DECODE) {
-                aobj->setDenseInitializedLength(i + 1);
-                aobj->initDenseElement(i, tmpValue);
-            }
-        }
-
-        uint32_t copyOnWrite;
-        if (mode == XDR_ENCODE)
-            copyOnWrite = aobj->denseElementsAreCopyOnWrite();
-        if (!xdr->codeUint32(&copyOnWrite))
-            return false;
-
-        if (mode == XDR_DECODE) {
-            if (copyOnWrite) {
-                if (!ObjectElements::MakeElementsCopyOnWrite(cx, aobj))
+                if (!NativeDefineProperty(cx, obj, id, tmpValue, nullptr, nullptr,
+                                          JSPROP_ENUMERATE))
+                {
                     return false;
-            } else {
-                ObjectGroup::fixArrayGroup(cx, aobj);
+                }
             }
         }
 
-        return true;
+        MOZ_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
     }
 
-    // Code the properties in the object.
-    AutoIdValueVector properties(cx);
-    if (mode == XDR_ENCODE && !GetScriptPlainObjectProperties(cx, obj, properties))
-        return false;
-
-    uint32_t nproperties = properties.length();
-    if (!xdr->codeUint32(&nproperties))
-        return false;
-
-    if (mode == XDR_DECODE && !properties.appendN(IdValuePair(), nproperties))
-        return false;
-
-    for (size_t i = 0; i < nproperties; i++) {
-        if (mode == XDR_ENCODE) {
-            tmpIdValue = IdToValue(properties[i].get().id);
-            tmpValue = properties[i].get().value;
-        }
-
-        if (!xdr->codeConstValue(&tmpIdValue) || !xdr->codeConstValue(&tmpValue))
-            return false;
-
-        if (mode == XDR_DECODE) {
-            if (!ValueToId<CanGC>(cx, tmpIdValue, &tmpId))
-                return false;
-            properties[i].get().id = tmpId;
-            properties[i].get().value = tmpValue;
-        }
-    }
-
-    // Code whether the object is a singleton.
-    uint32_t isSingleton;
+    // Fix up types, distinguishing singleton-typed objects.
+    uint32_t isSingletonTyped;
     if (mode == XDR_ENCODE)
-        isSingleton = obj->isSingleton() ? 1 : 0;
-    if (!xdr->codeUint32(&isSingleton))
+        isSingletonTyped = obj->isSingleton() ? 1 : 0;
+    if (!xdr->codeUint32(&isSingletonTyped))
         return false;
 
     if (mode == XDR_DECODE) {
-        NewObjectKind newKind = isSingleton ? SingletonObject : TenuredObject;
-        obj.set(ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), newKind));
-        if (!obj)
+        if (isSingletonTyped) {
+            if (!JSObject::setSingleton(cx, obj))
+                return false;
+        } else if (isArray) {
+            ObjectGroup::fixArrayGroup(cx, &obj->as<ArrayObject>());
+        } else {
+            ObjectGroup::fixPlainObjectGroup(cx, &obj->as<PlainObject>());
+        }
+    }
+
+    {
+        uint32_t frozen;
+        bool extensible;
+        if (mode == XDR_ENCODE) {
+            if (!IsExtensible(cx, obj, &extensible))
+                return false;
+            frozen = extensible ? 0 : 1;
+        }
+        if (!xdr->codeUint32(&frozen))
             return false;
+        if (mode == XDR_DECODE && frozen == 1) {
+            if (!FreezeObject(cx, obj))
+                return false;
+        }
+    }
+
+    if (isArray) {
+        uint32_t copyOnWrite;
+        if (mode == XDR_ENCODE)
+            copyOnWrite = obj->denseElementsAreCopyOnWrite();
+        if (!xdr->codeUint32(&copyOnWrite))
+            return false;
+        if (mode == XDR_DECODE && copyOnWrite) {
+            if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj))
+                return false;
+        }
     }
 
     return true;
 }
 
 template bool
-js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
+js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleNativeObject obj);
 
 template bool
-js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
+js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleNativeObject obj);
 
 JSObject *
 js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
 {
     if (srcObj->is<PlainObject>()) {
         AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as<PlainObject>().numFixedSlots()));
         MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind());
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1112,16 +1112,46 @@ extern const char js_propertyIsEnumerabl
 extern const char js_defineGetter_str[];
 extern const char js_defineSetter_str[];
 extern const char js_lookupGetter_str[];
 extern const char js_lookupSetter_str[];
 #endif
 
 namespace js {
 
+/*
+ * The NewObjectKind allows an allocation site to specify the type properties
+ * and lifetime requirements that must be fixed at allocation time.
+ */
+enum NewObjectKind {
+    /* This is the default. Most objects are generic. */
+    GenericObject,
+
+    /*
+     * Singleton objects are treated specially by the type system. This flag
+     * ensures that the new object is automatically set up correctly as a
+     * singleton and is allocated in the correct heap.
+     */
+    SingletonObject,
+
+    /*
+     * Objects which may be marked as a singleton after allocation must still
+     * be allocated on the correct heap, but are not automatically setup as a
+     * singleton after allocation.
+     */
+    MaybeSingletonObject,
+
+    /*
+     * Objects which will not benefit from being allocated in the nursery
+     * (e.g. because they are known to have a long lifetime) may be allocated
+     * with this kind to place them immediately into the tenured generation.
+     */
+    TenuredObject
+};
+
 inline gc::InitialHeap
 GetInitialHeap(NewObjectKind newKind, const Class *clasp)
 {
     if (newKind != GenericObject)
         return gc::TenuredHeap;
     if (clasp->finalize && !(clasp->flags & JSCLASS_FINALIZE_FROM_NURSERY))
         return gc::TenuredHeap;
     return gc::DefaultHeap;
@@ -1139,18 +1169,18 @@ CreateThisForFunction(JSContext *cx, js:
 
 // 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);
 
-extern JSObject *
-DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject);
+extern NativeObject *
+DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind = GenericObject);
 
 extern bool
 DefineProperties(JSContext *cx, HandleObject obj, HandleObject props);
 
 /*
  * Read property descriptors from props, as for Object.defineProperties. See
  * ES5 15.2.3.7 steps 3-5.
  */
@@ -1236,17 +1266,17 @@ ToObjectFromStack(JSContext *cx, HandleV
 {
     if (vp.isObject())
         return &vp.toObject();
     return js::ToObjectSlow(cx, vp, true);
 }
 
 template<XDRMode mode>
 bool
-XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj);
+XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleNativeObject obj);
 
 extern JSObject *
 CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj);
 
 extern void
 GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize);
 
 extern bool
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -734,31 +734,31 @@ NewBuiltinClassInstance(ExclusiveContext
     return obj ? &obj->as<T>() : nullptr;
 }
 
 // Used to optimize calls to (new Object())
 bool
 NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj);
 
 JSObject *
-NewObjectWithGroupCommon(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
+NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObject parent,
                          gc::AllocKind allocKind, NewObjectKind newKind);
 
 template <typename T>
 inline T *
-NewObjectWithGroup(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
+NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent,
                    gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
 {
     JSObject *obj = NewObjectWithGroupCommon(cx, group, parent, allocKind, newKind);
     return obj ? &obj->as<T>() : nullptr;
 }
 
 template <typename T>
 inline T *
-NewObjectWithGroup(ExclusiveContext *cx, HandleObjectGroup group, HandleObject parent,
+NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent,
                    NewObjectKind newKind = GenericObject)
 {
     gc::AllocKind allocKind = gc::GetGCObjectKind(group->clasp());
     return NewObjectWithGroup<T>(cx, group, parent, allocKind, newKind);
 }
 
 /*
  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -246,18 +246,17 @@ class JS_PUBLIC_API(AutoGCRooter)
 
     enum {
         VALARRAY =     -2, /* js::AutoValueArray */
         PARSER =       -3, /* js::frontend::Parser */
         SHAPEVECTOR =  -4, /* js::AutoShapeVector */
         IDARRAY =      -6, /* js::AutoIdArray */
         DESCVECTOR =   -7, /* js::AutoPropDescVector */
         VALVECTOR =   -10, /* js::AutoValueVector */
-        IDVECTOR =    -11, /* js::AutoIdVector */
-        IDVALVECTOR = -12, /* js::AutoIdValueVector */
+        IDVECTOR =    -13, /* js::AutoIdVector */
         OBJVECTOR =   -14, /* js::AutoObjectVector */
         STRINGVECTOR =-15, /* js::AutoStringVector */
         SCRIPTVECTOR =-16, /* js::AutoScriptVector */
         NAMEVECTOR =  -17, /* js::AutoNameVector */
         HASHABLEVALUE=-18, /* js::HashableValue */
         IONMASM =     -19, /* js::jit::MacroAssembler */
         WRAPVECTOR =  -20, /* js::AutoWrapperVector */
         WRAPPER =     -21, /* js::AutoWrapperRooter */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -415,19 +415,19 @@ js::XDRScriptConst(XDRState<mode> *xdr, 
         if (mode == XDR_DECODE)
             vp.set(BooleanValue(false));
         break;
       case SCRIPT_NULL:
         if (mode == XDR_DECODE)
             vp.set(NullValue());
         break;
       case SCRIPT_OBJECT: {
-        RootedObject obj(cx);
+        RootedNativeObject obj(cx);
         if (mode == XDR_ENCODE)
-            obj = &vp.toObject();
+            obj = &vp.toObject().as<NativeObject>();
 
         if (!XDRObjectLiteral(xdr, &obj))
             return false;
 
         if (mode == XDR_DECODE)
             vp.setObject(*obj);
         break;
       }
@@ -525,17 +525,17 @@ XDRRelazificationInfo(XDRState<mode> *xd
 
     return true;
 }
 
 static inline uint32_t
 FindScopeObjectIndex(JSScript *script, NestedScopeObject &scope)
 {
     ObjectArray *objects = script->objects();
-    HeapPtrObject *vector = objects->vector;
+    HeapPtrNativeObject *vector = objects->vector;
     unsigned length = objects->length;
     for (unsigned i = 0; i < length; ++i) {
         if (vector[i] == &scope)
             return i;
     }
 
     MOZ_CRASH("Scope not found");
 }
@@ -897,17 +897,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
     }
 
     /*
      * Here looping from 0-to-length to xdr objects is essential to ensure that
      * all references to enclosing blocks (via FindScopeObjectIndex below) happen
      * after the enclosing block has been XDR'd.
      */
     for (i = 0; i != nobjects; ++i) {
-        HeapPtrObject *objp = &script->objects()->vector[i];
+        HeapPtrNativeObject *objp = &script->objects()->vector[i];
         XDRClassKind classk;
 
         if (mode == XDR_ENCODE) {
             JSObject *obj = *objp;
             if (obj->is<BlockObject>())
                 classk = CK_BlockObject;
             else if (obj->is<StaticWithObject>())
                 classk = CK_WithObject;
@@ -1010,17 +1010,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
             if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
                 return false;
             *objp = tmp;
             break;
           }
 
           case CK_JSObject: {
             /* Code object literal. */
-            RootedObject tmp(cx, *objp);
+            RootedNativeObject tmp(cx, *objp);
             if (!XDRObjectLiteral(xdr, &tmp))
                 return false;
             *objp = tmp;
             break;
           }
 
           default: {
             MOZ_ASSERT(false, "Unknown class kind.");
@@ -2478,23 +2478,23 @@ JSScript::partiallyInit(ExclusiveContext
         MOZ_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0);
         script->consts()->length = nconsts;
         script->consts()->vector = (HeapValue *)cursor;
         cursor += nconsts * sizeof(script->consts()->vector[0]);
     }
 
     if (nobjects != 0) {
         script->objects()->length = nobjects;
-        script->objects()->vector = (HeapPtrObject *)cursor;
+        script->objects()->vector = (HeapPtrNativeObject *)cursor;
         cursor += nobjects * sizeof(script->objects()->vector[0]);
     }
 
     if (nregexps != 0) {
         script->regexps()->length = nregexps;
-        script->regexps()->vector = (HeapPtrObject *)cursor;
+        script->regexps()->vector = (HeapPtrNativeObject *)cursor;
         cursor += nregexps * sizeof(script->regexps()->vector[0]);
     }
 
     if (ntrynotes != 0) {
         script->trynotes()->length = ntrynotes;
         script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor);
         size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
 #ifdef DEBUG
@@ -2980,17 +2980,17 @@ js::CloneScript(JSContext *cx, HandleObj
         InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
     if (!Bindings::clone(cx, bindingsHandle, data, src))
         return nullptr;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
-        HeapPtrObject *vector = src->objects()->vector;
+        HeapPtrNativeObject *vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
             RootedObject obj(cx, vector[i]);
             RootedObject clone(cx);
             if (obj->is<NestedScopeObject>()) {
                 Rooted<NestedScopeObject*> innerBlock(cx, &obj->as<NestedScopeObject>());
 
                 RootedObject enclosingScope(cx);
                 if (NestedScopeObject *enclosingBlock = innerBlock->enclosingNestedScope())
@@ -3035,17 +3035,17 @@ js::CloneScript(JSContext *cx, HandleObj
                 return nullptr;
         }
     }
 
     /* RegExps */
 
     AutoObjectVector regexps(cx);
     for (unsigned i = 0; i < nregexps; i++) {
-        HeapPtrObject *vector = src->regexps()->vector;
+        HeapPtrNativeObject *vector = src->regexps()->vector;
         for (unsigned i = 0; i < nregexps; i++) {
             JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>());
             if (!clone || !regexps.append(clone))
                 return nullptr;
         }
     }
 
     /*
@@ -3125,23 +3125,23 @@ js::CloneScript(JSContext *cx, HandleObj
 
     if (nconsts != 0) {
         HeapValue *vector = Rebase<HeapValue>(dst, src, src->consts()->vector);
         dst->consts()->vector = vector;
         for (unsigned i = 0; i < nconsts; ++i)
             MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
     }
     if (nobjects != 0) {
-        HeapPtrObject *vector = Rebase<HeapPtrObject>(dst, src, src->objects()->vector);
+        HeapPtrNativeObject *vector = Rebase<HeapPtrNativeObject>(dst, src, src->objects()->vector);
         dst->objects()->vector = vector;
         for (unsigned i = 0; i < nobjects; ++i)
             vector[i].init(&objects[i]->as<NativeObject>());
     }
     if (nregexps != 0) {
-        HeapPtrObject *vector = Rebase<HeapPtrObject>(dst, src, src->regexps()->vector);
+        HeapPtrNativeObject *vector = Rebase<HeapPtrNativeObject>(dst, src, src->regexps()->vector);
         dst->regexps()->vector = vector;
         for (unsigned i = 0; i < nregexps; ++i)
             vector[i].init(&regexps[i]->as<NativeObject>());
     }
     if (ntrynotes != 0)
         dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
     if (nblockscopes != 0)
         dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -109,18 +109,18 @@ struct BlockScopeNote {
 };
 
 struct ConstArray {
     js::HeapValue   *vector;    /* array of indexed constant values */
     uint32_t        length;
 };
 
 struct ObjectArray {
-    js::HeapPtrObject *vector;  // Array of indexed objects.
-    uint32_t        length;     // Count of indexed objects.
+    js::HeapPtrNativeObject *vector;  // Array of indexed objects.
+    uint32_t        length;           // Count of indexed objects.
 };
 
 struct TryNoteArray {
     JSTryNote       *vector;    // Array of indexed try notes.
     uint32_t        length;     // Count of indexed try notes.
 };
 
 struct BlockScopeArray {
@@ -1546,28 +1546,28 @@ class JSScript : public js::gc::TenuredC
         return getAtom(index)->asPropertyName();
     }
 
     js::PropertyName *getName(jsbytecode *pc) const {
         MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
         return getAtom(GET_UINT32_INDEX(pc))->asPropertyName();
     }
 
-    JSObject *getObject(size_t index) {
+    js::NativeObject *getObject(size_t index) {
         js::ObjectArray *arr = objects();
         MOZ_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
     size_t innerObjectsStart() {
         // The first object contains the caller if savedCallerFun is used.
         return savedCallerFun() ? 1 : 0;
     }
 
-    JSObject *getObject(jsbytecode *pc) {
+    js::NativeObject *getObject(jsbytecode *pc) {
         MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
         return getObject(GET_UINT32_INDEX(pc));
     }
 
     JSVersion getVersion() const {
         return JSVersion(version);
     }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2776,20 +2776,20 @@ CASE(JSOP_TOSTRING)
 END_CASE(JSOP_TOSTRING)
 
 CASE(JSOP_SYMBOL)
     PUSH_SYMBOL(cx->wellKnownSymbols().get(GET_UINT8(REGS.pc)));
 END_CASE(JSOP_SYMBOL)
 
 CASE(JSOP_OBJECT)
 {
-    RootedObject &ref = rootObject0;
+    RootedNativeObject &ref = rootNativeObject0;
     ref = script->getObject(REGS.pc);
     if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
-        JSObject *obj = DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
+        JSObject *obj = js::DeepCloneObjectLiteral(cx, ref, js::MaybeSingletonObject);
         if (!obj)
             goto error;
         PUSH_OBJECT(*obj);
     } else {
         JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
         PUSH_OBJECT(*ref);
     }
 }
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -572,22 +572,65 @@ JSONParser<CharT>::advanceAfterProperty(
         current++;
         return token(ObjectClose);
     }
 
     error("expected ',' or '}' after property value in object");
     return token(Error);
 }
 
+JSObject *
+JSONParserBase::createFinishedObject(PropertyVector &properties)
+{
+    /*
+     * Look for an existing cached group and shape for objects with this set of
+     * properties.
+     */
+    {
+        JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(),
+                                                    properties.length());
+        if (obj)
+            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());
+    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;
+        value = properties[i].value;
+        if (!NativeDefineProperty(cx, obj, propid, value, nullptr, nullptr, JSPROP_ENUMERATE))
+            return nullptr;
+    }
+
+    /*
+     * Try to assign a new group to the object with type information for its
+     * properties, and update the initializer object group cache with this
+     * object's final shape.
+     */
+    ObjectGroup::fixPlainObjectGroup(cx, obj);
+
+    return obj;
+}
+
 inline bool
 JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector &properties)
 {
     MOZ_ASSERT(&properties == &stack.back().properties());
 
-    JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), properties.length(), GenericObject);
+    JSObject *obj = createFinishedObject(properties);
     if (!obj)
         return false;
 
     vp.setObject(*obj);
     if (!freeProperties.append(&properties))
         return false;
     stack.popBack();
     return true;
--- a/js/src/vm/JSONParser.h
+++ b/js/src/vm/JSONParser.h
@@ -168,16 +168,18 @@ class MOZ_STACK_CLASS JSONParserBase : p
 
     bool finishObject(MutableHandleValue vp, PropertyVector &properties);
     bool finishArray(MutableHandleValue vp, ElementVector &elements);
 
   private:
     friend void AutoGCRooter::trace(JSTracer *trc);
     void trace(JSTracer *trc);
 
+    JSObject *createFinishedObject(PropertyVector &properties);
+
     JSONParserBase(const JSONParserBase &other) = delete;
     void operator=(const JSONParserBase &other) = delete;
 };
 
 template <typename CharT>
 class MOZ_STACK_CLASS JSONParser : public JSONParserBase
 {
   private:
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -691,17 +691,17 @@ class NativeObject : public JSObject
 
     static inline bool changePropertyAttributes(JSContext *cx, HandleNativeObject obj,
                                                 HandleShape shape, unsigned attrs);
 
     /* Remove the property named by id from this object. */
     bool removeProperty(ExclusiveContext *cx, jsid id);
 
     /* Clear the scope, making it empty. */
-    static void clear(ExclusiveContext *cx, HandleNativeObject obj);
+    static void clear(JSContext *cx, HandleNativeObject obj);
 
   protected:
     /*
      * Internal helper that adds a shape not yet mapped by this object.
      *
      * Notes:
      * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
      * 2. Checks for non-extensibility must be done by callers.
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -843,248 +843,243 @@ ObjectGroup::setGroupToHomogenousArray(E
         (void) p.add(cx, *table, key, group);
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // ObjectGroupCompartment PlainObjectTable
 /////////////////////////////////////////////////////////////////////
 
+/*
+ * N.B. We could also use the initial shape of the object (before its type is
+ * fixed) as the key in the object table, but since all references in the table
+ * are weak the hash entries would usually be collected on GC even if objects
+ * with the new type/shape are still live.
+ */
 struct ObjectGroupCompartment::PlainObjectKey
 {
     jsid *properties;
     uint32_t nproperties;
+    uint32_t nfixed;
 
     struct Lookup {
         IdValuePair *properties;
         uint32_t nproperties;
+        uint32_t nfixed;
 
-        Lookup(IdValuePair *properties, uint32_t nproperties)
-          : properties(properties), nproperties(nproperties)
+        Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed)
+          : properties(properties), nproperties(nproperties), nfixed(nfixed)
         {}
     };
 
     static inline HashNumber hash(const Lookup &lookup) {
         return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
-                             lookup.nproperties);
+                             lookup.nproperties ^
+                             lookup.nfixed);
     }
 
     static inline bool match(const PlainObjectKey &v, const Lookup &lookup) {
-        if (lookup.nproperties != v.nproperties)
+        if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed)
             return false;
         for (size_t i = 0; i < lookup.nproperties; i++) {
             if (lookup.properties[i].id != v.properties[i])
                 return false;
         }
         return true;
     }
 };
 
 struct ObjectGroupCompartment::PlainObjectEntry
 {
     ReadBarrieredObjectGroup group;
     ReadBarrieredShape shape;
     TypeSet::Type *types;
 };
 
-static bool
-CanShareObjectGroup(IdValuePair *properties, size_t nproperties)
+/* static */ void
+ObjectGroupCompartment::updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry,
+                                                    IdValuePair *properties, size_t nproperties)
 {
-    // Don't reuse groups for objects containing indexed properties, which
-    // might end up as dense elements.
+    if (entry.group->unknownProperties())
+        return;
     for (size_t i = 0; i < nproperties; i++) {
-        uint32_t index;
-        if (IdIsIndex(properties[i].id, &index))
-            return false;
+        TypeSet::Type type = entry.types[i];
+        TypeSet::Type ntype = GetValueTypeForTable(properties[i].value);
+        if (ntype == type)
+            continue;
+        if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
+            type.isPrimitive(JSVAL_TYPE_DOUBLE))
+        {
+            /* The property types already reflect 'int32'. */
+        } else {
+            if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
+                type.isPrimitive(JSVAL_TYPE_INT32))
+            {
+                /* Include 'double' in the property types to avoid the update below later. */
+                entry.types[i] = TypeSet::DoubleType();
+            }
+            AddTypePropertyId(cx, entry.group, nullptr, IdToTypeId(properties[i].id), ntype);
+        }
     }
-    return true;
 }
 
-static bool
-AddPlainObjectProperties(ExclusiveContext *cx, HandlePlainObject obj,
-                         IdValuePair *properties, size_t nproperties)
+/* static */ void
+ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj)
 {
-    RootedId propid(cx);
-    RootedValue value(cx);
-
-    for (size_t i = 0; i < nproperties; i++) {
-        propid = properties[i].id;
-        value = properties[i].value;
-        if (!NativeDefineProperty(cx, obj, propid, value, nullptr, nullptr, JSPROP_ENUMERATE))
-            return false;
-    }
-
-    return true;
-}
-
-PlainObject *
-js::NewPlainObjectWithProperties(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties,
-                                 NewObjectKind newKind)
-{
-    gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
-    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
-    if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
-        return nullptr;
-    return obj;
-}
-
-/* static */ JSObject *
-ObjectGroup::newPlainObject(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties,
-                            NewObjectKind newKind)
-{
-    // Watch for simple cases where we don't try to reuse plain object groups.
-    if (newKind == SingletonObject || nproperties == 0 || nproperties >= PropertyTree::MAX_HEIGHT)
-        return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
+    AutoEnterAnalysis enter(cx);
 
     ObjectGroupCompartment::PlainObjectTable *&table =
         cx->compartment()->objectGroups.plainObjectTable;
 
     if (!table) {
         table = cx->new_<ObjectGroupCompartment::PlainObjectTable>();
         if (!table || !table->init()) {
             js_delete(table);
             table = nullptr;
-            return nullptr;
+            return;
         }
     }
 
-    ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties);
-    ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookup(lookup);
-
-    if (!p) {
-        if (!CanShareObjectGroup(properties, nproperties))
-            return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
+    /*
+     * Use the same group for all singleton/JSON objects with the same
+     * base shape, i.e. the same fields written in the same order.
+     *
+     * 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;
 
-        RootedObject proto(cx);
-        if (!GetBuiltinPrototype(cx, JSProto_Object, &proto))
-            return nullptr;
-
-        Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
-        RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_,
-                                                                      tagged));
-        if (!group)
-            return nullptr;
+    Vector<IdValuePair> properties(cx);
+    if (!properties.resize(obj->slotSpan()))
+        return;
 
-        gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
-        RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group, cx->global(),
-                                                                  allocKind, TenuredObject));
-        if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
-            return nullptr;
+    Shape *shape = obj->lastProperty();
+    while (!shape->isEmptyShape()) {
+        IdValuePair &entry = properties[shape->slot()];
+        entry.id = shape->propid();
+        entry.value = obj->getSlot(shape->slot());
+        shape = shape->previous();
+    }
 
-        // Don't make entries with duplicate property names, which will show up
-        // here as objects with fewer properties than we thought we were
-        // adding to the object. In this case, reset the object's group to the
-        // default (which will have unknown properties) so that the group we
-        // just created will be collected by the GC.
-        if (obj->slotSpan() != nproperties) {
-            ObjectGroup *group = defaultNewGroup(cx, obj->getClass(), obj->getTaggedProto());
-            if (!group)
-                return nullptr;
-            obj->setGroup(group);
-            return obj;
-        }
+    ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties.begin(), properties.length(),
+                                                          obj->numFixedSlots());
+    ObjectGroupCompartment::PlainObjectTable::AddPtr p = table->lookupForAdd(lookup);
+
+    if (p) {
+        MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject());
+        MOZ_ASSERT(obj->lastProperty() == p->value().shape);
+
+        ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(),
+                                                            properties.begin(),
+                                                            properties.length());
+        obj->setGroup(p->value().group);
+        return;
+    }
 
-        // Keep track of the initial objects we create with this type.
-        // If the initial ones have a consistent shape and property types, we
-        // will try to use an unboxed layout for the group.
-        PreliminaryObjectArrayWithTemplate *preliminaryObjects =
-            cx->new_<PreliminaryObjectArrayWithTemplate>(obj->lastProperty());
-        if (!preliminaryObjects)
-            return nullptr;
-        group->setPreliminaryObjects(preliminaryObjects);
-        preliminaryObjects->registerNewObject(obj);
+    /* Make a new type to use for the object and similar future ones. */
+    Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
+    ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, objProto);
+    if (!group || !group->addDefiniteProperties(cx, obj->lastProperty()))
+        return;
+
+    if (obj->isIndexed())
+        group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
 
-        ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(nproperties));
-        if (!ids)
-            return nullptr;
+    ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(properties.length()));
+    if (!ids)
+        return;
 
-        ScopedJSFreePtr<TypeSet::Type> types(
-            group->zone()->pod_calloc<TypeSet::Type>(nproperties));
-        if (!types)
-            return nullptr;
+    ScopedJSFreePtr<TypeSet::Type> types(
+        group->zone()->pod_calloc<TypeSet::Type>(properties.length()));
+    if (!types)
+        return;
 
-        for (size_t i = 0; i < nproperties; i++) {
-            ids[i] = properties[i].id;
-            types[i] = GetValueTypeForTable(obj->getSlot(i));
-            AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
-        }
+    for (size_t i = 0; i < properties.length(); i++) {
+        ids[i] = properties[i].id;
+        types[i] = GetValueTypeForTable(obj->getSlot(i));
+        AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
+    }
 
-        ObjectGroupCompartment::PlainObjectKey key;
-        key.properties = ids;
-        key.nproperties = nproperties;
-        MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
+    ObjectGroupCompartment::PlainObjectKey key;
+    key.properties = ids;
+    key.nproperties = properties.length();
+    key.nfixed = obj->numFixedSlots();
+    MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
 
-        ObjectGroupCompartment::PlainObjectEntry entry;
-        entry.group.set(group);
-        entry.shape.set(obj->lastProperty());
-        entry.types = types;
+    ObjectGroupCompartment::PlainObjectEntry entry;
+    entry.group.set(group);
+    entry.shape.set(obj->lastProperty());
+    entry.types = types;
 
-        ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
-        if (!table->add(np, key, entry))
-            return nullptr;
+    obj->setGroup(group);
 
+    p = table->lookupForAdd(lookup);
+    if (table->add(p, key, entry)) {
         ids.forget();
         types.forget();
+    }
+}
 
-        return obj;
-    }
+/* static */ PlainObject *
+ObjectGroup::newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
+{
+    AutoEnterAnalysis enter(cx);
+
+    ObjectGroupCompartment::PlainObjectTable *table =
+        cx->compartment()->objectGroups.plainObjectTable;
+
+    if (!table)
+        return nullptr;
+
+    /*
+     * Use the object group table to allocate an object with the specified
+     * properties, filling in its final group and shape and failing if no table
+     * entry could be found for the properties.
+     */
 
-    RootedObjectGroup group(cx, p->value().group);
+    /*
+     * Filter out a few cases where we don't want to use the object group table.
+     * Note that if the properties contain any duplicates or dense indexes,
+     * the lookup below will fail as such arrays of properties cannot be stored
+     * in the object group table --- fixObjectGroup 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, &PlainObject::class_);
 
-    // Watch for existing groups which now use an unboxed layout.
-    if (group->maybeUnboxedLayout()) {
-        MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
-        return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
+    ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties, nfixed);
+    ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookupForAdd(lookup);
+
+    if (!p)
+        return nullptr;
+
+    RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
+    if (!obj) {
+        cx->clearPendingException();
+        return nullptr;
+    }
+    MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject());
+
+    if (!obj->setLastProperty(cx, p->value().shape)) {
+        cx->clearPendingException();
+        return nullptr;
     }
 
-    // Update property types according to the properties we are about to add.
-    // Do this before we do anything which can GC, which might move or remove
-    // this table entry.
-    if (!group->unknownProperties()) {
-        for (size_t i = 0; i < nproperties; i++) {
-            TypeSet::Type type = p->value().types[i];
-            TypeSet::Type ntype = GetValueTypeForTable(properties[i].value);
-            if (ntype == type)
-                continue;
-            if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
-                type.isPrimitive(JSVAL_TYPE_DOUBLE))
-            {
-                // The property types already reflect 'int32'.
-            } else {
-                if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
-                    type.isPrimitive(JSVAL_TYPE_INT32))
-                {
-                    // Include 'double' in the property types to avoid the update below later.
-                    p->value().types[i] = TypeSet::DoubleType();
-                }
-                AddTypePropertyId(cx, group, nullptr, IdToTypeId(properties[i].id), ntype);
-            }
-        }
-    }
-
-    RootedShape shape(cx, p->value().shape);
-
-    if (group->maybePreliminaryObjects())
-        newKind = TenuredObject;
-
-    gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
-    RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group, cx->global(), allocKind,
-                                                              newKind));
-
-    if (!obj->setLastProperty(cx, shape))
-        return nullptr;
+    ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), properties, nproperties);
 
     for (size_t i = 0; i < nproperties; i++)
         obj->setSlot(i, properties[i].value);
 
-    if (group->maybePreliminaryObjects()) {
-        group->maybePreliminaryObjects()->registerNewObject(obj);
-        group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
-    }
-
+    obj->setGroup(p->value().group);
     return obj;
 }
 
 /////////////////////////////////////////////////////////////////////
 // ObjectGroupCompartment AllocationSiteTable
 /////////////////////////////////////////////////////////////////////
 
 struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
@@ -1148,17 +1143,17 @@ ObjectGroup::allocationSiteGroup(JSConte
     ObjectGroup *res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged,
                                                          OBJECT_FLAG_FROM_ALLOCATION_SITE);
     if (!res)
         return nullptr;
 
     if (JSOp(*pc) == JSOP_NEWOBJECT) {
         // Keep track of the preliminary objects with this group, so we can try
         // to use an unboxed layout for the object once some are allocated.
-        Shape *shape = script->getObject(pc)->as<PlainObject>().lastProperty();
+        Shape *shape = script->getObject(pc)->lastProperty();
         if (!shape->isEmptyShape()) {
             PreliminaryObjectArrayWithTemplate *preliminaryObjects =
                 cx->new_<PreliminaryObjectArrayWithTemplate>(shape);
             if (preliminaryObjects)
                 res->setPreliminaryObjects(preliminaryObjects);
             else
                 cx->recoverFromOutOfMemory();
         }
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -127,46 +127,16 @@ AsTaggedProto(HandleObject obj)
             reinterpret_cast<TaggedProto const*>(obj.address()));
 }
 
 namespace gc {
 void MergeCompartments(JSCompartment *source, JSCompartment *target);
 }
 
 /*
- * The NewObjectKind allows an allocation site to specify the type properties
- * and lifetime requirements that must be fixed at allocation time.
- */
-enum NewObjectKind {
-    /* This is the default. Most objects are generic. */
-    GenericObject,
-
-    /*
-     * Singleton objects are treated specially by the type system. This flag
-     * ensures that the new object is automatically set up correctly as a
-     * singleton and is allocated in the correct heap.
-     */
-    SingletonObject,
-
-    /*
-     * Objects which may be marked as a singleton after allocation must still
-     * be allocated on the correct heap, but are not automatically setup as a
-     * singleton after allocation.
-     */
-    MaybeSingletonObject,
-
-    /*
-     * Objects which will not benefit from being allocated in the nursery
-     * (e.g. because they are known to have a long lifetime) may be allocated
-     * with this kind to place them immediately into the tenured generation.
-     */
-    TenuredObject
-};
-
-/*
  * Lazy object groups overview.
  *
  * Object groups which represent at most one JS object are constructed lazily.
  * These include groups for native functions, standard classes, scripted
  * functions defined at the top level of global/eval scripts, and in some
  * other cases. Typical web workloads often create many windows (and many
  * copies of standard natives) and many scripts, with comparatively few
  * non-singleton groups.
@@ -583,20 +553,16 @@ class ObjectGroup : public gc::TenuredCe
     static inline uint32_t offsetOfAddendum() {
         return offsetof(ObjectGroup, addendum_);
     }
 
     static inline uint32_t offsetOfFlags() {
         return offsetof(ObjectGroup, flags_);
     }
 
-    const ObjectGroupFlags *addressOfFlags() const {
-        return &flags_;
-    }
-
     // Get the bit pattern stored in an object's addendum when it has an
     // original unboxed group.
     static inline int32_t addendumOriginalUnboxedGroupValue() {
         return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT;
     }
 
     inline uint32_t basePropertyCount();
 
@@ -631,27 +597,25 @@ class ObjectGroup : public gc::TenuredCe
     static void setDefaultNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj);
 
 #ifdef DEBUG
     static bool hasDefaultNewGroup(JSObject *proto, const Class *clasp, ObjectGroup *group);
 #endif
 
     // Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable.
 
-    // Update the group of a freshly created array according to
+    // Update the group of a freshly created array or plain object according to
     // the object's current contents.
     static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj);
+    static void fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj);
 
     // Update the group of a freshly created 'rest' arguments object.
     static void fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj);
 
-    // Create a PlainObject or UnboxedPlainObject with the specified properties.
-    static JSObject *newPlainObject(ExclusiveContext *cx,
-                                    IdValuePair *properties, size_t nproperties,
-                                    NewObjectKind newKind);
+    static PlainObject *newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
 
     // Static accessors for ObjectGroupCompartment AllocationSiteTable.
 
     // Get a non-singleton group to use for objects created at the specified
     // allocation site.
     static ObjectGroup *allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc,
                                             JSProtoKey key);
 
@@ -763,17 +727,15 @@ class ObjectGroupCompartment
     void checkNewTableAfterMovingGC(NewTable *table);
 #endif
 
     void sweepNewTable(NewTable *table);
     void fixupNewTableAfterMovingGC(NewTable *table);
 
     static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table,
                                     const Class *clasp, TaggedProto proto, JSObject *associated);
+    static void updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry,
+                                            IdValuePair *properties, size_t nproperties);
 };
 
-PlainObject *
-NewPlainObjectWithProperties(ExclusiveContext *cx, IdValuePair *properties, size_t nproperties,
-                             NewObjectKind newKind);
-
 } // namespace js
 
 #endif /* vm_ObjectGroup_h */
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1017,34 +1017,33 @@ NativeObject::removeProperty(ExclusiveCo
         self->removeLastProperty(cx);
     }
 
     self->checkShapeConsistency();
     return true;
 }
 
 /* static */ void
-NativeObject::clear(ExclusiveContext *cx, HandleNativeObject obj)
+NativeObject::clear(JSContext *cx, HandleNativeObject obj)
 {
     Shape *shape = obj->lastProperty();
     MOZ_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
 
     while (shape->parent) {
         shape = shape->parent;
         MOZ_ASSERT(obj->inDictionaryMode() == shape->inDictionary());
     }
     MOZ_ASSERT(shape->isEmptyShape());
 
     if (obj->inDictionaryMode())
         shape->listp = &obj->shape_;
 
     JS_ALWAYS_TRUE(obj->setLastProperty(cx, shape));
 
-    if (cx->isJSContext())
-        ++cx->asJSContext()->runtime()->propertyRemovals;
+    ++cx->runtime()->propertyRemovals;
     obj->checkShapeConsistency();
 }
 
 /* static */ bool
 NativeObject::rollbackProperties(ExclusiveContext *cx, HandleNativeObject obj, uint32_t slotSpan)
 {
     /*
      * Remove properties from this object until it has a matching slot span.
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3356,17 +3356,17 @@ CommonPrefix(Shape *first, Shape *second
         first = first->previous();
         second = second->previous();
     }
 
     return first;
 }
 
 void
-PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext *cx, ObjectGroup *group, bool force)
+PreliminaryObjectArrayWithTemplate::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool force)
 {
     // Don't perform the analyses until sufficient preliminary objects have
     // been allocated.
     if (!force && !full())
         return;
 
     AutoEnterAnalysis enter(cx);
 
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -802,17 +802,17 @@ class PreliminaryObjectArrayWithTemplate
     explicit PreliminaryObjectArrayWithTemplate(Shape *shape)
       : shape_(shape)
     {}
 
     Shape *shape() {
         return shape_;
     }
 
-    void maybeAnalyze(ExclusiveContext *cx, ObjectGroup *group, bool force = false);
+    void maybeAnalyze(JSContext *cx, ObjectGroup *group, bool force = false);
 
     void trace(JSTracer *trc);
 
     static void writeBarrierPre(PreliminaryObjectArrayWithTemplate *preliminaryObjects);
 };
 
 // New script properties analyses overview.
 //
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1,19 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/UnboxedObject.h"
 
-#include "jit/JitCommon.h"
-#include "jit/Linker.h"
-
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::UniquePtr;
@@ -36,19 +33,16 @@ UnboxedLayout::trace(JSTracer *trc)
     if (nativeGroup_)
         MarkObjectGroup(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
 
     if (nativeShape_)
         MarkShape(trc, &nativeShape_, "unboxed_layout_nativeShape");
 
     if (replacementNewGroup_)
         MarkObjectGroup(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup");
-
-    if (constructorCode_)
-        MarkJitCode(trc, &constructorCode_, "unboxed_layout_constructorCode");
 }
 
 size_t
 UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
     return mallocSizeOf(this)
          + properties_.sizeOfExcludingThis(mallocSizeOf)
          + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
@@ -58,213 +52,28 @@ UnboxedLayout::sizeOfIncludingThis(mozil
 void
 UnboxedLayout::setNewScript(TypeNewScript *newScript, bool writeBarrier /* = true */)
 {
     if (newScript_ && writeBarrier)
         TypeNewScript::writeBarrierPre(newScript_);
     newScript_ = newScript;
 }
 
-// Constructor code returns a 0x1 value to indicate the constructor code should
-// be cleared.
-static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
-
-/* static */ bool
-UnboxedLayout::makeConstructorCode(JSContext *cx, HandleObjectGroup group)
-{
-    using namespace jit;
-
-    UnboxedLayout &layout = group->unboxedLayout();
-    MOZ_ASSERT(!layout.constructorCode());
-
-    UnboxedPlainObject *templateObject = UnboxedPlainObject::create(cx, group, TenuredObject);
-    if (!templateObject)
-        return false;
-
-    JitContext jitContext(cx, nullptr);
-
-    MacroAssembler masm;
-
-    Register propertiesReg, newKindReg;
-#ifdef JS_CODEGEN_X86
-    propertiesReg = eax;
-    newKindReg = ecx;
-    masm.loadPtr(Address(StackPointer, sizeof(void*)), propertiesReg);
-    masm.loadPtr(Address(StackPointer, 2 * sizeof(void*)), newKindReg);
-#else
-    propertiesReg = IntArgReg0;
-    newKindReg = IntArgReg1;
-#endif
-
-    MOZ_ASSERT(propertiesReg.volatile_());
-    MOZ_ASSERT(newKindReg.volatile_());
-
-    GeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(propertiesReg);
-    regs.take(newKindReg);
-    Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
-
-    GeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
-    for (GeneralRegisterForwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
-        masm.Push(*iter);
-
-    Label failure, tenuredObject, allocated;
-    masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject);
-    masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()),
-                      Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject);
-
-    // Allocate an object in the nursery
-    masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure,
-                        /* initFixedSlots = */ false);
-
-    masm.jump(&allocated);
-    masm.bind(&tenuredObject);
-
-    // Allocate an object in the tenured heap.
-    masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure,
-                        /* initFixedSlots = */ false);
-
-    // If any of the properties being stored are in the nursery, add a store
-    // buffer entry for the new object.
-    Label postBarrier;
-    for (size_t i = 0; i < layout.properties().length(); i++) {
-        const UnboxedLayout::Property &property = layout.properties()[i];
-        if (property.type == JSVAL_TYPE_OBJECT) {
-            Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
-            Label notObject;
-            masm.branchTestObject(Assembler::NotEqual, valueAddress, &notObject);
-            Register valueObject = masm.extractObject(valueAddress, scratch1);
-            masm.branchPtrInNurseryRange(Assembler::Equal, valueObject, scratch2, &postBarrier);
-            masm.bind(&notObject);
-        }
-    }
-
-    masm.jump(&allocated);
-    masm.bind(&postBarrier);
-
-    masm.mov(ImmPtr(cx->runtime()), scratch1);
-    masm.setupUnalignedABICall(2, scratch2);
-    masm.passABIArg(scratch1);
-    masm.passABIArg(object);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier));
-
-    masm.bind(&allocated);
-
-    ValueOperand valueOperand;
-#ifdef JS_NUNBOX32
-    valueOperand = ValueOperand(scratch1, scratch2);
-#else
-    valueOperand = ValueOperand(scratch1);
-#endif
-
-    Label failureStoreOther, failureStoreObject;
-
-    for (size_t i = 0; i < layout.properties().length(); i++) {
-        const UnboxedLayout::Property &property = layout.properties()[i];
-        Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
-        Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset);
-
-        masm.loadValue(valueAddress, valueOperand);
-
-        if (property.type == JSVAL_TYPE_OBJECT) {
-            HeapTypeSet *types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
-
-            Label notObject;
-            masm.branchTestObject(Assembler::NotEqual, valueOperand,
-                                  types->mightBeMIRType(MIRType_Null) ? &notObject : &failureStoreObject);
-
-            Register payloadReg = masm.extractObject(valueOperand, scratch1);
-
-            if (!types->hasType(TypeSet::AnyObjectType()))
-                masm.guardObjectType(payloadReg, types, scratch2, &failureStoreObject);
-
-            masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
-                                      TypedOrValueRegister(MIRType_Object,
-                                                           AnyRegister(payloadReg)), nullptr);
-
-            if (notObject.used()) {
-                Label done;
-                masm.jump(&done);
-                masm.bind(&notObject);
-                masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
-                masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr);
-                masm.bind(&done);
-            }
-        } else {
-            masm.storeUnboxedProperty(targetAddress, property.type,
-                                      ConstantOrRegister(valueOperand), &failureStoreOther);
-        }
-    }
-
-    Label done;
-    masm.bind(&done);
-
-    if (object != ReturnReg)
-        masm.movePtr(object, ReturnReg);
-
-    // Restore non-volatile registers which were saved on entry.
-    for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
-        masm.Pop(*iter);
-
-    masm.ret();
-
-    masm.bind(&failureStoreOther);
-
-    // There was a failure while storing a value which cannot be stored at all
-    // in the unboxed object. Initialize the object so it is safe for GC and
-    // return null.
-    masm.initUnboxedObjectContents(object, templateObject);
-
-    masm.bind(&failure);
-
-    masm.movePtr(ImmWord(0), object);
-    masm.jump(&done);
-
-    masm.bind(&failureStoreObject);
-
-    // There was a failure while storing a value to an object slot of the
-    // unboxed object. If the value is storable, the failure occurred due to
-    // incomplete type information in the object, so return a token to trigger
-    // regeneration of the jitcode after a new object is created in the VM.
-    {
-        Label isObject;
-        masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
-        masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
-        masm.bind(&isObject);
-    }
-
-    // Initialize the object so it is safe for GC.
-    masm.initUnboxedObjectContents(object, templateObject);
-
-    masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
-    masm.jump(&done);
-
-    Linker linker(masm);
-    AutoFlushICache afc("RegExp");
-    JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
-    if (!code)
-        return false;
-
-    layout.setConstructorCode(code);
-    return true;
-}
-
 void
 UnboxedLayout::detachFromCompartment()
 {
     remove();
 }
 
 /////////////////////////////////////////////////////////////////////
 // UnboxedPlainObject
 /////////////////////////////////////////////////////////////////////
 
 bool
-UnboxedPlainObject::setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property,
-                             const Value &v)
+UnboxedPlainObject::setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v)
 {
     uint8_t *p = &data_[property.offset];
 
     switch (property.type) {
       case JSVAL_TYPE_BOOLEAN:
         if (v.isBoolean()) {
             *p = v.toBoolean();
             return true;
@@ -300,17 +109,17 @@ UnboxedPlainObject::setValue(ExclusiveCo
             // created.
             AddTypePropertyId(cx, this, NameToId(property.name), v);
 
             // Manually trigger post barriers on the whole object. If we treat
             // the pointer as a HeapPtrObject we will get confused later if the
             // object is converted to its native representation.
             JSObject *obj = v.toObjectOrNull();
             if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(this))
-                cx->asJSContext()->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this);
+                cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(this);
 
             *reinterpret_cast<PreBarrieredObject*>(p) = obj;
             return true;
         }
         return false;
 
       default:
         MOZ_CRASH("Invalid type for unboxed value");
@@ -493,17 +302,17 @@ UnboxedPlainObject::convertToNative(JSCo
     for (size_t i = 0; i < values.length(); i++)
         obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
 
     return true;
 }
 
 /* static */
 UnboxedPlainObject *
-UnboxedPlainObject::create(ExclusiveContext *cx, HandleObjectGroup group, NewObjectKind newKind)
+UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind)
 {
     MOZ_ASSERT(group->clasp() == &class_);
     gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
 
     UnboxedPlainObject *res = NewObjectWithGroup<UnboxedPlainObject>(cx, group, cx->global(),
                                                                      allocKind, newKind);
     if (!res)
         return nullptr;
@@ -529,62 +338,16 @@ UnboxedPlainObject::create(ExclusiveCont
         }
         // Unboxed objects don't have Values to initialize.
         MOZ_ASSERT(*(list + 1) == -1);
     }
 
     return res;
 }
 
-/* static */ JSObject *
-UnboxedPlainObject::createWithProperties(ExclusiveContext *cx, HandleObjectGroup group,
-                                         NewObjectKind newKind, IdValuePair *properties)
-{
-    MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
-
-    UnboxedLayout &layout = group->unboxedLayout();
-
-    if (layout.constructorCode()) {
-        MOZ_ASSERT(cx->isJSContext());
-
-        typedef JSObject *(*ConstructorCodeSignature)(IdValuePair *, NewObjectKind);
-        ConstructorCodeSignature function =
-            reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
-
-        JSObject *obj;
-        {
-            JS::AutoSuppressGCAnalysis nogc;
-            obj = reinterpret_cast<JSObject *>(CALL_GENERATED_2(function, properties, newKind));
-        }
-        if (obj > reinterpret_cast<JSObject *>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
-            return obj;
-
-        if (obj == reinterpret_cast<JSObject *>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
-            layout.setConstructorCode(nullptr);
-    }
-
-    UnboxedPlainObject *obj = UnboxedPlainObject::create(cx, group, newKind);
-    if (!obj)
-        return nullptr;
-
-    for (size_t i = 0; i < layout.properties().length(); i++) {
-        if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
-            return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
-    }
-
-#ifndef JS_CODEGEN_NONE
-    if (cx->isJSContext() && !layout.constructorCode()) {
-        if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
-            return nullptr;
-    }
-#endif
-
-    return obj;
-}
-
 /* static */ bool
 UnboxedPlainObject::obj_lookupProperty(JSContext *cx, HandleObject obj,
                                        HandleId id, MutableHandleObject objp,
                                        MutableHandleShape propp)
 {
     if (obj->as<UnboxedPlainObject>().layout().lookup(id)) {
         MarkNonNativePropertyFound<CanGC>(propp);
         objp.set(obj);
@@ -779,23 +542,20 @@ PropertiesAreSuperset(const UnboxedLayou
         }
         if (!found)
             return false;
     }
     return true;
 }
 
 bool
-js::TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
+js::TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
                               ObjectGroup *group, PreliminaryObjectArray *objects)
 {
-    if (!templateShape->runtimeFromAnyThread()->options().unboxedObjects())
-        return true;
-
-    if (templateShape->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global()))
+    if (!cx->runtime()->options().unboxedObjects())
         return true;
 
     if (templateShape->slotSpan() == 0)
         return true;
 
     UnboxedLayout::PropertyVector properties;
     if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan()))
         return false;
@@ -962,17 +722,17 @@ js::TryConvertToUnboxedLayout(ExclusiveC
 
     // Get an empty shape which we can use for the preliminary objects.
     Shape *newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_,
                                                   group->proto(),
                                                   templateShape->getObjectParent(),
                                                   templateShape->getObjectMetadata(),
                                                   templateShape->getObjectFlags());
     if (!newShape) {
-        cx->recoverFromOutOfMemory();
+        cx->clearPendingException();
         return false;
     }
 
     // Accumulate a list of all the properties in each preliminary object, and
     // update their shapes.
     Vector<Value, 0, SystemAllocPolicy> values;
     if (!values.reserve(objectCount * templateShape->slotSpan()))
         return false;
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -74,26 +74,20 @@ class UnboxedLayout : public mozilla::Li
     // If nativeGroup is set and this object originally had a TypeNewScript,
     // this points to the default 'new' group which replaced this one (and
     // which might itself have been cleared since). This link is only needed to
     // keep the replacement group from being GC'ed. If it were GC'ed and a new
     // one regenerated later, that new group might have a different allocation
     // kind from this group.
     HeapPtrObjectGroup replacementNewGroup_;
 
-    // If this layout has been used to construct script or JSON constant
-    // objects, this code might be filled in to more quickly fill in objects
-    // from an array of values.
-    HeapPtrJitCode constructorCode_;
-
   public:
     UnboxedLayout(const PropertyVector &properties, size_t size)
       : size_(size), newScript_(nullptr), traceList_(nullptr),
-        nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr),
-        constructorCode_(nullptr)
+        nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr)
     {
         properties_.appendAll(properties);
     }
 
     ~UnboxedLayout() {
         js_delete(newScript_);
         js_free(traceList_);
     }
@@ -139,32 +133,23 @@ class UnboxedLayout : public mozilla::Li
     ObjectGroup *nativeGroup() const {
         return nativeGroup_;
     }
 
     Shape *nativeShape() const {
         return nativeShape_;
     }
 
-    jit::JitCode *constructorCode() const {
-        return constructorCode_;
-    }
-
-    void setConstructorCode(jit::JitCode *code) {
-        constructorCode_ = code;
-    }
-
     inline gc::AllocKind getAllocKind() const;
 
     void trace(JSTracer *trc);
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     static bool makeNativeGroup(JSContext *cx, ObjectGroup *group);
-    static bool makeConstructorCode(JSContext *cx, HandleObjectGroup group);
 };
 
 // Class for a plain object using an unboxed representation. The physical
 // layout of these objects is identical to that of an InlineTypedObject, though
 // these objects use an UnboxedLayout instead of a TypeDescr to keep track of
 // how their properties are stored.
 class UnboxedPlainObject : public JSObject
 {
@@ -209,37 +194,34 @@ class UnboxedPlainObject : public JSObje
     const UnboxedLayout &layoutDontCheckGeneration() const {
         return group()->unboxedLayoutDontCheckGeneration();
     }
 
     uint8_t *data() {
         return &data_[0];
     }
 
-    bool setValue(ExclusiveContext *cx, const UnboxedLayout::Property &property, const Value &v);
+    bool setValue(JSContext *cx, const UnboxedLayout::Property &property, const Value &v);
     Value getValue(const UnboxedLayout::Property &property);
 
     static bool convertToNative(JSContext *cx, JSObject *obj);
-    static UnboxedPlainObject *create(ExclusiveContext *cx, HandleObjectGroup group,
-                                      NewObjectKind newKind);
-    static JSObject *createWithProperties(ExclusiveContext *cx, HandleObjectGroup group,
-                                          NewObjectKind newKind, IdValuePair *properties);
+    static UnboxedPlainObject *create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind);
 
     static void trace(JSTracer *trc, JSObject *object);
 
     static size_t offsetOfData() {
         return offsetof(UnboxedPlainObject, data_[0]);
     }
 };
 
 // Try to construct an UnboxedLayout for each of the preliminary objects,
 // provided they all match the template shape. If successful, converts the
 // preliminary objects and their group to the new unboxed representation.
 bool
-TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
+TryConvertToUnboxedLayout(JSContext *cx, Shape *templateShape,
                           ObjectGroup *group, PreliminaryObjectArray *objects);
 
 inline gc::AllocKind
 UnboxedLayout::getAllocKind() const
 {
     return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
 }
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 259;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 258;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 388,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");