Bug 1129226 - Refactor ObjectGroup class and accessors, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 06 Feb 2015 09:13:29 -0700
changeset 227824 6bfcb81d3716bfcdcd0045d0fb80153159513a83
parent 227823 e23d1b5c00d66f7d1d13ff64e4f751f67316fa9d
child 227825 7d211cebe7fcd898be21cb7f11dfcbd4108bc20d
push id55242
push userbhackett@mozilla.com
push dateFri, 06 Feb 2015 16:14:35 +0000
treeherdermozilla-inbound@6bfcb81d3716 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1129226
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1129226 - Refactor ObjectGroup class and accessors, r=jandem.
js/src/builtin/Object.cpp
js/src/builtin/Object.h
js/src/builtin/TypedObject.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/gc/Barrier.h
js/src/gc/GCTrace.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/gc/Nursery.cpp
js/src/gc/Nursery.h
js/src/gc/RootMarking.cpp
js/src/gc/Rooting.h
js/src/gc/Tracer.h
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineInspector.h
js/src/jit/CodeGenerator.cpp
js/src/jit/Disassembler.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonAnalysis.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MIRGenerator.h
js/src/jit/MIRGraph.cpp
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/OptimizationTracking.cpp
js/src/jit/Recover.h
js/src/jit/ScalarReplacement.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jshashutil.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscript.h
js/src/jsstr.cpp
js/src/moz.build
js/src/vm/ArgumentsObject.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayObject-inl.h
js/src/vm/HelperThreads.cpp
js/src/vm/Interpreter.cpp
js/src/vm/JSONParser.cpp
js/src/vm/MemoryMetrics.cpp
js/src/vm/NativeObject-inl.h
js/src/vm/ObjectGroup.cpp
js/src/vm/ObjectGroup.h
js/src/vm/ProxyObject.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/Runtime-inl.h
js/src/vm/Runtime.h
js/src/vm/ScopeObject.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/vm/SharedTypedArrayObject.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/UbiNode.cpp
js/src/vm/UnboxedObject.cpp
js/src/vm/UnboxedObject.h
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -13,16 +13,17 @@
 
 #include "builtin/Eval.h"
 #include "frontend/BytecodeCompiler.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
+#include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 using js::frontend::IsIdentifier;
 using mozilla::ArrayLength;
 using mozilla::UniquePtr;
 
@@ -632,17 +633,17 @@ js::ObjectCreateImpl(JSContext *cx, Hand
 
     if (!proto) {
         // Object.create(null) is common, optimize it by using an allocation
         // site specific ObjectGroup. Because GetCallerInitGroup is pretty
         // slow, the caller can pass in the group if it's known and we use that
         // instead.
         RootedObjectGroup ngroup(cx, group);
         if (!ngroup) {
-            ngroup = GetCallerInitGroup(cx, JSProto_Null);
+            ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null);
             if (!ngroup)
                 return nullptr;
         }
 
         MOZ_ASSERT(!ngroup->proto().toObjectOrNull());
 
         return NewObjectWithGroup<PlainObject>(cx, ngroup, cx->global(), allocKind,
                                                newKind);
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_Object_h
 #define builtin_Object_h
 
 #include "jsapi.h"
 
+#include "vm/NativeObject.h"
+
 namespace JS {
 class CallArgs;
 class Value;
 }
 
 namespace js {
 
 // Object constructor native. Exposed only so the JIT can know its address.
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1490,17 +1490,19 @@ OutlineTypedObject::createUnattachedWith
                                               const Class *clasp,
                                               HandleTypeDescr descr,
                                               int32_t length,
                                               gc::InitialHeap heap)
 {
     MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ ||
                clasp == &OutlineOpaqueTypedObject::class_);
 
-    RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr));
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
+                                                             TaggedProto(&descr->typedProto()),
+                                                             descr));
     if (!group)
         return nullptr;
 
     NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
     OutlineTypedObject *obj = NewObjectWithGroup<OutlineTypedObject>(cx, group, cx->global(),
                                                                      gc::FINALIZE_OBJECT0, newKind);
     if (!obj)
         return nullptr;
@@ -2126,17 +2128,19 @@ OutlineTypedObject::neuter(void *newData
 InlineTypedObject::create(JSContext *cx, HandleTypeDescr descr, gc::InitialHeap heap)
 {
     gc::AllocKind allocKind = allocKindForTypeDescriptor(descr);
 
     const Class *clasp = descr->opaque()
                          ? &InlineOpaqueTypedObject::class_
                          : &InlineTransparentTypedObject::class_;
 
-    RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr));
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
+                                                             TaggedProto(&descr->typedProto()),
+                                                             descr));
     if (!group)
         return nullptr;
 
     NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject;
     return NewObjectWithGroup<InlineTypedObject>(cx, group, cx->global(), allocKind, newKind);
 }
 
 /* static */ InlineTypedObject *
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4290,17 +4290,17 @@ ParseNode::getConstantValue(ExclusiveCon
                 return true;
             }
             id = INT_TO_JSID(idx);
             if (!DefineProperty(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
                 return false;
         }
         MOZ_ASSERT(idx == count);
 
-        types::FixArrayGroup(cx, obj);
+        ObjectGroup::fixArrayGroup(cx, obj);
         vp.setObject(*obj);
         return true;
       }
       case PNK_OBJECT: {
         MOZ_ASSERT(isOp(JSOP_NEWINIT));
         MOZ_ASSERT(!(pn_xflags & PNX_NONCONST));
 
         if (allowObjects == DontAllowObjects) {
@@ -4353,17 +4353,17 @@ ParseNode::getConstantValue(ExclusiveCon
                 if (!DefineProperty(cx, obj, name->asPropertyName(), value,
                                     nullptr, nullptr, JSPROP_ENUMERATE))
                 {
                     return false;
                 }
             }
         }
 
-        types::FixObjectGroup(cx, obj);
+        ObjectGroup::fixPlainObjectGroup(cx, obj);
         vp.setObject(*obj);
         return true;
       }
       default:
         MOZ_CRASH("Unexpected node");
     }
     return false;
 }
@@ -7245,23 +7245,23 @@ frontend::EmitTree(ExclusiveContext *cx,
             // If the array consists entirely of primitive values, make a
             // template object with copy on write elements that can be reused
             // every time the initializer executes.
             if (bce->emitterMode != BytecodeEmitter::SelfHosting && pn->pn_count != 0) {
                 RootedValue value(cx);
                 if (!pn->getConstantValue(cx, ParseNode::DontAllowNestedObjects, &value))
                     return false;
                 if (!value.isMagic(JS_GENERIC_MAGIC)) {
-                    // Note: the type of the template object might not yet reflect
+                    // Note: the group of the template object might not yet reflect
                     // that the object has copy on write elements. When the
                     // interpreter or JIT compiler fetches the template, it should
-                    // use types::GetOrFixupCopyOnWriteObject to make sure the type
-                    // for the template is accurate. We don't do this here as we
-                    // want to use types::InitObject, which requires a finished
-                    // script.
+                    // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the
+                    // group for the template is accurate. We don't do this here as we
+                    // want to use ObjectGroup::allocationSiteGroup, which requires a
+                    // finished script.
                     NativeObject *obj = &value.toObject().as<NativeObject>();
                     if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj))
                         return false;
 
                     ObjectBox *objbox = bce->parser->newObjectBox(obj);
                     if (!objbox)
                         return false;
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -176,20 +176,17 @@ class NestedScopeObject;
 class Nursery;
 class PlainObject;
 class PropertyName;
 class SavedFrame;
 class ScopeObject;
 class ScriptSourceObject;
 class Shape;
 class UnownedBaseShape;
-
-namespace types {
-struct ObjectGroup;
-}
+class ObjectGroup;
 
 namespace jit {
 class JitCode;
 }
 
 #ifdef DEBUG
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
@@ -227,17 +224,17 @@ template <> struct MapTypeToTraceKind<Pr
 template <> struct MapTypeToTraceKind<SavedFrame>       { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ScopeObject>      { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<Shape>            { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
 template <> struct MapTypeToTraceKind<AccessorShape>    { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
 template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<SharedTypedArrayObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<jit::JitCode>     { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
-template <> struct MapTypeToTraceKind<types::ObjectGroup> { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; };
+template <> struct MapTypeToTraceKind<ObjectGroup>      { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; };
 
 // Direct value access used by the write barriers and the jits.
 void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
 
 // These three declarations are also present in gc/Marking.h, via the DeclMarker
 // macro.  Not great, but hard to avoid.
 void
@@ -801,17 +798,17 @@ typedef HeapPtr<JSLinearString*> HeapPtr
 typedef HeapPtr<JSObject*> HeapPtrObject;
 typedef HeapPtr<JSScript*> HeapPtrScript;
 typedef HeapPtr<JSString*> HeapPtrString;
 typedef HeapPtr<PlainObject*> HeapPtrPlainObject;
 typedef HeapPtr<PropertyName*> HeapPtrPropertyName;
 typedef HeapPtr<Shape*> HeapPtrShape;
 typedef HeapPtr<UnownedBaseShape*> HeapPtrUnownedBaseShape;
 typedef HeapPtr<jit::JitCode*> HeapPtrJitCode;
-typedef HeapPtr<types::ObjectGroup*> HeapPtrObjectGroup;
+typedef HeapPtr<ObjectGroup*> HeapPtrObjectGroup;
 
 typedef PreBarriered<Value> PreBarrieredValue;
 typedef RelocatablePtr<Value> RelocatableValue;
 typedef HeapPtr<Value> HeapValue;
 
 typedef PreBarriered<jsid> PreBarrieredId;
 typedef RelocatablePtr<jsid> RelocatableId;
 typedef HeapPtr<jsid> HeapId;
@@ -822,17 +819,17 @@ typedef ImmutableTenuredPtr<JS::Symbol*>
 typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
 typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
 typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
 typedef ReadBarriered<Shape*> ReadBarrieredShape;
 typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
 typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
-typedef ReadBarriered<types::ObjectGroup*> ReadBarrieredObjectGroup;
+typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
 typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
 typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
 
 typedef ReadBarriered<Value> ReadBarrieredValue;
 
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
 // memory, but with substantially less overhead than a RelocatablePtr.
--- a/js/src/gc/GCTrace.h
+++ b/js/src/gc/GCTrace.h
@@ -6,17 +6,17 @@
 
 #ifndef gc_GCTrace_h
 #define gc_GCTrace_h
 
 #include "gc/Heap.h"
 
 namespace js {
 
-namespace types { struct ObjectGroup; }
+class ObjectGroup;
 
 namespace gc {
 
 #ifdef JS_GC_TRACE
 
 extern bool InitTrace(GCRuntime &gc);
 extern void FinishTrace();
 extern bool TraceEnabled();
@@ -24,32 +24,32 @@ extern void TraceNurseryAlloc(Cell *thin
 extern void TraceTenuredAlloc(Cell *thing, AllocKind kind);
 extern void TraceCreateObject(JSObject* object);
 extern void TraceMinorGCStart();
 extern void TracePromoteToTenured(Cell *src, Cell *dst);
 extern void TraceMinorGCEnd();
 extern void TraceMajorGCStart();
 extern void TraceTenuredFinalize(Cell *thing);
 extern void TraceMajorGCEnd();
-extern void TraceTypeNewScript(js::types::ObjectGroup *group);
+extern void TraceTypeNewScript(js::ObjectGroup *group);
 
 #else
 
 inline bool InitTrace(GCRuntime &gc) { return true; }
 inline void FinishTrace() {}
 inline bool TraceEnabled() { return false; }
 inline void TraceNurseryAlloc(Cell *thing, size_t size) {}
 inline void TraceTenuredAlloc(Cell *thing, AllocKind kind) {}
 inline void TraceCreateObject(JSObject* object) {}
 inline void TraceMinorGCStart() {}
 inline void TracePromoteToTenured(Cell *src, Cell *dst) {}
 inline void TraceMinorGCEnd() {}
 inline void TraceMajorGCStart() {}
 inline void TraceTenuredFinalize(Cell *thing) {}
 inline void TraceMajorGCEnd() {}
-inline void TraceTypeNewScript(js::types::ObjectGroup *group) {}
+inline void TraceTypeNewScript(js::ObjectGroup *group) {}
 
 #endif
 
 } /* namespace gc */
 } /* namespace js */
 
 #endif
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -82,28 +82,28 @@ PushMarkStack(GCMarker *gcmarker, Shape 
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *str);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym);
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing);
+PushMarkStack(GCMarker *gcmarker, ObjectGroup *thing);
 
 namespace js {
 namespace gc {
 
 static void MarkChildren(JSTracer *trc, JSString *str);
 static void MarkChildren(JSTracer *trc, JS::Symbol *sym);
 static void MarkChildren(JSTracer *trc, JSScript *script);
 static void MarkChildren(JSTracer *trc, LazyScript *lazy);
 static void MarkChildren(JSTracer *trc, Shape *shape);
 static void MarkChildren(JSTracer *trc, BaseShape *base);
-static void MarkChildren(JSTracer *trc, types::ObjectGroup *group);
+static void MarkChildren(JSTracer *trc, ObjectGroup *group);
 static void MarkChildren(JSTracer *trc, jit::JitCode *code);
 
 } /* namespace gc */
 } /* namespace js */
 
 /*** Object Marking ***/
 
 #if defined(DEBUG)
@@ -609,17 +609,17 @@ DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(LazyScript, LazyScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
 DeclMarkerImpl(String, JSString)
 DeclMarkerImpl(String, JSFlatString)
 DeclMarkerImpl(String, JSLinearString)
 DeclMarkerImpl(String, PropertyName)
 DeclMarkerImpl(Symbol, JS::Symbol)
-DeclMarkerImpl(ObjectGroup, js::types::ObjectGroup)
+DeclMarkerImpl(ObjectGroup, js::ObjectGroup)
 
 } /* namespace gc */
 } /* namespace js */
 
 /*** Externally Typed Marking ***/
 
 void
 gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
@@ -650,17 +650,17 @@ gc::MarkKind(JSTracer *trc, void **thing
         break;
       case JSTRACE_LAZY_SCRIPT:
         MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
         break;
       case JSTRACE_SHAPE:
         MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
         break;
       case JSTRACE_OBJECT_GROUP:
-        MarkInternal(trc, reinterpret_cast<types::ObjectGroup **>(thingp));
+        MarkInternal(trc, reinterpret_cast<ObjectGroup **>(thingp));
         break;
       default:
         MOZ_CRASH("Invalid trace kind in MarkKind.");
     }
 }
 
 static void
 MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name)
@@ -791,17 +791,17 @@ gc::MarkTypeRoot(JSTracer *trc, types::T
 {
     JS_ROOT_MARKING_ASSERT(trc);
     trc->setTracingName(name);
     if (v->isSingleton()) {
         JSObject *obj = v->singleton();
         MarkInternal(trc, &obj);
         *v = types::Type::ObjectType(obj);
     } else if (v->isGroup()) {
-        types::ObjectGroup *group = v->group();
+        ObjectGroup *group = v->group();
         MarkInternal(trc, &group);
         *v = types::Type::ObjectType(group);
     }
 }
 
 void
 gc::MarkValueRange(JSTracer *trc, size_t len, BarrieredBase<Value> *vec, const char *name)
 {
@@ -1061,17 +1061,17 @@ PushMarkStack(GCMarker *gcmarker, JSFunc
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
     MOZ_ASSERT(!IsInsideNursery(thing));
 
     if (thing->asTenured().markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 static void
-PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing)
+PushMarkStack(GCMarker *gcmarker, ObjectGroup *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
     MOZ_ASSERT(!IsInsideNursery(thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushType(thing);
 }
 
@@ -1417,17 +1417,17 @@ gc::MarkCycleCollectorChildren(JSTracer 
             MOZ_ASSERT(tmp == shape->setterObject());
         }
 
         shape = shape->previous();
     } while (shape);
 }
 
 static void
-ScanObjectGroup(GCMarker *gcmarker, types::ObjectGroup *group)
+ScanObjectGroup(GCMarker *gcmarker, ObjectGroup *group)
 {
     unsigned count = group->getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         if (types::Property *prop = group->getProperty(i))
             MarkId(gcmarker, &prop->id, "ObjectGroup property id");
     }
 
     if (group->proto().isObject())
@@ -1445,17 +1445,17 @@ ScanObjectGroup(GCMarker *gcmarker, type
     if (TypeDescr *descr = group->maybeTypeDescr())
         PushMarkStack(gcmarker, descr);
 
     if (JSFunction *fun = group->maybeInterpretedFunction())
         PushMarkStack(gcmarker, fun);
 }
 
 static void
-gc::MarkChildren(JSTracer *trc, types::ObjectGroup *group)
+gc::MarkChildren(JSTracer *trc, ObjectGroup *group)
 {
     unsigned count = group->getPropertyCount();
     for (unsigned i = 0; i < count; i++) {
         types::Property *prop = group->getProperty(i);
         if (prop)
             MarkId(trc, &prop->id, "group_property");
     }
 
@@ -1528,17 +1528,17 @@ gc::PushArena(GCMarker *gcmarker, ArenaH
         PushArenaTyped<LazyScript>(gcmarker, aheader);
         break;
 
       case JSTRACE_SHAPE:
         PushArenaTyped<js::Shape>(gcmarker, aheader);
         break;
 
       case JSTRACE_OBJECT_GROUP:
-        PushArenaTyped<js::types::ObjectGroup>(gcmarker, aheader);
+        PushArenaTyped<js::ObjectGroup>(gcmarker, aheader);
         break;
 
       default:
         MOZ_CRASH("Invalid trace kind in PushArena.");
     }
 }
 
 struct SlotArrayLayout
@@ -1647,17 +1647,17 @@ GCMarker::restoreValueArray(NativeObject
     MOZ_ASSERT(*vpp <= *endp);
     return true;
 }
 
 void
 GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
 {
     if (tag == GroupTag) {
-        ScanObjectGroup(this, reinterpret_cast<types::ObjectGroup *>(addr));
+        ScanObjectGroup(this, reinterpret_cast<ObjectGroup *>(addr));
     } else if (tag == SavedValueArrayTag) {
         MOZ_ASSERT(!(addr & CellMask));
         NativeObject *obj = reinterpret_cast<NativeObject *>(addr);
         HeapValue *vp, *end;
         if (restoreValueArray(obj, (void **)&vp, (void **)&end))
             pushValueArray(obj, vp, end);
         else
             pushObject(obj);
@@ -1797,17 +1797,17 @@ GCMarker::processMarkStackTop(SliceBudge
         JS_COMPARTMENT_ASSERT(runtime(), obj);
 
         budget.step();
         if (budget.isOverBudget()) {
             pushObject(obj);
             return;
         }
 
-        types::ObjectGroup *group = obj->groupFromGC();
+        ObjectGroup *group = obj->groupFromGC();
         PushMarkStack(this, group);
 
         Shape *shape = obj->lastProperty();
         PushMarkStack(this, shape);
 
         /* Call the trace hook if necessary. */
         const Class *clasp = group->clasp();
         if (clasp->trace) {
@@ -1952,17 +1952,17 @@ js::TraceChildren(JSTracer *trc, void *t
         MarkChildren(trc, static_cast<LazyScript *>(thing));
         break;
 
       case JSTRACE_SHAPE:
         MarkChildren(trc, static_cast<Shape *>(thing));
         break;
 
       case JSTRACE_OBJECT_GROUP:
-        MarkChildren(trc, (types::ObjectGroup *)thing);
+        MarkChildren(trc, (ObjectGroup *)thing);
         break;
 
       default:
         MOZ_CRASH("Invalid trace kind in TraceChildren.");
     }
 }
 
 #ifdef DEBUG
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -127,17 +127,17 @@ DeclMarker(Script, JSScript)
 DeclMarker(LazyScript, LazyScript)
 DeclMarker(Shape, Shape)
 DeclMarker(String, JSAtom)
 DeclMarker(String, JSString)
 DeclMarker(String, JSFlatString)
 DeclMarker(String, JSLinearString)
 DeclMarker(String, PropertyName)
 DeclMarker(Symbol, JS::Symbol)
-DeclMarker(ObjectGroup, types::ObjectGroup)
+DeclMarker(ObjectGroup, ObjectGroup)
 
 #undef DeclMarker
 
 void
 MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name);
 
 void
 MarkWellKnownSymbol(JSTracer *trc, JS::Symbol *sym);
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -532,31 +532,31 @@ js::Nursery::forwardBufferPointer(HeapSl
     MOZ_ASSERT(!isInside(*pSlotsElems));
     MOZ_ASSERT(IsWriteableAddress(*pSlotsElems));
 }
 
 // Structure for counting how many times objects in a particular group have
 // been tenured during a minor collection.
 struct TenureCount
 {
-    types::ObjectGroup *group;
+    ObjectGroup *group;
     int count;
 };
 
 // Keep rough track of how many times we tenure objects in particular groups
 // during minor collections, using a fixed size hash for efficiency at the cost
 // of potential collisions.
 struct Nursery::TenureCountCache
 {
     TenureCount entries[16];
 
     TenureCountCache() { PodZero(this); }
 
-    TenureCount &findEntry(types::ObjectGroup *group) {
-        return entries[PointerHasher<types::ObjectGroup *, 3>::hash(group) % ArrayLength(entries)];
+    TenureCount &findEntry(ObjectGroup *group) {
+        return entries[PointerHasher<ObjectGroup *, 3>::hash(group) % ArrayLength(entries)];
     }
 };
 
 void
 js::Nursery::collectToFixedPoint(MinorCollectionTracer *trc, TenureCountCache &tenureCounts)
 {
     for (RelocationOverlay *p = trc->head; p; p = p->next()) {
         JSObject *obj = static_cast<JSObject*>(p->forwardingAddress());
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -26,28 +26,26 @@ struct Zone;
 }
 
 namespace js {
 
 class TypedArrayObject;
 class ObjectElements;
 class NativeObject;
 class HeapSlot;
+class ObjectGroup;
+
 void SetGCZeal(JSRuntime *, uint8_t, uint32_t);
 
 namespace gc {
 struct Cell;
 class Collector;
 class MinorCollectionTracer;
 } /* namespace gc */
 
-namespace types {
-struct ObjectGroup;
-}
-
 namespace jit {
 class CodeGenerator;
 class MacroAssembler;
 class ICStubCompiler;
 class BaselineCompiler;
 }
 
 class Nursery
@@ -112,17 +110,17 @@ class Nursery
 
     /* Resize an existing elements vector. */
     ObjectElements *reallocateElements(JSObject *obj, ObjectElements *oldHeader,
                                        uint32_t oldCount, uint32_t newCount);
 
     /* Free a slots array. */
     void freeSlots(HeapSlot *slots);
 
-    typedef Vector<types::ObjectGroup *, 0, SystemAllocPolicy> ObjectGroupList;
+    typedef Vector<ObjectGroup *, 0, SystemAllocPolicy> ObjectGroupList;
 
     /*
      * Do a minor collection, optionally specifying a list to store groups which
      * should be pretenured afterwards.
      */
     void collect(JSRuntime *rt, JS::gcreason::Reason reason, ObjectGroupList *pretenureGroups);
 
     /*
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -98,17 +98,17 @@ MarkExactStackRootList(JSTracer *trc, So
 
 template<class T>
 static void
 MarkExactStackRootsAcrossTypes(T context, JSTracer *trc)
 {
     MarkExactStackRootList<JSObject *, MarkObjectRoot>(trc, context, "exact-object");
     MarkExactStackRootList<Shape *, MarkShapeRoot>(trc, context, "exact-shape");
     MarkExactStackRootList<BaseShape *, MarkBaseShapeRoot>(trc, context, "exact-baseshape");
-    MarkExactStackRootList<types::ObjectGroup *, MarkObjectGroupRoot>(
+    MarkExactStackRootList<ObjectGroup *, MarkObjectGroupRoot>(
         trc, context, "exact-objectgroup");
     MarkExactStackRootList<JSString *, MarkStringRoot>(trc, context, "exact-string");
     MarkExactStackRootList<JS::Symbol *, MarkSymbolRoot>(trc, context, "exact-symbol");
     MarkExactStackRootList<jit::JitCode *, MarkJitCodeRoot>(trc, context, "exact-jitcode");
     MarkExactStackRootList<JSScript *, MarkScriptRoot>(trc, context, "exact-script");
     MarkExactStackRootList<LazyScript *, MarkLazyScriptRoot>(trc, context, "exact-lazy-script");
     MarkExactStackRootList<jsid, MarkIdRoot>(trc, context, "exact-id");
     MarkExactStackRootList<Value, MarkValueRoot>(trc, context, "exact-value");
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -15,38 +15,37 @@ class JSLinearString;
 namespace js {
 
 class PropertyName;
 class NativeObject;
 class ArrayObject;
 class PlainObject;
 class ScriptSourceObject;
 class Shape;
-
-namespace types { struct ObjectGroup; }
+class ObjectGroup;
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<NativeObject*>      HandleNativeObject;
 typedef JS::Handle<Shape*>             HandleShape;
-typedef JS::Handle<types::ObjectGroup*> HandleObjectGroup;
+typedef JS::Handle<ObjectGroup*>       HandleObjectGroup;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<JSLinearString*>    HandleLinearString;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
 typedef JS::Handle<ArrayObject*>       HandleArrayObject;
 typedef JS::Handle<PlainObject*>       HandlePlainObject;
 typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
 
 typedef JS::Rooted<NativeObject*>      RootedNativeObject;
 typedef JS::Rooted<Shape*>             RootedShape;
-typedef JS::Rooted<types::ObjectGroup*> RootedObjectGroup;
+typedef JS::Rooted<ObjectGroup*>       RootedObjectGroup;
 typedef JS::Rooted<JSAtom*>            RootedAtom;
 typedef JS::Rooted<JSLinearString*>    RootedLinearString;
 typedef JS::Rooted<PropertyName*>      RootedPropertyName;
 typedef JS::Rooted<ArrayObject*>       RootedArrayObject;
 typedef JS::Rooted<PlainObject*>       RootedPlainObject;
 typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -11,25 +11,23 @@
 
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/TracingAPI.h"
 
 namespace js {
 class NativeObject;
 class GCMarker;
+class ObjectGroup;
 namespace gc {
 struct ArenaHeader;
 }
 namespace jit {
 class JitCode;
 }
-namespace types {
-struct ObjectGroup;
-}
 
 static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
 static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
 
 /*
  * When the native stack is low, the GC does not call JS_TraceChildren to mark
  * the reachable "children" of the thing. Rather the thing is put aside and
  * JS_TraceChildren is called later with more space on the C stack.
@@ -138,17 +136,17 @@ class GCMarker : public JSTracer
     void start();
     void stop();
     void reset();
 
     void pushObject(JSObject *obj) {
         pushTaggedPtr(ObjectTag, obj);
     }
 
-    void pushType(types::ObjectGroup *group) {
+    void pushType(ObjectGroup *group) {
         pushTaggedPtr(GroupTag, group);
     }
 
     void pushJitCode(jit::JitCode *code) {
         pushTaggedPtr(JitCodeTag, code);
     }
 
     uint32_t getMarkColor() const {
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1717,18 +1717,18 @@ BaselineCompiler::emit_JSOP_LINENO()
 
 bool
 BaselineCompiler::emit_JSOP_NEWARRAY()
 {
     frame.syncStack(0);
 
     uint32_t length = GET_UINT24(pc);
     RootedObjectGroup group(cx);
-    if (!types::UseSingletonForInitializer(script, pc, JSProto_Array)) {
-        group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array);
+    if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) {
+        group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
         if (!group)
             return false;
     }
 
     // Pass length in R0, group in R1.
     masm.move32(Imm32(length), R0.scratchReg());
     masm.movePtr(ImmGCPtr(group), R1.scratchReg());
 
@@ -1748,17 +1748,17 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
 typedef JSObject *(*NewArrayCopyOnWriteFn)(JSContext *, HandleArrayObject, gc::InitialHeap);
 const VMFunction jit::NewArrayCopyOnWriteInfo =
     FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray);
 
 bool
 BaselineCompiler::emit_JSOP_NEWARRAY_COPYONWRITE()
 {
     RootedScript scriptRoot(cx, script);
-    JSObject *obj = types::GetOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
+    JSObject *obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc);
     if (!obj)
         return false;
 
     prepareVMCall();
 
     pushArg(Imm32(gc::DefaultHeap));
     pushArg(ImmGCPtr(obj));
 
@@ -1792,18 +1792,18 @@ BaselineCompiler::emit_JSOP_INITELEM_ARR
 }
 
 bool
 BaselineCompiler::emit_JSOP_NEWOBJECT()
 {
     frame.syncStack(0);
 
     RootedObjectGroup group(cx);
-    if (!types::UseSingletonForInitializer(script, pc, JSProto_Object)) {
-        group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Object);
+    if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Object)) {
+        group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Object);
         if (!group)
             return false;
     }
 
     RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>());
     RootedPlainObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject));
     if (!templateObject)
         return false;
@@ -1817,18 +1817,18 @@ BaselineCompiler::emit_JSOP_NEWOBJECT()
 
     // Try to do the allocation inline.
     Label done;
     if (group && !group->shouldPreTenure() && !templateObject->hasDynamicSlots()) {
         Label slowPath;
         Register objReg = R0.scratchReg();
         Register tempReg = R1.scratchReg();
         masm.movePtr(ImmGCPtr(group), tempReg);
-        masm.branchTest32(Assembler::NonZero, Address(tempReg, types::ObjectGroup::offsetOfFlags()),
-                          Imm32(types::OBJECT_FLAG_PRE_TENURE), &slowPath);
+        masm.branchTest32(Assembler::NonZero, Address(tempReg, ObjectGroup::offsetOfFlags()),
+                          Imm32(OBJECT_FLAG_PRE_TENURE), &slowPath);
         masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->compartment()->addressOfMetadataCallback()),
                       ImmWord(0), &slowPath);
         masm.createGCObject(objReg, tempReg, templateObject, gc::DefaultHeap, &slowPath);
         masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
         masm.jump(&done);
         masm.bind(&slowPath);
     }
 
@@ -1843,18 +1843,18 @@ BaselineCompiler::emit_JSOP_NEWOBJECT()
 
 bool
 BaselineCompiler::emit_JSOP_NEWINIT()
 {
     frame.syncStack(0);
     JSProtoKey key = JSProtoKey(GET_UINT8(pc));
 
     RootedObjectGroup group(cx);
-    if (!types::UseSingletonForInitializer(script, pc, key)) {
-        group = types::TypeScript::InitGroup(cx, script, pc, key);
+    if (!ObjectGroup::useSingletonForAllocationSite(script, pc, key)) {
+        group = ObjectGroup::allocationSiteGroup(cx, script, pc, key);
         if (!group)
             return false;
     }
 
     if (key == JSProto_Array) {
         // Pass length in R0, group in R1.
         masm.move32(Imm32(0), R0.scratchReg());
         masm.movePtr(ImmGCPtr(group), R1.scratchReg());
@@ -3359,17 +3359,17 @@ BaselineCompiler::emit_JSOP_RUNONCE()
 bool
 BaselineCompiler::emit_JSOP_REST()
 {
     frame.syncStack(0);
 
     ArrayObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject);
     if (!templateObject)
         return false;
-    types::FixRestArgumentsType(cx, templateObject);
+    ObjectGroup::fixRestArgumentsGroup(cx, templateObject);
 
     // Call IC.
     ICRest_Fallback::Compiler compiler(cx, templateObject);
     if (!emitOpIC(compiler.getStub(&stubSpace_)))
         return false;
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2057,17 +2057,17 @@ ICCompare_ObjectWithUndefined::Compiler:
         // obj !== undefined for all objects.
         masm.moveValue(BooleanValue(op == JSOP_STRICTNE), R0);
         EmitReturnFromIC(masm);
     } else {
         // obj != undefined only where !obj->getClass()->emulatesUndefined()
         Label emulatesUndefined;
         Register obj = masm.extractObject(objectOperand, ExtractTemp0);
         masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), obj);
-        masm.loadPtr(Address(obj, types::ObjectGroup::offsetOfClasp()), obj);
+        masm.loadPtr(Address(obj, ObjectGroup::offsetOfClasp()), obj);
         masm.branchTest32(Assembler::NonZero,
                           Address(obj, Class::offsetOfFlags()),
                           Imm32(JSCLASS_EMULATES_UNDEFINED),
                           &emulatesUndefined);
         masm.moveValue(BooleanValue(op == JSOP_NE), R0);
         EmitReturnFromIC(masm);
         masm.bind(&emulatesUndefined);
         masm.moveValue(BooleanValue(op == JSOP_EQ), R0);
@@ -4516,17 +4516,17 @@ LoadTypedThingLength(MacroAssembler &mas
 {
     switch (layout) {
       case Layout_TypedArray:
         masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), result);
         break;
       case Layout_OutlineTypedObject:
       case Layout_InlineTypedObject:
         masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), result);
-        masm.loadPtr(Address(result, types::ObjectGroup::offsetOfAddendum()), result);
+        masm.loadPtr(Address(result, ObjectGroup::offsetOfAddendum()), result);
         masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result);
         break;
       default:
         MOZ_CRASH();
     }
 }
 
 static void
@@ -8515,17 +8515,17 @@ ICSetPropNativeAddCompiler::generateStub
 
     // Check if the cache has a new group to change to.
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
     masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange);
 
     // Check if the old group still has a newScript.
     masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch);
     masm.branchPtr(Assembler::Equal,
-                   Address(scratch, types::ObjectGroup::offsetOfAddendum()),
+                   Address(scratch, ObjectGroup::offsetOfAddendum()),
                    ImmWord(0),
                    &noGroupChange);
 
     // Reload the new group from the cache.
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch);
 
     // Change the object's group.
     Address groupAddr(objReg, JSObject::offsetOfGroup());
@@ -9044,29 +9044,29 @@ GetTemplateObjectForNative(JSContext *cx
         if (args.length() != 1)
             count = args.length();
         else if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0)
             count = args[0].toInt32();
         res.set(NewDenseUnallocatedArray(cx, count, nullptr, TenuredObject));
         if (!res)
             return false;
 
-        types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array);
+        ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
         if (!group)
             return false;
         res->setGroup(group);
         return true;
     }
 
     if (native == intrinsic_NewDenseArray) {
         res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
         if (!res)
             return false;
 
-        types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array);
+        ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
         if (!group)
             return false;
         res->setGroup(group);
         return true;
     }
 
     if (native == js::array_concat) {
         if (args.thisv().isObject() && args.thisv().toObject().is<ArrayObject>() &&
@@ -9080,17 +9080,17 @@ GetTemplateObjectForNative(JSContext *cx
         }
     }
 
     if (native == js::str_split && args.length() == 1 && args[0].isString()) {
         res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
         if (!res)
             return false;
 
-        types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array);
+        ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
         if (!group)
             return false;
         res->setGroup(group);
         return true;
     }
 
     if (native == js_String) {
         RootedString emptyString(cx, cx->runtime()->emptyString);
@@ -9482,17 +9482,17 @@ DoCallFallback(JSContext *cx, BaselineFr
     if (op == JSOP_FUNAPPLY && argc == 2 && args[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
         CallArgs callArgs = CallArgsFromVp(argc, vp);
         if (!GuardFunApplyArgumentsOptimization(cx, frame, callArgs))
             return false;
     }
 
     // Compute construcing and useNewGroup flags.
     bool constructing = (op == JSOP_NEW);
-    bool createSingleton = types::UseSingletonForNewObject(cx, script, pc);
+    bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc);
 
     // Try attaching a call stub.
     if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, createSingleton))
         return false;
 
     if (!MaybeCloneFunctionAtCallsite(cx, &callee, script, pc))
         return false;
 
@@ -11604,17 +11604,17 @@ ICGetElem_Arguments::Clone(JSContext *, 
 }
 
 ICSetElem_Dense::ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleObjectGroup group)
   : ICUpdatedStub(SetElem_Dense, stubCode),
     shape_(shape),
     group_(group)
 { }
 
-ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group,
+ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, ObjectGroup *group,
                                        size_t protoChainDepth)
   : ICUpdatedStub(SetElem_DenseAdd, stubCode),
     group_(group)
 {
     MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
     extra_ = protoChainDepth;
 }
 
@@ -12118,17 +12118,17 @@ static bool DoRestFallback(JSContext *cx
     unsigned numFormals = frame->numFormalArgs() - 1;
     unsigned numActuals = frame->numActualArgs();
     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
     Value *rest = frame->argv() + numFormals;
 
     ArrayObject *obj = NewDenseCopiedArray(cx, numRest, rest, nullptr);
     if (!obj)
         return false;
-    types::FixRestArgumentsType(cx, obj);
+    ObjectGroup::fixRestArgumentsGroup(cx, obj);
     res.setObject(*obj);
     return true;
 }
 
 typedef bool (*DoRestFallbackFn)(JSContext *, ICRest_Fallback *, BaselineFrame *,
                                  MutableHandleValue);
 static const VMFunction DoRestFallbackInfo =
     FunctionInfo<DoRestFallbackFn>(DoRestFallback, TailCall);
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3571,17 +3571,17 @@ class ICSetElem_DenseAdd : public ICUpda
     friend class ICStubSpace;
 
   public:
     static const size_t MAX_PROTO_CHAIN_DEPTH = 4;
 
   protected:
     HeapPtrObjectGroup group_;
 
-    ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, size_t protoChainDepth);
+    ICSetElem_DenseAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth);
 
   public:
     static size_t offsetOfGroup() {
         return offsetof(ICSetElem_DenseAdd, group_);
     }
 
     HeapPtrObjectGroup &group() {
         return group_;
@@ -3606,28 +3606,28 @@ class ICSetElem_DenseAdd : public ICUpda
 template <size_t ProtoChainDepth>
 class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd
 {
     friend class ICStubSpace;
 
     static const size_t NumShapes = ProtoChainDepth + 1;
     mozilla::Array<HeapPtrShape, NumShapes> shapes_;
 
-    ICSetElem_DenseAddImpl(JitCode *stubCode, types::ObjectGroup *group,
+    ICSetElem_DenseAddImpl(JitCode *stubCode, ObjectGroup *group,
                            const AutoShapeVector *shapes)
       : ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth)
     {
         MOZ_ASSERT(shapes->length() == NumShapes);
         for (size_t i = 0; i < NumShapes; i++)
             shapes_[i].init((*shapes)[i]);
     }
 
   public:
     static inline ICSetElem_DenseAddImpl *New(ICStubSpace *space, JitCode *code,
-                                              types::ObjectGroup *group,
+                                              ObjectGroup *group,
                                               const AutoShapeVector *shapes)
     {
         if (!code)
             return nullptr;
         return space->allocate<ICSetElem_DenseAddImpl<ProtoChainDepth> >(code, group, shapes);
     }
 
     void traceShapes(JSTracer *trc) {
@@ -4580,17 +4580,17 @@ class ICGetProp_Unboxed : public ICMonit
         bool generateStubCode(MacroAssembler &masm);
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind) | (static_cast<int32_t>(fieldType_)) << 16;
         }
 
       public:
         Compiler(JSContext *cx, ICStub *firstMonitorStub,
-                 types::ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType)
+                 ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType)
           : ICStubCompiler(cx, ICStub::GetProp_Unboxed),
             firstMonitorStub_(firstMonitorStub),
             group_(cx, group),
             fieldOffset_(fieldOffset),
             fieldType_(fieldType)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
@@ -5540,17 +5540,17 @@ class ICSetProp_Unboxed : public ICUpdat
         bool generateStubCode(MacroAssembler &masm);
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind) |
                    (static_cast<int32_t>(fieldType_) << 16);
         }
 
       public:
-        Compiler(JSContext *cx, types::ObjectGroup *group, uint32_t fieldOffset,
+        Compiler(JSContext *cx, ObjectGroup *group, uint32_t fieldOffset,
                  JSValueType fieldType)
           : ICStubCompiler(cx, ICStub::SetProp_Unboxed),
             group_(cx, group),
             fieldOffset_(fieldOffset),
             fieldType_(fieldType)
         {}
 
         ICUpdatedStub *getStub(ICStubSpace *space) {
@@ -5630,17 +5630,17 @@ class ICSetProp_TypedObject : public ICU
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind) |
                    (static_cast<int32_t>(SimpleTypeDescrKey(fieldDescr_)) << 16) |
                    (static_cast<int32_t>(layout_) << 24);
         }
 
       public:
-        Compiler(JSContext *cx, Shape *shape, types::ObjectGroup *group, uint32_t fieldOffset,
+        Compiler(JSContext *cx, Shape *shape, ObjectGroup *group, uint32_t fieldOffset,
                  SimpleTypeDescr *fieldDescr)
           : ICStubCompiler(cx, ICStub::SetProp_TypedObject),
             shape_(cx, shape),
             group_(cx, group),
             fieldOffset_(fieldOffset),
             layout_(GetTypedThingLayout(shape->getObjectClass())),
             fieldDescr_(cx, fieldDescr)
         {}
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -94,17 +94,17 @@ BaselineInspector::maybeInfoForPropertyO
         return true;
 
     MOZ_ASSERT(isValidPC(pc));
     const ICEntry &entry = icEntryFromPC(pc);
 
     ICStub *stub = entry.firstStub();
     while (stub->next()) {
         Shape *shape = nullptr;
-        types::ObjectGroup *group = nullptr;
+        ObjectGroup *group = nullptr;
         if (stub->isGetProp_Native()) {
             shape = stub->toGetProp_Native()->shape();
         } else if (stub->isSetProp_Native()) {
             shape = stub->toSetProp_Native()->shape();
         } else if (stub->isGetProp_Unboxed()) {
             group = stub->toGetProp_Unboxed()->group();
         } else if (stub->isSetProp_Unboxed()) {
             group = stub->toSetProp_Unboxed()->group();
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -88,17 +88,17 @@ class BaselineInspector
         return ICInspectorType(this, pc, ent);
     }
 
     ICStub *monomorphicStub(jsbytecode *pc);
     bool dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond);
 
   public:
     typedef Vector<Shape *, 4, JitAllocPolicy> ShapeVector;
-    typedef Vector<types::ObjectGroup *, 4, JitAllocPolicy> ObjectGroupVector;
+    typedef Vector<ObjectGroup *, 4, JitAllocPolicy> ObjectGroupVector;
     bool maybeInfoForPropertyOp(jsbytecode *pc,
                                 ShapeVector &nativeShapes,
                                 ObjectGroupVector &unboxedGroups);
 
     SetElemICInspector setElemICInspector(jsbytecode *pc) {
         return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
     }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -718,17 +718,17 @@ CodeGenerator::visitFunctionDispatch(LFu
         casesWithFallback = mir->numCases() + 1;
         lastLabel = skipTrivialBlocks(mir->getFallback())->lir()->label();
     }
 
     // Compare function pointers, except for the last case.
     for (size_t i = 0; i < casesWithFallback - 1; i++) {
         MOZ_ASSERT(i < mir->numCases());
         LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir();
-        if (types::ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) {
+        if (ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) {
             masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfGroup()),
                            ImmGCPtr(funcGroup), target->label());
         } else {
             JSFunction *func = mir->getCase(i);
             masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
         }
     }
 
@@ -759,17 +759,17 @@ CodeGenerator::visitObjectGroupDispatch(
         DebugOnly<bool> found = false;
         for (size_t j = 0; j < propTable->numEntries(); j++) {
             if (propTable->getFunction(j) != func)
                 continue;
 
             if (lastBranch.isInitialized())
                 lastBranch.emit(masm);
 
-            types::ObjectGroup *group = propTable->getObjectGroup(j);
+            ObjectGroup *group = propTable->getObjectGroup(j);
             lastBranch = MacroAssembler::BranchGCPtr(Assembler::Equal, temp, ImmGCPtr(group),
                                                      target->label());
             lastBlock = target;
             found = true;
         }
         MOZ_ASSERT(found);
     }
 
@@ -3675,18 +3675,18 @@ CodeGenerator::emitObjectOrStringResultC
         masm.jump(&ok);
 
         masm.bind(&miss);
 
         // Type set guards might miss when an object's group changes and its
         // properties become unknown, so check for this case.
         masm.loadPtr(Address(output, JSObject::offsetOfGroup()), temp);
         masm.branchTestPtr(Assembler::NonZero,
-                           Address(temp, types::ObjectGroup::offsetOfFlags()),
-                           Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok);
+                           Address(temp, ObjectGroup::offsetOfFlags()),
+                           Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok);
 
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
     saveVolatile();
@@ -3755,18 +3755,18 @@ CodeGenerator::emitValueResultChecks(LIn
 
         // Type set guards might miss when an object's group changes and its
         // properties become unknown, so check for this case.
         Label realMiss;
         masm.branchTestObject(Assembler::NotEqual, output, &realMiss);
         Register payload = masm.extractObject(output, temp1);
         masm.loadPtr(Address(payload, JSObject::offsetOfGroup()), temp1);
         masm.branchTestPtr(Assembler::NonZero,
-                           Address(temp1, types::ObjectGroup::offsetOfFlags()),
-                           Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok);
+                           Address(temp1, ObjectGroup::offsetOfFlags()),
+                           Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok);
         masm.bind(&realMiss);
 
         masm.assumeUnreachable("MIR instruction returned value with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
@@ -3942,17 +3942,17 @@ void
 CodeGenerator::visitNewArrayCallVM(LNewArray *lir)
 {
     Register objReg = ToRegister(lir->output());
 
     MOZ_ASSERT(!lir->isCall());
     saveLive(lir);
 
     JSObject *templateObject = lir->mir()->templateObject();
-    types::ObjectGroup *group =
+    ObjectGroup *group =
         templateObject->isSingleton() ? nullptr : templateObject->group();
 
     pushArg(Imm32(lir->mir()->allocatingBehaviour()));
     pushArg(ImmGCPtr(group));
     pushArg(Imm32(lir->mir()->count()));
 
     callVM(NewDenseArrayInfo, lir);
 
@@ -4333,25 +4333,25 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox
     Register temp = ToRegister(lir->temp());
     Label bail;
 
     // obj->group()
     masm.loadPtr(Address(object, JSObject::offsetOfGroup()), temp);
 
     // Guard that the object has the same representation as the one produced for
     // SIMD value-type.
-    Address clasp(temp, types::ObjectGroup::offsetOfClasp());
+    Address clasp(temp, ObjectGroup::offsetOfClasp());
     static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent");
     masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_),
                    &bail);
 
     // obj->type()->typeDescr()
     // The previous class pointer comparison implies that the addendumKind is
     // Addendum_TypeDescr.
-    masm.loadPtr(Address(temp, types::ObjectGroup::offsetOfAddendum()), temp);
+    masm.loadPtr(Address(temp, ObjectGroup::offsetOfAddendum()), temp);
 
     // Check for the /Kind/ reserved slot of the TypeDescr.  This is an Int32
     // Value which is equivalent to the object class check.
     static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
     Address typeDescrKind(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND));
     masm.assertTestInt32(Assembler::Equal, typeDescrKind,
       "MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())");
     masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrKind), Imm32(js::type::Simd), &bail);
@@ -4805,17 +4805,17 @@ CodeGenerator::visitTypedArrayElements(L
 
 void
 CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
 
     masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), out);
-    masm.loadPtr(Address(out, types::ObjectGroup::offsetOfAddendum()), out);
+    masm.loadPtr(Address(out, ObjectGroup::offsetOfAddendum()), out);
 }
 
 void
 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
 {
     Register obj = ToRegister(lir->object());
     Register out = ToRegister(lir->output());
 
--- a/js/src/jit/Disassembler.h
+++ b/js/src/jit/Disassembler.h
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_Disassembler_h
 #define jit_Disassembler_h
 
+#include "jit/MacroAssembler.h"
 #include "jit/Registers.h"
 
 namespace js {
 namespace jit {
 
 namespace Disassembler {
 
 class ComplexAddress {
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2875,35 +2875,32 @@ jit::ConvertLinearInequality(TempAllocat
     MCompare *compare = MCompare::New(alloc, lhsDef, rhsDef, op);
     block->insertAtEnd(compare);
     compare->setCompareType(MCompare::Compare_Int32);
 
     return compare;
 }
 
 static bool
-AnalyzePoppedThis(JSContext *cx, types::ObjectGroup *group,
+AnalyzePoppedThis(JSContext *cx, ObjectGroup *group,
                   MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted,
                   HandlePlainObject baseobj,
                   Vector<types::TypeNewScript::Initializer> *initializerList,
                   Vector<PropertyName *> *accessedProperties,
                   bool *phandled)
 {
     // Determine the effect that a use of the |this| value when calling |new|
     // on a script has on the properties definitely held by the new object.
 
     if (ins->isCallSetProperty()) {
         MCallSetProperty *setprop = ins->toCallSetProperty();
 
         if (setprop->object() != thisValue)
             return true;
 
-        // Don't use GetAtomId here, we need to watch for SETPROP on
-        // integer properties and bail out. We can't mark the aggregate
-        // JSID_VOID type property as being in a definite slot.
         if (setprop->name() == cx->names().prototype ||
             setprop->name() == cx->names().proto ||
             setprop->name() == cx->names().constructor)
         {
             return true;
         }
 
         // Ignore assignments to properties that were already written to.
@@ -3011,17 +3008,17 @@ static int
 CmpInstructions(const void *a, const void *b)
 {
     return (*static_cast<MInstruction * const *>(a))->id() -
            (*static_cast<MInstruction * const *>(b))->id();
 }
 
 bool
 jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
-                                        types::ObjectGroup *group, HandlePlainObject baseobj,
+                                        ObjectGroup *group, HandlePlainObject baseobj,
                                         Vector<types::TypeNewScript::Initializer> *initializerList)
 {
     MOZ_ASSERT(cx->zone()->types.activeAnalysis);
 
     // When invoking 'new' on the specified script, try to find some properties
     // which will definitely be added to the created object before it has a
     // chance to escape and be accessed elsewhere.
 
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -165,17 +165,17 @@ ConvertLinearSum(TempAllocator &alloc, M
 
 // Convert the test 'sum >= 0' to a comparison, adding any necessary
 // instructions to the end of block.
 MCompare *
 ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum);
 
 bool
 AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun,
-                                   types::ObjectGroup *group, HandlePlainObject baseobj,
+                                   ObjectGroup *group, HandlePlainObject baseobj,
                                    Vector<types::TypeNewScript::Initializer> *initializerList);
 
 bool
 AnalyzeArgumentsUsage(JSContext *cx, JSScript *script);
 
 bool
 DeadIfUnused(const MDefinition *def);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -354,17 +354,17 @@ IonBuilder::getPolyCallTargets(types::Te
 
     if (!targets.reserve(objCount))
         return false;
     for (unsigned i = 0; i < objCount; i++) {
         JSObject *obj = calleeTypes->getSingleton(i);
         if (obj) {
             MOZ_ASSERT(obj->isSingleton());
         } else {
-            types::ObjectGroup *group = calleeTypes->getGroup(i);
+            ObjectGroup *group = calleeTypes->getGroup(i);
             if (!group)
                 continue;
 
             obj = group->maybeInterpretedFunction();
             if (!obj) {
                 targets.clear();
                 return true;
             }
@@ -5317,17 +5317,17 @@ IonBuilder::inlineCalls(CallInfo &callIn
         // inlineSingleCall() changed |current| to the inline return block.
         MBasicBlock *inlineReturnBlock = current;
         setCurrent(dispatchBlock);
 
         // Connect the inline path to the returnBlock.
         //
         // Note that guarding is on the original function pointer even
         // if there is a clone, since cloning occurs at the callsite.
-        types::ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group();
+        ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group();
         dispatch->addCase(original, funcGroup, inlineBlock);
 
         MDefinition *retVal = inlineReturnBlock->peek(-1);
         retPhi->addInput(retVal);
         inlineReturnBlock->end(MGoto::New(alloc(), returnBlock));
         if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock))
             return false;
     }
@@ -5538,17 +5538,17 @@ IonBuilder::createThisScriptedSingleton(
     if (!templateObject)
         return nullptr;
     if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>())
         return nullptr;
     if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
         return nullptr;
 
     types::TypeSetObjectKey *templateObjectKey = types::TypeSetObjectKey::get(templateObject->group());
-    if (templateObjectKey->hasFlags(constraints(), types::OBJECT_FLAG_NEW_SCRIPT_CLEARED))
+    if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
         return nullptr;
 
     types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript());
     if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject)))
         return nullptr;
 
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
@@ -6245,24 +6245,24 @@ IonBuilder::jsop_newarray(uint32_t count
     else
         templateObject->as<ArrayObject>().clearShouldConvertDoubleElements();
     return true;
 }
 
 bool
 IonBuilder::jsop_newarray_copyonwrite()
 {
-    ArrayObject *templateObject = types::GetCopyOnWriteObject(script(), pc);
+    ArrayObject *templateObject = ObjectGroup::getCopyOnWriteObject(script(), pc);
 
     // The baseline compiler should have ensured the template object has a type
     // with the copy on write flag set already. During the arguments usage
     // analysis the baseline compiler hasn't run yet, however, though in this
     // case the template object's type doesn't matter.
     MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage,
-                  templateObject->group()->hasAnyFlags(types::OBJECT_FLAG_COPY_ON_WRITE));
+                  templateObject->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
 
     MNewArrayCopyOnWrite *ins =
         MNewArrayCopyOnWrite::New(alloc(), constraints(), templateObject,
                                   templateObject->group()->initialHeap(constraints()));
 
     current->add(ins);
     current->push(ins);
 
@@ -6321,17 +6321,17 @@ IonBuilder::jsop_initelem_array()
     // intializer, and that arrays are marked as non-packed when writing holes
     // to them during initialization.
     bool needStub = false;
     if (obj->isUnknownValue()) {
         needStub = true;
     } else {
         types::TypeSetObjectKey *initializer = obj->resultTypeSet()->getObject(0);
         if (value->type() == MIRType_MagicHole) {
-            if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
+            if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED))
                 needStub = true;
         } else if (!initializer->unknownProperties()) {
             types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID);
             if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) {
                 elemTypes.freeze(constraints());
                 needStub = true;
             }
         }
@@ -7624,17 +7624,17 @@ IonBuilder::checkTypedObjectIndexInBound
     int32_t lenOfAll;
     MDefinition *length;
     if (objPrediction.hasKnownArrayLength(&lenOfAll)) {
         length = constantInt(lenOfAll);
 
         // If we are not loading the length from the object itself, only
         // optimize if the array buffer can't have been neutered.
         types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-        if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) {
+        if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) {
             trackOptimizationOutcome(TrackedOutcome::TypedObjectNeutered);
             return false;
         }
     } else {
         trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange);
         return false;
     }
 
@@ -9077,34 +9077,34 @@ IonBuilder::jsop_length_fastPath()
     }
 
     if (obj->mightBeType(MIRType_Object)) {
         types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
 
         // Compute the length for array objects.
         if (objTypes &&
             objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
-            !objTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_LENGTH_OVERFLOW))
+            !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
         {
             current->pop();
             MElements *elements = MElements::New(alloc(), obj);
             current->add(elements);
 
             // Read length.
             MArrayLength *length = MArrayLength::New(alloc(), elements);
             current->add(length);
             current->push(length);
             return true;
         }
 
         // Compute the length for array typed objects.
         TypedObjectPrediction prediction = typedObjectPrediction(obj);
         if (!prediction.isUseless()) {
             types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-            if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+            if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
                 return false;
 
             MInstruction *length;
             int32_t sizedLength;
             if (prediction.hasKnownArrayLength(&sizedLength)) {
                 obj->setImplicitlyUsedUnchecked();
                 length = MConstant::New(alloc(), Int32Value(sizedLength));
             } else {
@@ -9225,17 +9225,17 @@ IonBuilder::getDefiniteSlot(types::Tempo
     // objects. Later, this will let us elide baseline IC stubs for preliminary
     // objects, which often have a different number of fixed slots from
     // subsequent objects.
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeSetObjectKey *key = types->getObject(i);
         if (!key)
             continue;
 
-        if (types::ObjectGroup *group = key->maybeGroup()) {
+        if (ObjectGroup *group = key->maybeGroup()) {
             if (group->newScript() && !group->newScript()->analyzed()) {
                 addAbortedNewScriptPropertiesGroup(group);
                 trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo);
                 return UINT32_MAX;
             }
         }
     }
 
@@ -9533,17 +9533,17 @@ IonBuilder::annotateGetPropertyCache(MDe
 
     InlinePropertyTable *inlinePropTable = getPropCache->initInlinePropertyTable(alloc(), pc);
     if (!inlinePropTable)
         return false;
 
     // Ensure that the relevant property typeset for each group is
     // is a single-object typeset containing a JSFunction
     for (unsigned int i = 0; i < objCount; i++) {
-        types::ObjectGroup *group = objTypes->getGroup(i);
+        ObjectGroup *group = objTypes->getGroup(i);
         if (!group)
             continue;
         types::TypeSetObjectKey *key = types::TypeSetObjectKey::get(group);
         if (key->unknownProperties() || !key->hasTenuredProto() || !key->proto().isObject())
             continue;
 
         const Class *clasp = key->clasp();
         if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name))
@@ -9983,17 +9983,17 @@ IonBuilder::getPropTryScalarPropOfTypedO
                                               int32_t fieldOffset,
                                               TypedObjectPrediction fieldPrediction)
 {
     // Must always be loading the same scalar type
     Scalar::Type fieldType = fieldPrediction.scalarType();
 
     // Don't optimize if the typed object might be neutered.
     types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-    if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+    if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     trackOptimizationSuccess();
     *emitted = true;
 
     LinearSum byteOffset(alloc());
     if (!byteOffset.add(fieldOffset))
         setForceAbort();
@@ -10005,17 +10005,17 @@ bool
 IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj,
                                                  int32_t fieldOffset,
                                                  TypedObjectPrediction fieldPrediction,
                                                  PropertyName *name)
 {
     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
 
     types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-    if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+    if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     trackOptimizationSuccess();
     *emitted = true;
 
     LinearSum byteOffset(alloc());
     if (!byteOffset.add(fieldOffset))
         setForceAbort();
@@ -10027,17 +10027,17 @@ bool
 IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted,
                                                MDefinition *typedObj,
                                                int32_t fieldOffset,
                                                TypedObjectPrediction fieldPrediction,
                                                size_t fieldIndex)
 {
     // Don't optimize if the typed object might be neutered.
     types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-    if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+    if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     // OK, perform the optimization
 
     // Identify the type object for the field.
     MDefinition *type = loadTypedObjectType(typedObj);
     MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex);
 
@@ -10425,17 +10425,17 @@ IonBuilder::getPropTryInlineAccess(bool 
         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
         *emitted = true;
         return true;
     }
 
     if (nativeShapes.empty() && unboxedGroups.length() == 1) {
         spew("Inlining monomorphic unboxed GETPROP");
 
-        types::ObjectGroup *group = unboxedGroups[0];
+        ObjectGroup *group = unboxedGroups[0];
 
         // Failures in this group guard should be treated the same as a shape guard failure.
         obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false,
                                      Bailout_ShapeGuard);
         current->add(obj->toInstruction());
 
         if (failedShapeGuard_)
             obj->toGuardObjectGroup()->setNotMovable();
@@ -10599,17 +10599,17 @@ IonBuilder::tryInnerizeWindow(MDefinitio
     JSObject *inner = GetInnerObject(singleton);
     if (inner == singleton || inner != &script()->global())
         return obj;
 
     // When we navigate, the outer object is brain transplanted and we'll mark
     // its ObjectGroup as having unknown properties. The type constraint we add
     // here will invalidate JIT code when this happens.
     types::TypeSetObjectKey *key = types::TypeSetObjectKey::get(singleton);
-    if (key->hasFlags(constraints(), types::OBJECT_FLAG_UNKNOWN_PROPERTIES))
+    if (key->hasFlags(constraints(), OBJECT_FLAG_UNKNOWN_PROPERTIES))
         return obj;
 
     obj->setImplicitlyUsedUnchecked();
     return constant(ObjectValue(script()->global()));
 }
 
 bool
 IonBuilder::getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name,
@@ -10878,17 +10878,17 @@ IonBuilder::setPropTryReferencePropOfTyp
                                                  int32_t fieldOffset,
                                                  MDefinition *value,
                                                  TypedObjectPrediction fieldPrediction,
                                                  PropertyName *name)
 {
     ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType();
 
     types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-    if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+    if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     LinearSum byteOffset(alloc());
     if (!byteOffset.add(fieldOffset))
         setForceAbort();
 
     if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value, name))
         return true;
@@ -10907,17 +10907,17 @@ IonBuilder::setPropTryScalarPropOfTypedO
                                               MDefinition *value,
                                               TypedObjectPrediction fieldPrediction)
 {
     // Must always be loading the same scalar type
     Scalar::Type fieldType = fieldPrediction.scalarType();
 
     // Don't optimize if the typed object might be neutered.
     types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-    if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
+    if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED))
         return true;
 
     LinearSum byteOffset(alloc());
     if (!byteOffset.add(fieldOffset))
         setForceAbort();
 
     if (!storeScalarTypedObjectValue(obj, byteOffset, fieldType, false, value))
         return false;
@@ -11104,17 +11104,17 @@ IonBuilder::setPropTryInlineAccess(bool 
         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
         *emitted = true;
         return true;
     }
 
     if (nativeShapes.empty() && unboxedGroups.length() == 1) {
         spew("Inlining monomorphic unboxed SETPROP");
 
-        types::ObjectGroup *group = unboxedGroups[0];
+        ObjectGroup *group = unboxedGroups[0];
 
         // Failures in this group guard should be treated the same as a shape guard failure.
         obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false,
                                     Bailout_ShapeGuard);
         current->add(obj->toInstruction());
 
         if (failedShapeGuard_)
             obj->toGuardObjectGroup()->setNotMovable();
@@ -11239,17 +11239,17 @@ IonBuilder::jsop_regexp(RegExpObject *re
     // by getting or setting properties on it.
     //
     // First, make sure the regex is one we can safely optimize. Lowering can
     // then check if this regex object only flows into known natives and can
     // avoid cloning in this case.
 
     bool mustClone = true;
     types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global());
-    if (!globalKey->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
+    if (!globalKey->hasFlags(constraints(), OBJECT_FLAG_REGEXP_FLAGS_SET)) {
 #ifdef DEBUG
         // Only compare the statics if the one on script()->global() has been
         // instantiated.
         if (script()->global().hasRegExpStatics()) {
             RegExpStatics *res = script()->global().getAlreadyCreatedRegExpStatics();
             MOZ_ASSERT(res);
             uint32_t origFlags = reobj->getFlags();
             uint32_t staticsFlags = res->getFlags();
@@ -11628,17 +11628,17 @@ bool
 IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall)
 {
     JSScript *outerScript = ScopeCoordinateFunctionScript(script(), pc);
     if (!outerScript || !outerScript->treatAsRunOnce())
         return false;
 
     types::TypeSetObjectKey *funKey =
         types::TypeSetObjectKey::get(outerScript->functionNonDelazifying());
-    if (funKey->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED))
+    if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED))
         return false;
 
     // The script this aliased var operation is accessing will run only once,
     // so there will be only one call object and the aliased var access can be
     // compiled in the same manner as a global access. We still need to find
     // the call object though.
 
     // Look for the call object on the current script's function's scope chain.
@@ -12098,17 +12098,17 @@ IonBuilder::typedObjectPrediction(types:
         return TypedObjectPrediction();
 
     // And only known objects.
     if (types->unknownObject())
         return TypedObjectPrediction();
 
     TypedObjectPrediction out;
     for (uint32_t i = 0; i < types->getObjectCount(); i++) {
-        types::ObjectGroup *group = types->getGroup(i);
+        ObjectGroup *group = types->getGroup(i);
         if (!group || !types::TypeSetObjectKey::get(group)->hasStableClassAndProto(constraints()))
             return TypedObjectPrediction();
 
         if (!IsTypedObjectClass(group->clasp()))
             return TypedObjectPrediction();
 
         out.addDescr(group->typeDescr());
     }
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -483,17 +483,17 @@ GeneratePrototypeGuards(JSContext *cx, I
      * in reshaping the holder, and thus the failure of the shape guard.
      */
     MOZ_ASSERT(obj != holder);
 
     if (obj->hasUncacheableProto()) {
         // Note: objectReg and scratchReg may be the same register, so we cannot
         // use objectReg in the rest of this function.
         masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg);
-        Address proto(scratchReg, types::ObjectGroup::offsetOfProto());
+        Address proto(scratchReg, ObjectGroup::offsetOfProto());
         masm.branchPtr(Assembler::NotEqual, proto,
                        ImmMaybeNurseryPtr(obj->getProto()), failures);
     }
 
     JSObject *pobj = IsCacheableDOMProxy(obj)
                      ? obj->getTaggedProto().toObjectOrNull()
                      : obj->getProto();
     if (!pobj)
@@ -1892,17 +1892,17 @@ IonCache::destroy()
 
 // Jump to failure if a value being written is not a property for obj/id.
 // This might clobber |object|.
 static void
 CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id,
                      Register object, ConstantOrRegister value, Label *failure)
 {
     TypedOrValueRegister valReg = value.reg();
-    types::ObjectGroup *group = obj->group();
+    ObjectGroup *group = obj->group();
     if (group->unknownProperties())
         return;
     types::HeapTypeSet *propTypes = group->maybeGetProperty(id);
     MOZ_ASSERT(propTypes);
 
     // guardTypeSet can read from type sets without triggering read barriers.
     types::TypeSet::readBarrier(propTypes);
 
@@ -1924,17 +1924,17 @@ GenerateSetSlot(JSContext *cx, MacroAsse
 
     // Guard that the incoming value is in the type set for the property
     // if a type barrier is required.
     if (needsTypeBarrier) {
         // We can't do anything that would change the HeapTypeSet, so
         // just guard that it's already there.
 
         // Obtain and guard on the ObjectGroup of the object.
-        types::ObjectGroup *group = obj->group();
+        ObjectGroup *group = obj->group();
         masm.branchPtr(Assembler::NotEqual,
                        Address(object, JSObject::offsetOfGroup()),
                        ImmGCPtr(group), &failures);
 
         if (checkTypeset) {
             masm.push(object);
             CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &barrierFailure);
             masm.pop(object);
@@ -2458,17 +2458,17 @@ SetPropertyIC::attachCallSetter(JSContex
     masm.bind(&failure);
     attacher.jumpNextStub(masm);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "setter call");
 }
 
 static void
 GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                NativeObject *obj, Shape *oldShape, types::ObjectGroup *oldGroup,
+                NativeObject *obj, Shape *oldShape, ObjectGroup *oldGroup,
                 Register object, ConstantOrRegister value,
                 bool checkTypeset)
 {
     MOZ_ASSERT(obj->isNative());
 
     Label failures;
 
     // Guard the type of the object
@@ -2515,17 +2515,17 @@ GenerateAddSlot(JSContext *cx, MacroAsse
         // Changing object's group from a partially to fully initialized group,
         // per the acquired properties analysis. Only change the group if the
         // old group still has a newScript.
         Label noTypeChange, skipPop;
 
         masm.push(object);
         masm.loadPtr(Address(object, JSObject::offsetOfGroup()), object);
         masm.branchPtr(Assembler::Equal,
-                       Address(object, types::ObjectGroup::offsetOfAddendum()),
+                       Address(object, ObjectGroup::offsetOfAddendum()),
                        ImmWord(0),
                        &noTypeChange);
         masm.pop(object);
 
         Address groupAddr(object, JSObject::offsetOfGroup());
         if (cx->zone()->needsIncrementalBarrier())
             masm.callPreBarrier(groupAddr, MIRType_ObjectGroup);
         masm.storePtr(ImmGCPtr(obj->group()), groupAddr);
@@ -2574,17 +2574,17 @@ SetPropertyIC::attachAddSlot(JSContext *
     GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldGroup, object(), value(), checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "adding");
 }
 
 static bool
 CanInlineSetPropTypeCheck(JSObject *obj, jsid id, ConstantOrRegister val, bool *checkTypeset)
 {
     bool shouldCheck = false;
-    types::ObjectGroup *group = obj->group();
+    ObjectGroup *group = obj->group();
     if (!group->unknownProperties()) {
         types::HeapTypeSet *propTypes = group->maybeGetProperty(id);
         if (!propTypes)
             return false;
         if (!propTypes->unknown()) {
             if (obj->isSingleton() && !propTypes->nonConstantProperty())
                 return false;
             shouldCheck = true;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -501,20 +501,20 @@ IonBuilder::inlineArrayPopShift(CallInfo
     if (returnType == MIRType_Undefined || returnType == MIRType_Null)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
     // Pop and shift are only handled for dense arrays that have never been
     // used in an iterator: popping elements does not account for suppressing
     // deleted properties in active iterators.
-    types::ObjectGroupFlags unhandledFlags =
-        types::OBJECT_FLAG_SPARSE_INDEXES |
-        types::OBJECT_FLAG_LENGTH_OVERFLOW |
-        types::OBJECT_FLAG_ITERATED;
+    ObjectGroupFlags unhandledFlags =
+        OBJECT_FLAG_SPARSE_INDEXES |
+        OBJECT_FLAG_LENGTH_OVERFLOW |
+        OBJECT_FLAG_ITERATED;
 
     MDefinition *obj = callInfo.thisArg();
     types::TemporaryTypeSet *thisTypes = obj->resultTypeSet();
     if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
@@ -525,17 +525,17 @@ IonBuilder::inlineArrayPopShift(CallInfo
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
     obj = addMaybeCopyElementsForWrite(obj);
 
     types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
-    bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED);
+    bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
                                                        obj, nullptr, returnTypes);
     if (barrier != BarrierKind::NoBarrier)
         returnType = MIRType_Value;
 
     MArrayPopShift *ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
@@ -638,18 +638,18 @@ IonBuilder::inlineArrayPush(CallInfo &ca
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
-                                  types::OBJECT_FLAG_LENGTH_OVERFLOW))
+    if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
+                                  OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
     if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
         return InliningStatus_NotInlined;
@@ -706,54 +706,54 @@ IonBuilder::inlineArrayConcat(CallInfo &
     // |this| and the argument must be dense arrays.
     types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
     types::TemporaryTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet();
     if (!thisTypes || !argTypes)
         return InliningStatus_NotInlined;
 
     if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
-                                  types::OBJECT_FLAG_LENGTH_OVERFLOW))
+    if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
+                                  OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
     if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_)
         return InliningStatus_NotInlined;
-    if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES |
-                                 types::OBJECT_FLAG_LENGTH_OVERFLOW))
+    if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
+                                 OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
     // Watch out for indexed properties on the prototype.
     if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
         return InliningStatus_NotInlined;
     }
 
     // Require the 'this' types to have a specific type matching the current
     // global, so we can create the result object inline.
     if (thisTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
 
-    types::ObjectGroup *thisGroup = thisTypes->getGroup(0);
+    ObjectGroup *thisGroup = thisTypes->getGroup(0);
     if (!thisGroup)
         return InliningStatus_NotInlined;
     types::TypeSetObjectKey *thisKey = types::TypeSetObjectKey::get(thisGroup);
     if (thisKey->unknownProperties())
         return InliningStatus_NotInlined;
 
     // Don't inline if 'this' is packed and the argument may not be packed
     // (the result array will reuse the 'this' type).
-    if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) &&
-        argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
+    if (!thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED) &&
+        argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
     // Constraints modeling this concat have not been generated by inference,
     // so check that type information already reflects possible side effects of
     // this call.
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4316,33 +4316,33 @@ jit::ElementAccessIsAnyTypedArray(types:
     *arrayType = types->getSharedTypedArrayType(constraints);
     return *arrayType != Scalar::MaxTypedArrayViewType;
 }
 
 bool
 jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj)
 {
     types::TemporaryTypeSet *types = obj->resultTypeSet();
-    return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED);
+    return types && !types->hasObjectFlags(constraints, OBJECT_FLAG_NON_PACKED);
 }
 
 bool
 jit::ElementAccessMightBeCopyOnWrite(types::CompilerConstraintList *constraints, MDefinition *obj)
 {
     types::TemporaryTypeSet *types = obj->resultTypeSet();
-    return !types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_COPY_ON_WRITE);
+    return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE);
 }
 
 bool
 jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,
                                           MDefinition *obj)
 {
     types::TemporaryTypeSet *types = obj->resultTypeSet();
 
-    if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW))
+    if (!types || types->hasObjectFlags(constraints, OBJECT_FLAG_LENGTH_OVERFLOW))
         return true;
 
     return types::TypeCanHaveExtraIndexedProperties(constraints, types);
 }
 
 MIRType
 jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6314,17 +6314,17 @@ class MStringSplit
         return getOperand(0);
     }
     MDefinition *separator() const {
         return getOperand(1);
     }
     JSObject *templateObject() const {
         return &getOperand(2)->toConstant()->value().toObject();
     }
-    types::ObjectGroup *group() const {
+    ObjectGroup *group() const {
         return templateObject()->group();
     }
     bool possiblyCalls() const MOZ_OVERRIDE {
         return true;
     }
     virtual AliasSet getAliasSet() const MOZ_OVERRIDE {
         // Although this instruction returns a new array, we don't have to mark
         // it as store instruction, see also MNewArray.
@@ -7161,17 +7161,17 @@ struct LambdaFunctionInfo
     bool useSingletonForClone;
 
     explicit LambdaFunctionInfo(JSFunction *fun)
       : fun(fun), flags(fun->flags()),
         scriptOrLazyScript(fun->hasScript()
                            ? (gc::Cell *) fun->nonLazyScript()
                            : (gc::Cell *) fun->lazyScript()),
         singletonType(fun->isSingleton()),
-        useSingletonForClone(types::UseSingletonForClone(fun))
+        useSingletonForClone(ObjectGroup::useSingletonForClone(fun))
     {}
 
     LambdaFunctionInfo(const LambdaFunctionInfo &info)
       : fun((JSFunction *) info.fun), flags(info.flags),
         scriptOrLazyScript(info.scriptOrLazyScript),
         singletonType(info.singletonType),
         useSingletonForClone(info.useSingletonForClone)
     {}
@@ -7182,17 +7182,17 @@ class MLambda
     public SingleObjectPolicy::Data
 {
     LambdaFunctionInfo info_;
 
     MLambda(types::CompilerConstraintList *constraints, MDefinition *scopeChain, MConstant *cst)
       : MBinaryInstruction(scopeChain, cst), info_(&cst->value().toObject().as<JSFunction>())
     {
         setResultType(MIRType_Object);
-        if (!info().fun->isSingleton() && !types::UseSingletonForClone(info().fun))
+        if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun))
             setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun));
     }
 
   public:
     INSTRUCTION_HEADER(Lambda)
 
     static MLambda *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
                         MDefinition *scopeChain, MConstant *fun)
@@ -7220,17 +7220,17 @@ class MLambdaArrow
 {
     LambdaFunctionInfo info_;
 
     MLambdaArrow(types::CompilerConstraintList *constraints, MDefinition *scopeChain,
                  MDefinition *this_, JSFunction *fun)
       : MBinaryInstruction(scopeChain, this_), info_(fun)
     {
         setResultType(MIRType_Object);
-        MOZ_ASSERT(!types::UseSingletonForClone(fun));
+        MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun));
         if (!fun->isSingleton())
             setResultTypeSet(MakeSingletonTypeSet(constraints, fun));
     }
 
   public:
     INSTRUCTION_HEADER(LambdaArrow)
 
     static MLambdaArrow *New(TempAllocator &alloc, types::CompilerConstraintList *constraints,
@@ -9196,20 +9196,20 @@ class MStoreFixedSlot
 };
 
 typedef Vector<JSObject *, 4, JitAllocPolicy> ObjectVector;
 typedef Vector<bool, 4, JitAllocPolicy> BoolVector;
 
 class InlinePropertyTable : public TempObject
 {
     struct Entry : public TempObject {
-        AlwaysTenured<types::ObjectGroup *> group;
+        AlwaysTenured<ObjectGroup *> group;
         AlwaysTenuredFunction func;
 
-        Entry(types::ObjectGroup *group, JSFunction *func)
+        Entry(ObjectGroup *group, JSFunction *func)
           : group(group), func(func)
         { }
     };
 
     jsbytecode *pc_;
     MResumePoint *priorResumePoint_;
     Vector<Entry *, 4, JitAllocPolicy> entries_;
 
@@ -9227,25 +9227,25 @@ class InlinePropertyTable : public TempO
         priorResumePoint_ = nullptr;
         return rp;
     }
 
     jsbytecode *pc() const {
         return pc_;
     }
 
-    bool addEntry(TempAllocator &alloc, types::ObjectGroup *group, JSFunction *func) {
+    bool addEntry(TempAllocator &alloc, ObjectGroup *group, JSFunction *func) {
         return entries_.append(new(alloc) Entry(group, func));
     }
 
     size_t numEntries() const {
         return entries_.length();
     }
 
-    types::ObjectGroup *getObjectGroup(size_t i) const {
+    ObjectGroup *getObjectGroup(size_t i) const {
         MOZ_ASSERT(i < numEntries());
         return entries_[i]->group;
     }
 
     JSFunction *getFunction(size_t i) const {
         MOZ_ASSERT(i < numEntries());
         return entries_[i]->func;
     }
@@ -9375,17 +9375,17 @@ class MGetPropertyPolymorphic
         // The shape to guard against.
         Shape *objShape;
 
         // The property to laod.
         Shape *shape;
     };
 
     Vector<Entry, 4, JitAllocPolicy> nativeShapes_;
-    Vector<types::ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_;
+    Vector<ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_;
     AlwaysTenuredPropertyName name_;
 
     MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name)
       : MUnaryInstruction(obj),
         nativeShapes_(alloc),
         unboxedGroups_(alloc),
         name_(name)
     {
@@ -9410,32 +9410,32 @@ class MGetPropertyPolymorphic
     }
 
     bool addShape(Shape *objShape, Shape *shape) {
         Entry entry;
         entry.objShape = objShape;
         entry.shape = shape;
         return nativeShapes_.append(entry);
     }
-    bool addUnboxedGroup(types::ObjectGroup *group) {
+    bool addUnboxedGroup(ObjectGroup *group) {
         return unboxedGroups_.append(group);
     }
     size_t numShapes() const {
         return nativeShapes_.length();
     }
     Shape *objShape(size_t i) const {
         return nativeShapes_[i].objShape;
     }
     Shape *shape(size_t i) const {
         return nativeShapes_[i].shape;
     }
     size_t numUnboxedGroups() const {
         return unboxedGroups_.length();
     }
-    types::ObjectGroup *unboxedGroup(size_t i) const {
+    ObjectGroup *unboxedGroup(size_t i) const {
         return unboxedGroups_[i];
     }
     PropertyName *name() const {
         return name_;
     }
     MDefinition *obj() const {
         return getOperand(0);
     }
@@ -9457,17 +9457,17 @@ class MSetPropertyPolymorphic
         // The shape to guard against.
         Shape *objShape;
 
         // The property to load.
         Shape *shape;
     };
 
     Vector<Entry, 4, JitAllocPolicy> nativeShapes_;
-    Vector<types::ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_;
+    Vector<ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_;
     AlwaysTenuredPropertyName name_;
     bool needsBarrier_;
 
     MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value,
                             PropertyName *name)
       : MBinaryInstruction(obj, value),
         nativeShapes_(alloc),
         unboxedGroups_(alloc),
@@ -9485,32 +9485,32 @@ class MSetPropertyPolymorphic
     }
 
     bool addShape(Shape *objShape, Shape *shape) {
         Entry entry;
         entry.objShape = objShape;
         entry.shape = shape;
         return nativeShapes_.append(entry);
     }
-    bool addUnboxedGroup(types::ObjectGroup *group) {
+    bool addUnboxedGroup(ObjectGroup *group) {
         return unboxedGroups_.append(group);
     }
     size_t numShapes() const {
         return nativeShapes_.length();
     }
     Shape *objShape(size_t i) const {
         return nativeShapes_[i].objShape;
     }
     Shape *shape(size_t i) const {
         return nativeShapes_[i].shape;
     }
     size_t numUnboxedGroups() const {
         return unboxedGroups_.length();
     }
-    types::ObjectGroup *unboxedGroup(size_t i) const {
+    ObjectGroup *unboxedGroup(size_t i) const {
         return unboxedGroups_[i];
     }
     PropertyName *name() const {
         return name_;
     }
     MDefinition *obj() const {
         return getOperand(0);
     }
@@ -9534,20 +9534,20 @@ class MDispatchInstruction
     public SingleObjectPolicy::Data
 {
     // Map from JSFunction* -> MBasicBlock.
     struct Entry {
         JSFunction *func;
         // If |func| has a singleton group, |funcGroup| is null. Otherwise,
         // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards
         // on the group instead of directly on the function.
-        types::ObjectGroup *funcGroup;
+        ObjectGroup *funcGroup;
         MBasicBlock *block;
 
-        Entry(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block)
+        Entry(JSFunction *func, ObjectGroup *funcGroup, MBasicBlock *block)
           : func(func), funcGroup(funcGroup), block(block)
         { }
     };
     Vector<Entry, 4, JitAllocPolicy> map_;
 
     // An optional fallback path that uses MCall.
     MBasicBlock *fallback_;
     MUse operand_;
@@ -9606,26 +9606,26 @@ class MDispatchInstruction
     MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(i < numSuccessors());
         if (i == map_.length())
             return fallback_;
         return map_[i].block;
     }
 
   public:
-    void addCase(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) {
+    void addCase(JSFunction *func, ObjectGroup *funcGroup, MBasicBlock *block) {
         map_.append(Entry(func, funcGroup, block));
     }
     uint32_t numCases() const {
         return map_.length();
     }
     JSFunction *getCase(uint32_t i) const {
         return map_[i].func;
     }
-    types::ObjectGroup *getCaseObjectGroup(uint32_t i) const {
+    ObjectGroup *getCaseObjectGroup(uint32_t i) const {
         return map_[i].funcGroup;
     }
     MBasicBlock *getCaseBlock(uint32_t i) const {
         return map_[i].block;
     }
 
     bool hasFallback() const {
         return bool(fallback_);
@@ -9853,44 +9853,44 @@ class MGuardShapePolymorphic
     }
 };
 
 // Guard on an object's group, inclusively or exclusively.
 class MGuardObjectGroup
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    AlwaysTenured<types::ObjectGroup *> group_;
+    AlwaysTenured<ObjectGroup *> group_;
     bool bailOnEquality_;
     BailoutKind bailoutKind_;
 
-    MGuardObjectGroup(MDefinition *obj, types::ObjectGroup *group, bool bailOnEquality,
+    MGuardObjectGroup(MDefinition *obj, ObjectGroup *group, bool bailOnEquality,
                       BailoutKind bailoutKind)
       : MUnaryInstruction(obj),
         group_(group),
         bailOnEquality_(bailOnEquality),
         bailoutKind_(bailoutKind)
     {
         setGuard();
         setMovable();
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(GuardObjectGroup)
 
-    static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, types::ObjectGroup *group,
+    static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, ObjectGroup *group,
                                   bool bailOnEquality, BailoutKind bailoutKind) {
         return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind);
     }
 
     MDefinition *obj() const {
         return getOperand(0);
     }
-    const types::ObjectGroup *group() const {
+    const ObjectGroup *group() const {
         return group_;
     }
     bool bailOnEquality() const {
         return bailOnEquality_;
     }
     BailoutKind bailoutKind() const {
         return bailoutKind_;
     }
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -149,17 +149,17 @@ class MIRGenerator
     uint32_t minAsmJSHeapLength() const {
         return minAsmJSHeapLength_;
     }
 
     bool modifiesFrameArguments() const {
         return modifiesFrameArguments_;
     }
 
-    typedef Vector<types::ObjectGroup *, 0, JitAllocPolicy> ObjectGroupVector;
+    typedef Vector<ObjectGroup *, 0, JitAllocPolicy> ObjectGroupVector;
 
     // When abortReason() == AbortReason_NewScriptProperties, all types which
     // the new script properties analysis hasn't been performed on yet.
     const ObjectGroupVector &abortedNewScriptPropertiesGroups() const {
         return abortedNewScriptPropertiesGroups_;
     }
 
   public:
@@ -194,17 +194,17 @@ class MIRGenerator
     bool instrumentedProfilingIsCached_;
 
     // List of nursery objects used by this compilation. Can be traced by a
     // minor GC while compilation happens off-thread. This Vector should only
     // be accessed on the main thread (IonBuilder, nursery GC or
     // CodeGenerator::link).
     ObjectVector nurseryObjects_;
 
-    void addAbortedNewScriptPropertiesGroup(types::ObjectGroup *type);
+    void addAbortedNewScriptPropertiesGroup(ObjectGroup *type);
     void setForceAbort() {
         shouldForceAbort_ = true;
     }
     bool shouldForceAbort() {
         return shouldForceAbort_;
     }
 
 #if defined(JS_ION_PERF)
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -88,17 +88,17 @@ MIRGenerator::abort(const char *message,
     va_list ap;
     va_start(ap, message);
     abortFmt(message, ap);
     va_end(ap);
     return false;
 }
 
 void
-MIRGenerator::addAbortedNewScriptPropertiesGroup(types::ObjectGroup *group)
+MIRGenerator::addAbortedNewScriptPropertiesGroup(ObjectGroup *group)
 {
     for (size_t i = 0; i < abortedNewScriptPropertiesGroups_.length(); i++) {
         if (group == abortedNewScriptPropertiesGroups_[i])
             return;
     }
     if (!abortedNewScriptPropertiesGroups_.append(group))
         CrashAtUnhandlableOOM("addAbortedNewScriptPropertiesGroup");
 }
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -55,17 +55,17 @@ class TypeWrapper {
             return 0;
         return 1;
     }
     inline JSObject *getSingletonNoBarrier(unsigned) const {
         if (t_.isSingleton())
             return t_.singletonNoBarrier();
         return nullptr;
     }
-    inline types::ObjectGroup *getGroupNoBarrier(unsigned) const {
+    inline ObjectGroup *getGroupNoBarrier(unsigned) const {
         if (t_.isGroup())
             return t_.groupNoBarrier();
         return nullptr;
     }
 };
 
 } /* anonymous namespace */
 
@@ -141,18 +141,18 @@ MacroAssembler::guardTypeSet(const Sourc
         bind(&fail);
 
         // Type set guards might miss when an object's type changes and its
         // properties become unknown, so check for this case.
         if (obj == scratch)
             extractObject(address, scratch);
         loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
         branchTestPtr(Assembler::NonZero,
-                      Address(scratch, types::ObjectGroup::offsetOfFlags()),
-                      Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched);
+                      Address(scratch, ObjectGroup::offsetOfFlags()),
+                      Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched);
 
         assumeUnreachable("Unexpected object type");
 #endif
     }
 
     bind(&matched);
 }
 
@@ -205,17 +205,17 @@ MacroAssembler::guardObjectType(Register
 
         for (unsigned i = 0; i < count; i++) {
             if (!types->getGroupNoBarrier(i))
                 continue;
 
             if (lastBranch.isInitialized())
                 lastBranch.emit(*this);
 
-            types::ObjectGroup *group = types->getGroupNoBarrier(i);
+            ObjectGroup *group = types->getGroupNoBarrier(i);
             lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(group), &matched);
         }
     }
 
     if (!lastBranch.isInitialized()) {
         jump(miss);
         return;
     }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -285,22 +285,22 @@ class MacroAssembler : public MacroAssem
     }
     void loadBaseShape(Register objReg, Register dest) {
         loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
 
         loadPtr(Address(dest, Shape::offsetOfBase()), dest);
     }
     void loadObjClass(Register objReg, Register dest) {
         loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest);
-        loadPtr(Address(dest, types::ObjectGroup::offsetOfClasp()), dest);
+        loadPtr(Address(dest, ObjectGroup::offsetOfClasp()), dest);
     }
     void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp,
                             Label *label) {
         loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
-        branchPtr(cond, Address(scratch, types::ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label);
+        branchPtr(cond, Address(scratch, ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label);
     }
     void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) {
         branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label);
     }
     void branchTestObjShape(Condition cond, Register obj, Register shape, Label *label) {
         branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
     }
     void branchTestProxyHandlerFamily(Condition cond, Register proxy, Register scratch,
@@ -343,17 +343,17 @@ class MacroAssembler : public MacroAssem
     }
 
     void loadObjPrivate(Register obj, uint32_t nfixed, Register dest) {
         loadPtr(Address(obj, NativeObject::getPrivateDataOffset(nfixed)), dest);
     }
 
     void loadObjProto(Register obj, Register dest) {
         loadPtr(Address(obj, JSObject::offsetOfGroup()), dest);
-        loadPtr(Address(dest, types::ObjectGroup::offsetOfProto()), dest);
+        loadPtr(Address(dest, ObjectGroup::offsetOfProto()), dest);
     }
 
     void loadStringLength(Register str, Register dest) {
         load32(Address(str, JSString::offsetOfLength()), dest);
     }
 
     void loadFunctionFromCalleeToken(Address token, Register dest) {
         loadPtr(token, dest);
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -813,17 +813,17 @@ WriteOffsetsTable(CompactBufferWriter &w
     return true;
 }
 
 static JSFunction *
 MaybeConstructorFromType(types::Type ty)
 {
     if (ty.isUnknown() || ty.isAnyObject() || !ty.isGroup())
         return nullptr;
-    types::ObjectGroup *obj = ty.group();
+    ObjectGroup *obj = ty.group();
     types::TypeNewScript *newScript = obj->newScript();
     if (!newScript && obj->maybeUnboxedLayout())
         newScript = obj->unboxedLayout().newScript();
     return newScript ? newScript->function() : nullptr;
 }
 
 static void
 SpewConstructor(types::Type ty, JSFunction *constructor)
@@ -949,17 +949,17 @@ jit::WriteIonTrackedOptimizationsTable(J
         types::Type ty = uniqueTypeList[i];
         if (JSFunction *constructor = MaybeConstructorFromType(ty)) {
             if (!allTypes->append(IonTrackedTypeWithAddendum(ty, constructor)))
                 return false;
             SpewConstructor(ty, constructor);
         } else {
             JSScript *script;
             uint32_t offset;
-            if (cx->findAllocationSiteForType(ty, &script, &offset)) {
+            if (ObjectGroup::findAllocationSiteForType(cx, ty, &script, &offset)) {
                 if (!allTypes->append(IonTrackedTypeWithAddendum(ty, script, offset)))
                     return false;
                 SpewAllocationSite(ty, script, offset);
             } else {
                 if (!allTypes->append(IonTrackedTypeWithAddendum(ty)))
                     return false;
             }
         }
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -6,16 +6,17 @@
 
 #ifndef jit_Recover_h
 #define jit_Recover_h
 
 #include "mozilla/Attributes.h"
 
 #include "jsarray.h"
 
+#include "jit/MIR.h"
 #include "jit/Snapshots.h"
 
 struct JSContext;
 
 namespace js {
 namespace jit {
 
 #define RECOVER_OPCODE_LIST(_)                  \
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Vector.h"
 
 #include "jit/IonAnalysis.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
+#include "vm/UnboxedObject.h"
 
 namespace js {
 namespace jit {
 
 template <typename MemoryView>
 class EmulateStateOf
 {
   private:
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1283,17 +1283,17 @@ MarkObjectFromIon(JSRuntime *rt, JSObjec
 
 void
 MarkShapeFromIon(JSRuntime *rt, Shape **shapep)
 {
     gc::MarkShapeUnbarriered(&rt->gc.marker, shapep, "write barrier");
 }
 
 void
-MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp)
+MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp)
 {
     gc::MarkObjectGroupUnbarriered(&rt->gc.marker, groupp, "write barrier");
 }
 
 bool
 ThrowUninitializedLexical(JSContext *cx)
 {
     ScriptFrameIter iter(cx);
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -340,17 +340,17 @@ template <> struct TypeToArgProperties<H
 };
 template <> struct TypeToArgProperties<MutableHandleValue> {
     static const uint32_t result = TypeToArgProperties<Value>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleShape> {
     static const uint32_t result = TypeToArgProperties<Shape *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleObjectGroup> {
-    static const uint32_t result = TypeToArgProperties<types::ObjectGroup *>::result | VMFunction::ByRef;
+    static const uint32_t result = TypeToArgProperties<ObjectGroup *>::result | VMFunction::ByRef;
 };
 
 // Convert argument type to whether or not it should be passed in a float
 // register on platforms that have them, like x64.
 template <class T> struct TypeToPassInFloatReg {
     static const uint32_t result = 0;
 };
 template <> struct TypeToPassInFloatReg<double> {
@@ -754,17 +754,17 @@ void AssertValidStringPtr(JSContext *cx,
 void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym);
 void AssertValidValue(JSContext *cx, Value *v);
 #endif
 
 void MarkValueFromIon(JSRuntime *rt, Value *vp);
 void MarkStringFromIon(JSRuntime *rt, JSString **stringp);
 void MarkObjectFromIon(JSRuntime *rt, JSObject **objp);
 void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
-void MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp);
+void MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp);
 
 // Helper for generatePreBarrier.
 inline void *
 IonMarkFunction(MIRType type)
 {
     switch (type) {
       case MIRType_Value:
         return JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -1986,17 +1986,17 @@ CodeGeneratorX86Shared::visitGuardObject
 
 void
 CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
 
     masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp);
-    masm.cmpPtr(Operand(tmp, types::ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass()));
+    masm.cmpPtr(Operand(tmp, ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass()));
     bailoutIf(Assembler::NotEqual, guard->snapshot());
 }
 
 void
 CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress *ins)
 {
     const MEffectiveAddress *mir = ins->mir();
     Register base = ToRegister(ins->base());
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1242,17 +1242,17 @@ enum ShouldUpdateTypes
 static bool
 InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes)
 {
     MOZ_ASSERT(count <= MAX_ARRAY_INDEX);
 
     if (count == 0)
         return true;
 
-    types::ObjectGroup *group = obj->getGroup(cx);
+    ObjectGroup *group = obj->getGroup(cx);
     if (!group)
         return false;
     if (updateTypes && !InitArrayTypes(cx, group, vector, count))
         return false;
 
     /*
      * Optimize for dense arrays so long as adding the given set of elements
      * wouldn't otherwise make the array slow or exceed a non-writable array
@@ -2321,17 +2321,17 @@ js::array_unshift(JSContext *cx, unsigne
 static inline void
 TryReuseArrayGroup(JSObject *obj, ArrayObject *narr)
 {
     /*
      * Try to change the group of a newly created array narr to the same group
      * as obj. This can only be performed if the original object is an array
      * and has the same prototype.
      */
-    MOZ_ASSERT(narr->getProto()->hasNewGroup(&ArrayObject::class_, narr->group()));
+    MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(), &ArrayObject::class_, narr->group()));
 
     if (obj->is<ArrayObject>() && !obj->isSingleton() && obj->getProto() == narr->getProto())
         narr->setGroup(obj->group());
 }
 
 /*
  * Returns true if this is a dense array whose |count| properties starting from
  * |startingIndex| may be accessed (get, set, delete) directly through its
@@ -2358,17 +2358,17 @@ CanOptimizeForDenseStorage(HandleObject 
      * deleted if a hole is moved from one location to another location not yet
      * visited.  See bug 690622.
      *
      * Another potential wrinkle: what if the enumeration is happening on an
      * object which merely has |arr| on its prototype chain?  It turns out this
      * case can't happen, because any dense array used as the prototype of
      * another object is first slowified, for type inference's sake.
      */
-    types::ObjectGroup *arrGroup = arr->getGroup(cx);
+    ObjectGroup *arrGroup = arr->getGroup(cx);
     if (MOZ_UNLIKELY(!arrGroup || arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED)))
         return false;
 
     /*
      * Now watch out for getters and setters along the prototype chain or in
      * other indexed properties on the object.  (Note that non-writable length
      * is subsumed by the initializedLength comparison.)
      */
@@ -2977,17 +2977,17 @@ array_filter(JSContext *cx, unsigned arg
 
     /* Step 5. */
     RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
 
     /* Step 6. */
     RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0));
     if (!arr)
         return false;
-    ObjectGroup *newGroup = GetCallerInitGroup(cx, JSProto_Array);
+    ObjectGroup *newGroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
     if (!newGroup)
         return false;
     arr->setGroup(newGroup);
 
     /* Step 7. */
     uint32_t k = 0;
 
     /* Step 8. */
@@ -3077,17 +3077,17 @@ ArrayFromCallArgs(JSContext *cx, HandleO
 static bool
 array_of(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
         // IsArrayConstructor(this) will usually be true in practice. This is
         // the most common path.
-        RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array));
+        RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
         if (!group)
             return false;
         return ArrayFromCallArgs(cx, group, args);
     }
 
     // Step 4.
     RootedObject obj(cx);
     {
@@ -3184,17 +3184,17 @@ static const JSFunctionSpec array_static
     JS_FS_END
 };
 
 /* ES5 15.4.2 */
 bool
 js_Array(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array));
+    RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
     if (!group)
         return false;
 
     if (args.length() != 1 || !args[0].isNumber())
         return ArrayFromCallArgs(cx, group, args);
 
     uint32_t length;
     if (args[0].isInt32()) {
@@ -3246,17 +3246,18 @@ js::ArrayConstructorOneArg(JSContext *cx
 static JSObject *
 CreateArrayPrototype(JSContext *cx, JSProtoKey key)
 {
     MOZ_ASSERT(key == JSProto_Array);
     RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
 
-    RootedObjectGroup group(cx, cx->getNewGroup(&ArrayObject::class_, TaggedProto(proto)));
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_,
+                                                             TaggedProto(proto)));
     if (!group)
         return nullptr;
 
     JSObject *metadata = nullptr;
     if (!NewObjectMetadata(cx, &metadata))
         return nullptr;
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
@@ -3374,17 +3375,18 @@ NewArray(ExclusiveContext *cxArg, uint32
 
     RootedObject proto(cxArg, protoArg);
     if (protoArg)
         JS::PoisonPtr(&protoArg);
 
     if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto))
         return nullptr;
 
-    RootedObjectGroup group(cxArg, cxArg->getNewGroup(&ArrayObject::class_, TaggedProto(proto)));
+    RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_,
+                                                                TaggedProto(proto)));
     if (!group)
         return nullptr;
 
     JSObject *metadata = nullptr;
     if (!NewObjectMetadata(cxArg, &metadata))
         return nullptr;
 
     /*
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -111,17 +111,17 @@ JSCompartment::sweepCallsiteClones()
 }
 
 JSFunction *
 js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun,
                                     JSScript *script, jsbytecode *pc)
 {
     MOZ_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite());
     MOZ_ASSERT(!fun->nonLazyScript()->enclosingStaticScope());
-    MOZ_ASSERT(types::UseSingletonForClone(fun));
+    MOZ_ASSERT(ObjectGroup::useSingletonForClone(fun));
 
     /*
      * If we start allocating function objects in the nursery, then the callsite
      * clone table will need a postbarrier.
      */
     MOZ_ASSERT(fun->isTenured());
 
     if (!table.initialized())
@@ -248,17 +248,17 @@ js::DestroyContext(JSContext *cx, Destro
     cx->remove();
     bool last = !rt->hasContexts();
     if (last) {
         /*
          * Dump remaining type inference results while we still have a context.
          * This printing depends on atoms still existing.
          */
         for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-            c->types.print(cx, false);
+            types::PrintTypes(cx, c, false);
     }
     if (mode == DCM_FORCE_GC) {
         MOZ_ASSERT(!rt->isHeapBusy());
         JS::PrepareForFullGC(rt);
         rt->gc.gc(GC_NORMAL, JS::gcreason::DESTROY_CONTEXT);
     }
     js_delete_poison(cx);
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -296,23 +296,16 @@ class ExclusiveContext : public ContextF
     }
     JS::Zone *zone() const {
         MOZ_ASSERT_IF(!compartment(), !zone_);
         MOZ_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_);
         return zone_;
     }
 
     // Zone local methods that can be used freely from an ExclusiveContext.
-    types::ObjectGroup *getNewGroup(const Class *clasp, TaggedProto proto,
-                                    JSObject *associated = nullptr);
-    types::ObjectGroup *getLazySingletonGroup(const Class *clasp, TaggedProto proto);
-
-    // Returns false if not found.
-    bool findAllocationSiteForType(types::Type ty, JSScript **script, uint32_t *offset) const;
-
     inline js::LifoAlloc &typeLifoAlloc();
 
     // Current global. This is only safe to use within the scope of the
     // AutoCompartment from which it's called.
     inline js::Handle<js::GlobalObject*> global() const;
 
     // Methods to access runtime data that must be protected by locks.
     frontend::ParseMapPool &parseMapPool() {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -540,23 +540,16 @@ JSCompartment::markRoots(JSTracer *trc)
 
 void
 JSCompartment::sweepInnerViews()
 {
     innerViews.sweep(runtimeFromAnyThread());
 }
 
 void
-JSCompartment::sweepObjectGroupTables()
-{
-    sweepNewObjectGroupTable(newObjectGroups);
-    sweepNewObjectGroupTable(lazyObjectGroups);
-}
-
-void
 JSCompartment::sweepSavedStacks()
 {
     savedStacks_.sweep(runtimeFromAnyThread());
 }
 
 void
 JSCompartment::sweepGlobalObject(FreeOp *fop)
 {
@@ -647,20 +640,19 @@ JSCompartment::sweepCrossCompartmentWrap
             e.rekeyFront(key);
         }
     }
 }
 
 void JSCompartment::fixupAfterMovingGC()
 {
     fixupGlobal();
-    fixupNewObjectGroupTable(newObjectGroups);
-    fixupNewObjectGroupTable(lazyObjectGroups);
     fixupInitialShapeTable();
     fixupBaseShapeTable();
+    objectGroups.fixupTablesAfterMovingGC();
 }
 
 void
 JSCompartment::fixupGlobal()
 {
     GlobalObject *global = *global_.unsafeGet();
     if (global)
         global_.set(MaybeForwarded(global));
@@ -683,25 +675,21 @@ JSCompartment::clearTables()
     MOZ_ASSERT(crossCompartmentWrappers.empty());
     MOZ_ASSERT_IF(callsiteClones.initialized(), callsiteClones.empty());
     MOZ_ASSERT(!jitCompartment_);
     MOZ_ASSERT(!debugScopes);
     MOZ_ASSERT(!gcWeakMapList);
     MOZ_ASSERT(enumerators->next() == enumerators);
     MOZ_ASSERT(regExps.empty());
 
-    types.clearTables();
+    objectGroups.clearTables();
     if (baseShapes.initialized())
         baseShapes.clear();
     if (initialShapes.initialized())
         initialShapes.clear();
-    if (newObjectGroups.initialized())
-        newObjectGroups.clear();
-    if (lazyObjectGroups.initialized())
-        lazyObjectGroups.clear();
     if (savedStacks_.initialized())
         savedStacks_.clear();
 }
 
 void
 JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback)
 {
     // Clear any jitcode in the runtime, which behaves differently depending on
@@ -811,21 +799,20 @@ JSCompartment::addSizeOfIncludingThis(mo
                                       size_t *compartmentTables,
                                       size_t *innerViewsArg,
                                       size_t *lazyArrayBuffersArg,
                                       size_t *crossCompartmentWrappersArg,
                                       size_t *regexpCompartment,
                                       size_t *savedStacksSet)
 {
     *compartmentObject += mallocSizeOf(this);
-    types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
-                                 tiArrayTypeTables, tiObjectTypeTables);
+    objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
+                                        tiArrayTypeTables, tiObjectTypeTables,
+                                        compartmentTables);
     *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf)
-                        + initialShapes.sizeOfExcludingThis(mallocSizeOf)
-                        + newObjectGroups.sizeOfExcludingThis(mallocSizeOf)
-                        + lazyObjectGroups.sizeOfExcludingThis(mallocSizeOf);
+                        + initialShapes.sizeOfExcludingThis(mallocSizeOf);
     *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
     if (lazyArrayBuffers)
         *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
     *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
     *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
     *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
 }
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -215,19 +215,16 @@ struct JSCompartment
     inline js::GlobalObject *maybeGlobal() const;
 
     /* An unbarriered getter for use while tracing. */
     inline js::GlobalObject *unsafeUnbarrieredMaybeGlobal() const;
 
     inline void initGlobal(js::GlobalObject &global);
 
   public:
-    /* Type information about the scripts and objects in this compartment. */
-    js::types::TypeCompartment   types;
-
     void                         *data;
 
   private:
     js::ObjectMetadataCallback   objectMetadataCallback;
 
     js::SavedStacks              savedStacks_;
 
     js::WrapperMap               crossCompartmentWrappers;
@@ -271,24 +268,20 @@ struct JSCompartment
     /* Set of all unowned base shapes in the compartment. */
     js::BaseShapeSet             baseShapes;
     void sweepBaseShapeTable();
 
     /* Set of initial shapes in the compartment. */
     js::InitialShapeSet          initialShapes;
     void sweepInitialShapeTable();
 
-    /* Set of default 'new' or lazy groups in the compartment. */
-    js::types::NewObjectGroupTable newObjectGroups;
-    js::types::NewObjectGroupTable lazyObjectGroups;
-    void sweepNewObjectGroupTable(js::types::NewObjectGroupTable &table);
+    // Object group tables and other state in the compartment.
+    js::ObjectGroupCompartment   objectGroups;
 
 #ifdef JSGC_HASH_TABLE_CHECKS
-    void checkObjectGroupTablesAfterMovingGC();
-    void checkObjectGroupTableAfterMovingGC(js::types::NewObjectGroupTable &table);
     void checkInitialShapesTableAfterMovingGC();
     void checkWrapperMapAfterMovingGC();
     void checkBaseShapeTableAfterMovingGC();
 #endif
 
     /*
      * Hash table of all manually call site-cloned functions from within
      * self-hosted code. Cloning according to call site provides extra
@@ -381,31 +374,29 @@ struct JSCompartment
     };
 
     void trace(JSTracer *trc);
     void markRoots(JSTracer *trc);
     bool preserveJitCode() { return gcPreserveJitCode; }
 
     void sweepInnerViews();
     void sweepCrossCompartmentWrappers();
-    void sweepObjectGroupTables();
     void sweepSavedStacks();
     void sweepGlobalObject(js::FreeOp *fop);
     void sweepSelfHostingScriptSource();
     void sweepJitCompartment(js::FreeOp *fop);
     void sweepRegExps();
     void sweepDebugScopes();
     void sweepWeakMaps();
     void sweepNativeIterators();
 
     void purge();
     void clearTables();
 
     void fixupInitialShapeTable();
-    void fixupNewObjectGroupTable(js::types::NewObjectGroupTable &table);
     void fixupAfterMovingGC();
     void fixupGlobal();
     void fixupBaseShapeTable();
 
     bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
     void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
     void forgetObjectMetadataCallback() {
         objectMetadataCallback = nullptr;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -868,17 +868,17 @@ CreateFunctionPrototype(JSContext *cx, J
                                              /* staticLevel = */ 0,
                                              sourceObject,
                                              0,
                                              ss->length()));
     if (!script || !JSScript::fullyInitTrivial(cx, script))
         return nullptr;
 
     functionProto->initScript(script);
-    types::ObjectGroup* protoGroup = functionProto->getGroup(cx);
+    ObjectGroup* protoGroup = functionProto->getGroup(cx);
     if (!protoGroup)
         return nullptr;
 
     protoGroup->setInterpretedFunction(functionProto);
     script->setFunction(functionProto);
 
     /*
      * The default 'new' group of Function.prototype is required by type
@@ -2032,17 +2032,17 @@ js::NewFunctionWithProto(ExclusiveContex
     return fun;
 }
 
 bool
 js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun)
 {
     return compartment == fun->compartment() &&
            !fun->isSingleton() &&
-           !types::UseSingletonForClone(fun);
+           !ObjectGroup::useSingletonForClone(fun);
 }
 
 JSFunction *
 js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, gc::AllocKind allocKind,
                         NewObjectKind newKindArg /* = GenericObject */)
 {
     MOZ_ASSERT(parent);
     MOZ_ASSERT(!fun->isBoundFunction());
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -281,17 +281,17 @@ const uint32_t Arena::ThingSizes[] = CHE
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     sizeof(JSScript),           /* FINALIZE_SCRIPT              */
     sizeof(LazyScript),         /* FINALIZE_LAZY_SCRIPT         */
     sizeof(Shape),              /* FINALIZE_SHAPE               */
     sizeof(AccessorShape),      /* FINALIZE_ACCESSOR_SHAPE      */
     sizeof(BaseShape),          /* FINALIZE_BASE_SHAPE          */
-    sizeof(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP        */
+    sizeof(ObjectGroup),        /* FINALIZE_OBJECT_GROUP        */
     sizeof(JSFatInlineString),  /* FINALIZE_FAT_INLINE_STRING   */
     sizeof(JSString),           /* FINALIZE_STRING              */
     sizeof(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
     sizeof(JS::Symbol),         /* FINALIZE_SYMBOL              */
     sizeof(jit::JitCode),       /* FINALIZE_JITCODE             */
 );
 
 #undef CHECK_MIN_THING_SIZE_INNER
@@ -312,17 +312,17 @@ const uint32_t Arena::FirstThingOffsets[
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     OFFSET(JSScript),           /* FINALIZE_SCRIPT              */
     OFFSET(LazyScript),         /* FINALIZE_LAZY_SCRIPT         */
     OFFSET(Shape),              /* FINALIZE_SHAPE               */
     OFFSET(AccessorShape),      /* FINALIZE_ACCESSOR_SHAPE      */
     OFFSET(BaseShape),          /* FINALIZE_BASE_SHAPE          */
-    OFFSET(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP        */
+    OFFSET(ObjectGroup),        /* FINALIZE_OBJECT_GROUP        */
     OFFSET(JSFatInlineString),  /* FINALIZE_FAT_INLINE_STRING   */
     OFFSET(JSString),           /* FINALIZE_STRING              */
     OFFSET(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
     OFFSET(JS::Symbol),         /* FINALIZE_SYMBOL              */
     OFFSET(jit::JitCode),       /* FINALIZE_JITCODE             */
 };
 
 #undef OFFSET
@@ -604,17 +604,17 @@ FinalizeArenas(FreeOp *fop,
         return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_SHAPE:
         return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_ACCESSOR_SHAPE:
         return FinalizeTypedArenas<AccessorShape>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_BASE_SHAPE:
         return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_OBJECT_GROUP:
-        return FinalizeTypedArenas<types::ObjectGroup>(fop, src, dest, thingKind, budget, keepArenas);
+        return FinalizeTypedArenas<ObjectGroup>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_STRING:
         return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_FAT_INLINE_STRING:
         return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_EXTERNAL_STRING:
         return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_SYMBOL:
         return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget, keepArenas);
@@ -2221,17 +2221,17 @@ GCRuntime::sweepTypesAfterCompacting(Zon
     types::AutoClearTypeInferenceStateOnOOM oom(zone);
 
     for (ZoneCellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         script->maybeSweepTypes(&oom);
     }
 
     for (ZoneCellIterUnderGC i(zone, FINALIZE_OBJECT_GROUP); !i.done(); i.next()) {
-        types::ObjectGroup *group = i.get<types::ObjectGroup>();
+        ObjectGroup *group = i.get<ObjectGroup>();
         group->maybeSweep(&oom);
     }
 
     zone->types.endSweep(rt);
 }
 
 void
 GCRuntime::sweepZoneAfterCompacting(Zone *zone)
@@ -2242,17 +2242,17 @@ GCRuntime::sweepZoneAfterCompacting(Zone
     sweepTypesAfterCompacting(zone);
     zone->sweepBreakpoints(fop);
 
     for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
         c->sweepInnerViews();
         c->sweepCrossCompartmentWrappers();
         c->sweepBaseShapeTable();
         c->sweepInitialShapeTable();
-        c->sweepObjectGroupTables();
+        c->objectGroups.sweep(fop);
         c->sweepRegExps();
         c->sweepCallsiteClones();
         c->sweepSavedStacks();
         c->sweepGlobalObject(fop);
         c->sweepSelfHostingScriptSource();
         c->sweepDebugScopes();
         c->sweepJitCompartment(fop);
         c->sweepWeakMaps();
@@ -2306,17 +2306,17 @@ UpdateCellPointers(MovingTracer *trc, Ar
         return;
       case FINALIZE_ACCESSOR_SHAPE:
         UpdateCellPointersTyped<AccessorShape>(trc, arena, traceKind);
         return;
       case FINALIZE_BASE_SHAPE:
         UpdateCellPointersTyped<BaseShape>(trc, arena, traceKind);
         return;
       case FINALIZE_OBJECT_GROUP:
-        UpdateCellPointersTyped<types::ObjectGroup>(trc, arena, traceKind);
+        UpdateCellPointersTyped<ObjectGroup>(trc, arena, traceKind);
         return;
       case FINALIZE_JITCODE:
         UpdateCellPointersTyped<jit::JitCode>(trc, arena, traceKind);
         return;
       default:
         MOZ_CRASH("Invalid alloc kind for UpdateCellPointers");
     }
 }
@@ -4867,17 +4867,17 @@ SweepInitialShapesTask::run()
     for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
         c->sweepInitialShapeTable();
 }
 
 /* virtual */ void
 SweepObjectGroupsTask::run()
 {
     for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
-        c->sweepObjectGroupTables();
+        c->objectGroups.sweep(runtime->defaultFreeOp());
 }
 
 /* virtual */ void
 SweepRegExpsTask::run()
 {
     for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
         c->sweepRegExps();
 }
@@ -5192,17 +5192,17 @@ SweepThing(Shape *shape)
 
 static void
 SweepThing(JSScript *script, types::AutoClearTypeInferenceStateOnOOM *oom)
 {
     script->maybeSweepTypes(oom);
 }
 
 static void
-SweepThing(types::ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom)
+SweepThing(ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom)
 {
     group->maybeSweep(oom);
 }
 
 template <typename T, typename... Args>
 static bool
 SweepArenaList(ArenaHeader **arenasToSweep, SliceBudget &sliceBudget, Args... args)
 {
@@ -5245,17 +5245,17 @@ GCRuntime::sweepPhase(SliceBudget &slice
             for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
                 ArenaLists &al = sweepZone->arenas;
 
                 types::AutoClearTypeInferenceStateOnOOM oom(sweepZone);
 
                 if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate, sliceBudget, &oom))
                     return NotFinished;
 
-                if (!SweepArenaList<types::ObjectGroup>(
+                if (!SweepArenaList<ObjectGroup>(
                         &al.gcObjectGroupArenasToUpdate, sliceBudget, &oom))
                 {
                     return NotFinished;
                 }
 
                 // Finish sweeping type information in the zone.
                 {
                     gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_TYPES_END);
@@ -6537,17 +6537,17 @@ gc::MergeCompartments(JSCompartment *sou
 
     for (ZoneCellIter iter(source->zone(), FINALIZE_BASE_SHAPE); !iter.done(); iter.next()) {
         BaseShape *base = iter.get<BaseShape>();
         MOZ_ASSERT(base->compartment() == source);
         base->compartment_ = target;
     }
 
     for (ZoneCellIter iter(source->zone(), FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) {
-        types::ObjectGroup *group = iter.get<types::ObjectGroup>();
+        ObjectGroup *group = iter.get<ObjectGroup>();
         group->setGeneration(target->zone()->types.generation);
     }
 
     // Fixup zone pointers in source's zone to refer to target's zone.
 
     for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) {
         for (ArenaIter aiter(source->zone(), AllocKind(thingKind)); !aiter.done(); aiter.next()) {
             ArenaHeader *aheader = aiter.get();
@@ -6962,17 +6962,17 @@ JS::GCCellPtr::outOfLineKind() const
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        c->checkObjectGroupTablesAfterMovingGC();
+        c->objectGroups.checkTablesAfterMovingGC();
         c->checkInitialShapesTableAfterMovingGC();
         c->checkWrapperMapAfterMovingGC();
         c->checkBaseShapeTableAfterMovingGC();
         if (c->debugScopes)
             c->debugScopes->checkHashTablesAfterMovingGC(rt);
     }
 }
 #endif
@@ -7118,17 +7118,17 @@ JS::IncrementalReferenceBarrier(GCCellPt
         return LazyScript::writeBarrierPre(static_cast<LazyScript*>(thing.asCell()));
       case JSTRACE_JITCODE:
         return jit::JitCode::writeBarrierPre(static_cast<jit::JitCode*>(thing.asCell()));
       case JSTRACE_SHAPE:
         return Shape::writeBarrierPre(static_cast<Shape*>(thing.asCell()));
       case JSTRACE_BASE_SHAPE:
         return BaseShape::writeBarrierPre(static_cast<BaseShape*>(thing.asCell()));
       case JSTRACE_OBJECT_GROUP:
-        return types::ObjectGroup::writeBarrierPre(static_cast<types::ObjectGroup *>(thing.asCell()));
+        return ObjectGroup::writeBarrierPre(static_cast<ObjectGroup *>(thing.asCell()));
       default:
         MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier.");
     }
 }
 
 JS_PUBLIC_API(void)
 JS::IncrementalValueBarrier(const Value &v)
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -63,17 +63,17 @@ enum State {
 
 /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
 template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = FINALIZE_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = FINALIZE_SHAPE; };
 template <> struct MapTypeToFinalizeKind<AccessorShape>     { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; };
 template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
-template <> struct MapTypeToFinalizeKind<types::ObjectGroup> { static const AllocKind kind = FINALIZE_OBJECT_GROUP; };
+template <> struct MapTypeToFinalizeKind<ObjectGroup>       { static const AllocKind kind = FINALIZE_OBJECT_GROUP; };
 template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; };
 template <> struct MapTypeToFinalizeKind<JSString>          { static const AllocKind kind = FINALIZE_STRING; };
 template <> struct MapTypeToFinalizeKind<JSExternalString>  { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; };
 template <> struct MapTypeToFinalizeKind<JS::Symbol>        { static const AllocKind kind = FINALIZE_SYMBOL; };
 template <> struct MapTypeToFinalizeKind<jit::JitCode>      { static const AllocKind kind = FINALIZE_JITCODE; };
 
 static inline bool
 IsNurseryAllocable(AllocKind kind)
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -609,20 +609,20 @@ NewGCObject(js::ExclusiveContext *cx, js
 template <js::AllowGC allowGC>
 inline jit::JitCode *
 NewJitCode(js::ExclusiveContext *cx)
 {
     return gc::AllocateNonObject<jit::JitCode, allowGC>(cx);
 }
 
 inline
-types::ObjectGroup *
+ObjectGroup *
 NewObjectGroup(js::ExclusiveContext *cx)
 {
-    return gc::AllocateNonObject<types::ObjectGroup, js::CanGC>(cx);
+    return gc::AllocateNonObject<ObjectGroup, js::CanGC>(cx);
 }
 
 template <js::AllowGC allowGC>
 inline JSString *
 NewGCString(js::ExclusiveContext *cx)
 {
     return js::gc::AllocateNonObject<JSString, allowGC>(cx);
 }
--- a/js/src/jshashutil.h
+++ b/js/src/jshashutil.h
@@ -4,16 +4,18 @@
  * 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 jshashutil_h
 #define jshashutil_h
 
 #include "jscntxt.h"
 
+#include "gc/Zone.h"
+
 namespace js {
 
 /*
  * Used to add entries to a js::HashMap or HashSet where the key depends on a GC
  * thing that may be moved by generational or compacting GC between the call to
  * lookupForAdd() and relookupOrAdd().
  */
 template <class T>
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -220,25 +220,16 @@ types::TypeString(Type type)
 }
 
 const char *
 types::ObjectGroupString(ObjectGroup *group)
 {
     return TypeString(Type::ObjectType(group));
 }
 
-unsigned JSScript::id() {
-    if (!id_) {
-        id_ = ++compartment()->types.scriptCount;
-        InferSpew(ISpewOps, "script #%u: %p %s:%d",
-                  id_, this, filename() ? filename() : "<null>", lineno());
-    }
-    return id_;
-}
-
 void
 types::InferSpew(SpewChannel channel, const char *fmt, ...)
 {
     if (!InferSpewActive(channel))
         return;
 
     va_list ap;
     va_start(ap, fmt);
@@ -303,17 +294,17 @@ types::TypeFailure(JSContext *cx, const 
     va_list ap;
     va_start(ap, fmt);
     JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
     va_end(ap);
 
     JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf);
 
     /* Dump type state, even if INFERFLAGS is unset. */
-    cx->compartment()->types.print(cx, true);
+    PrintTypes(cx, cx->compartment(), true);
 
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
@@ -2255,192 +2246,16 @@ TemporaryTypeSet::propertyNeedsBarrier(C
         HeapTypeSetKey property = key->property(id);
         if (property.needsBarrier(constraints))
             return true;
     }
 
     return false;
 }
 
-/////////////////////////////////////////////////////////////////////
-// TypeCompartment
-/////////////////////////////////////////////////////////////////////
-
-TypeCompartment::TypeCompartment()
-{
-    PodZero(this);
-}
-
-ObjectGroup *
-TypeCompartment::newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
-                                ObjectGroupFlags initialFlags)
-{
-    MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
-
-    if (cx->isJSContext()) {
-        if (proto.isObject() && IsInsideNursery(proto.toObject()))
-            initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
-    }
-
-    ObjectGroup *group = js::NewObjectGroup(cx);
-    if (!group)
-        return nullptr;
-    new(group) ObjectGroup(clasp, proto, initialFlags);
-
-    return group;
-}
-
-ObjectGroup *
-TypeCompartment::addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key)
-{
-    AutoEnterAnalysis enter(cx);
-
-    if (!allocationSiteTable) {
-        allocationSiteTable = cx->new_<AllocationSiteTable>();
-        if (!allocationSiteTable || !allocationSiteTable->init()) {
-            js_delete(allocationSiteTable);
-            allocationSiteTable = nullptr;
-            return nullptr;
-        }
-    }
-
-    AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
-    MOZ_ASSERT(!p);
-
-    ObjectGroup *res = nullptr;
-
-    jsbytecode *pc = key.script->offsetToPC(key.offset);
-    RootedScript keyScript(cx, key.script);
-
-    if (!res) {
-        RootedObject proto(cx);
-        if (key.kind != JSProto_Null && !GetBuiltinPrototype(cx, key.kind, &proto))
-            return nullptr;
-
-        Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
-        res = newObjectGroup(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
-        if (!res)
-            return nullptr;
-        key.script = keyScript;
-    }
-
-    if (JSOp(*pc) == JSOP_NEWOBJECT) {
-        /*
-         * This object is always constructed the same way and will not be
-         * observed by other code before all properties have been added. Mark
-         * all the properties as definite properties of the object.
-         */
-        RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc)));
-
-        if (!res->addDefiniteProperties(cx, baseobj->lastProperty()))
-            return nullptr;
-    }
-
-    if (!allocationSiteTable->add(p, key, res))
-        return nullptr;
-
-    return res;
-}
-
-static inline jsid
-GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
-{
-    PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset));
-    return IdToTypeId(NameToId(name));
-}
-
-bool
-types::UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc)
-{
-    /*
-     * Make a heuristic guess at a use of JSOP_NEW that the constructed object
-     * should have a fresh group. We do this when the NEW is immediately
-     * followed by a simple assignment to an object's .prototype field.
-     * This is designed to catch common patterns for subclassing in JS:
-     *
-     * function Super() { ... }
-     * function Sub1() { ... }
-     * function Sub2() { ... }
-     *
-     * Sub1.prototype = new Super();
-     * Sub2.prototype = new Super();
-     *
-     * Using distinct groups for the particular prototypes of Sub1 and
-     * Sub2 lets us continue to distinguish the two subclasses and any extra
-     * properties added to those prototype objects.
-     */
-    if (script->isGenerator())
-        return false;
-    if (JSOp(*pc) != JSOP_NEW)
-        return false;
-    pc += JSOP_NEW_LENGTH;
-    if (JSOp(*pc) == JSOP_SETPROP) {
-        jsid id = GetAtomId(cx, script, pc, 0);
-        if (id == id_prototype(cx))
-            return true;
-    }
-
-    return false;
-}
-
-NewObjectKind
-types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key)
-{
-    // The return value of this method can either be tested like a boolean or
-    // passed to a NewObject method.
-    JS_STATIC_ASSERT(GenericObject == 0);
-
-    /*
-     * Objects created outside loops in global and eval scripts should have
-     * singleton types. For now this is only done for plain objects and typed
-     * arrays, but not normal arrays.
-     */
-
-    if (script->functionNonDelazifying() && !script->treatAsRunOnce())
-        return GenericObject;
-
-    if (key != JSProto_Object &&
-        !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) &&
-        !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray))
-    {
-        return GenericObject;
-    }
-
-    /*
-     * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
-     * indicating their boundary.
-     */
-
-    if (!script->hasTrynotes())
-        return SingletonObject;
-
-    unsigned offset = script->pcToOffset(pc);
-
-    JSTryNote *tn = script->trynotes()->vector;
-    JSTryNote *tnlimit = tn + script->trynotes()->length;
-    for (; tn < tnlimit; tn++) {
-        if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP)
-            continue;
-
-        unsigned startOffset = script->mainOffset() + tn->start;
-        unsigned endOffset = startOffset + tn->length;
-
-        if (offset >= startOffset && offset < endOffset)
-            return GenericObject;
-    }
-
-    return SingletonObject;
-}
-
-NewObjectKind
-types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp)
-{
-    return UseSingletonForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
-}
-
 static inline bool
 ClassCanHaveExtraProperties(const Class *clasp)
 {
     return clasp->resolve
         || clasp->ops.lookupProperty
         || clasp->ops.getProperty
         || IsAnyTypedArrayClass(clasp);
 }
@@ -2480,17 +2295,17 @@ types::TypeCanHaveExtraIndexedProperties
     const Class *clasp = types->getKnownClass(constraints);
 
     // Note: typed arrays have indexed properties not accounted for by type
     // information, though these are all in bounds and will be accounted for
     // by JIT paths.
     if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp)))
         return true;
 
-    if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))
+    if (types->hasObjectFlags(constraints, OBJECT_FLAG_SPARSE_INDEXES))
         return true;
 
     JSObject *proto = types->getCommonPrototype(constraints);
     if (!proto)
         return true;
 
     return PrototypeHasIndexedProperty(constraints, proto);
 }
@@ -2549,23 +2364,23 @@ TypeZone::addPendingRecompile(JSContext 
     // When one script is inlined into another the caller listens to state
     // changes on the callee's script, so trigger these to force recompilation
     // of any such callers.
     if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup())
         ObjectStateChange(cx, script->functionNonDelazifying()->group(), false);
 }
 
 void
-TypeCompartment::print(JSContext *cx, bool force)
+types::PrintTypes(JSContext *cx, JSCompartment *comp, bool force)
 {
 #ifdef DEBUG
     gc::AutoSuppressGC suppressGC(cx);
     JSAutoRequest request(cx);
 
-    Zone *zone = compartment()->zone();
+    Zone *zone = comp->zone();
     AutoEnterAnalysis enter(nullptr, zone);
 
     if (!force && !InferSpewActive(ISpewResult))
         return;
 
     for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         RootedScript script(cx, i.get<JSScript>());
         if (script->types())
@@ -2575,406 +2390,19 @@ TypeCompartment::print(JSContext *cx, bo
     for (gc::ZoneCellIter i(zone, gc::FINALIZE_OBJECT_GROUP); !i.done(); i.next()) {
         ObjectGroup *group = i.get<ObjectGroup>();
         group->print();
     }
 #endif
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeCompartment tables
-/////////////////////////////////////////////////////////////////////
-
-/*
- * The arrayTypeTable and objectTypeTable are per-compartment tables for making
- * common groups to model the contents of large script singletons and JSON
- * objects. These are vanilla Arrays and native Objects, so we distinguish the
- * types of different ones by looking at the types of their properties.
- *
- * All singleton/JSON arrays which have the same prototype, are homogenous and
- * of the same element type will share a group. All singleton/JSON objects
- * which have the same shape and property types will also share a group.
- * We don't try to collate arrays or objects that have type mismatches.
- */
-
-static inline bool
-NumberTypes(Type a, Type b)
-{
-    return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
-        && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
-}
-
-/*
- * As for GetValueType, but requires object types to be non-singletons with
- * their default prototype. These are the only values that should appear in
- * arrays and objects whose type can be fixed.
- */
-static inline Type
-GetValueTypeForTable(const Value &v)
-{
-    Type type = GetValueType(v);
-    MOZ_ASSERT(!type.isSingleton());
-    return type;
-}
-
-struct types::ArrayTableKey : public DefaultHasher<types::ArrayTableKey>
-{
-    Type type;
-    JSObject *proto;
-
-    ArrayTableKey()
-      : type(Type::UndefinedType()), proto(nullptr)
-    {}
-
-    ArrayTableKey(Type type, JSObject *proto)
-      : type(type), proto(proto)
-    {}
-
-    static inline uint32_t hash(const ArrayTableKey &v) {
-        return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
-    }
-
-    static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) {
-        return v1.type == v2.type && v1.proto == v2.proto;
-    }
-
-    bool operator==(const ArrayTableKey& other) {
-        return type == other.type && proto == other.proto;
-    }
-
-    bool operator!=(const ArrayTableKey& other) {
-        return !(*this == other);
-    }
-};
-
-void
-TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
-                                          JSObject *obj, Type elementType)
-{
-    MOZ_ASSERT(cx->zone()->types.activeAnalysis);
-
-    if (!arrayTypeTable) {
-        arrayTypeTable = cx->new_<ArrayTypeTable>();
-        if (!arrayTypeTable || !arrayTypeTable->init()) {
-            arrayTypeTable = nullptr;
-            return;
-        }
-    }
-
-    ArrayTableKey key(elementType, obj->getProto());
-    DependentAddPtr<ArrayTypeTable> p(cx, *arrayTypeTable, key);
-    if (p) {
-        obj->setGroup(p->value());
-    } else {
-        /* Make a new type to use for future arrays with the same elements. */
-        RootedObject objProto(cx, obj->getProto());
-        Rooted<TaggedProto> taggedProto(cx, TaggedProto(objProto));
-        ObjectGroup *group = newObjectGroup(cx, &ArrayObject::class_, taggedProto);
-        if (!group)
-            return;
-        obj->setGroup(group);
-
-        AddTypePropertyId(cx, group, JSID_VOID, elementType);
-
-        key.proto = objProto;
-        (void) p.add(cx, *arrayTypeTable, key, group);
-    }
-}
-
-void
-TypeCompartment::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj)
-{
-    AutoEnterAnalysis enter(cx);
-
-    /*
-     * If the array is of homogenous type, pick a group which will be
-     * shared with all other singleton/JSON arrays of the same type.
-     * If the array is heterogenous, keep the existing group, which has
-     * unknown properties.
-     */
-
-    unsigned len = obj->getDenseInitializedLength();
-    if (len == 0)
-        return;
-
-    Type type = GetValueTypeForTable(obj->getDenseElement(0));
-
-    for (unsigned i = 1; i < len; i++) {
-        Type ntype = GetValueTypeForTable(obj->getDenseElement(i));
-        if (ntype != type) {
-            if (NumberTypes(type, ntype))
-                type = Type::DoubleType();
-            else
-                return;
-        }
-    }
-
-    setTypeToHomogenousArray(cx, obj, type);
-}
-
-void
-types::FixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj)
-{
-    cx->compartment()->types.fixRestArgumentsType(cx, obj);
-}
-
-void
-TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj)
-{
-    AutoEnterAnalysis enter(cx);
-
-    /*
-     * Tracking element types for rest argument arrays is not worth it, but we
-     * still want it to be known that it's a dense array.
-     */
-    setTypeToHomogenousArray(cx, obj, Type::UnknownType());
-}
-
-/*
- * 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 types::ObjectTableKey
-{
-    jsid *properties;
-    uint32_t nproperties;
-    uint32_t nfixed;
-
-    struct Lookup {
-        IdValuePair *properties;
-        uint32_t nproperties;
-        uint32_t nfixed;
-
-        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.nfixed);
-    }
-
-    static inline bool match(const ObjectTableKey &v, const Lookup &lookup) {
-        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 types::ObjectTableEntry
-{
-    ReadBarrieredObjectGroup group;
-    ReadBarrieredShape shape;
-    Type *types;
-};
-
-static inline void
-UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry,
-                            IdValuePair *properties, size_t nproperties)
-{
-    if (entry.group->unknownProperties())
-        return;
-    for (size_t i = 0; i < nproperties; i++) {
-        Type type = entry.types[i];
-        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] = Type::DoubleType();
-            }
-            AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype);
-        }
-    }
-}
-
-void
-TypeCompartment::fixObjectGroup(ExclusiveContext *cx, PlainObject *obj)
-{
-    AutoEnterAnalysis enter(cx);
-
-    if (!objectTypeTable) {
-        objectTypeTable = cx->new_<ObjectTypeTable>();
-        if (!objectTypeTable || !objectTypeTable->init()) {
-            js_delete(objectTypeTable);
-            objectTypeTable = nullptr;
-            return;
-        }
-    }
-
-    /*
-     * Use the same 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;
-
-    Vector<IdValuePair> properties(cx);
-    if (!properties.resize(obj->slotSpan()))
-        return;
-
-    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();
-    }
-
-    ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots());
-    ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup);
-
-    if (p) {
-        MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject());
-        MOZ_ASSERT(obj->lastProperty() == p->value().shape);
-
-        UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length());
-        obj->setGroup(p->value().group);
-        return;
-    }
-
-    /* Make a new type to use for the object and similar future ones. */
-    Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
-    ObjectGroup *group = newObjectGroup(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>(properties.length()));
-    if (!ids)
-        return;
-
-    ScopedJSFreePtr<Type> types(group->zone()->pod_calloc<Type>(properties.length()));
-    if (!types)
-        return;
-
-    for (size_t i = 0; i < properties.length(); i++) {
-        ids[i] = properties[i].id;
-        types[i] = GetValueTypeForTable(obj->getSlot(i));
-        AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]);
-    }
-
-    ObjectTableKey key;
-    key.properties = ids;
-    key.nproperties = properties.length();
-    key.nfixed = obj->numFixedSlots();
-    MOZ_ASSERT(ObjectTableKey::match(key, lookup));
-
-    ObjectTableEntry entry;
-    entry.group.set(group);
-    entry.shape.set(obj->lastProperty());
-    entry.types = types;
-
-    obj->setGroup(group);
-
-    p = objectTypeTable->lookupForAdd(lookup);
-    if (objectTypeTable->add(p, key, entry)) {
-        ids.forget();
-        types.forget();
-    }
-}
-
-JSObject *
-TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties)
-{
-    AutoEnterAnalysis enter(cx);
-
-    if (!objectTypeTable) {
-        objectTypeTable = cx->new_<ObjectTypeTable>();
-        if (!objectTypeTable || !objectTypeTable->init()) {
-            js_delete(objectTypeTable);
-            objectTypeTable = nullptr;
-            return nullptr;
-        }
-    }
-
-    /*
-     * Use the object group table to allocate an object with the specified
-     * properties, filling in its final type and shape and failing if no cache
-     * entry could be found for the properties.
-     */
-
-    /*
-     * 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_);
-
-    ObjectTableKey::Lookup lookup(properties, nproperties, nfixed);
-    ObjectTypeTable::AddPtr p = objectTypeTable->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());
-
-    RootedShape shape(cx, p->value().shape);
-    if (!NativeObject::setLastProperty(cx, obj, shape)) {
-        cx->clearPendingException();
-        return nullptr;
-    }
-
-    UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties);
-
-    for (size_t i = 0; i < nproperties; i++)
-        obj->setSlot(i, properties[i].value);
-
-    obj->setGroup(p->value().group);
-    return obj;
-}
-
-/////////////////////////////////////////////////////////////////////
 // ObjectGroup
 /////////////////////////////////////////////////////////////////////
 
-void
-ObjectGroup::setProto(JSContext *cx, TaggedProto proto)
-{
-    MOZ_ASSERT(singleton());
-
-    if (proto.isObject() && IsInsideNursery(proto.toObject()))
-        addFlags(OBJECT_FLAG_NURSERY_PROTO);
-
-    setProtoUnchecked(proto);
-}
-
 static inline void
 UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, NativeObject *obj, Shape *shape,
                    bool indexed)
 {
     MOZ_ASSERT(obj->isSingleton() && !obj->hasLazyGroup());
 
     if (!shape->writable())
         types->setNonWritableProperty(cx);
@@ -3306,22 +2734,18 @@ ObjectGroup::detachNewScript(bool writeB
     // Clear the TypeNewScript from this ObjectGroup and, if it has been
     // analyzed, remove it from the newObjectGroups table so that it will not be
     // produced by calling 'new' on the associated function anymore.
     // The TypeNewScript is not actually destroyed.
     TypeNewScript *newScript = anyNewScript();
     MOZ_ASSERT(newScript);
 
     if (newScript->analyzed()) {
-        NewObjectGroupTable &newObjectGroups = newScript->function()->compartment()->newObjectGroups;
-        NewObjectGroupTable::Ptr p =
-            newObjectGroups.lookup(NewObjectGroupTable::Lookup(nullptr, proto(), newScript->function()));
-        MOZ_ASSERT(p->group == this);
-
-        newObjectGroups.remove(p);
+        newScript->function()->compartment()->objectGroups.removeDefaultNewGroup(nullptr, proto(),
+                                                                                 newScript->function());
     }
 
     if (this->newScript())
         setAddendum(Addendum_None, nullptr, writeBarrier);
     else
         unboxedLayout().setNewScript(nullptr, writeBarrier);
 }
 
@@ -3631,60 +3055,16 @@ types::FillBytecodeTypeMap(JSScript *scr
             bytecodeMap[added++] = script->pcToOffset(pc);
             if (added == script->nTypeSets())
                 break;
         }
     }
     MOZ_ASSERT(added == script->nTypeSets());
 }
 
-ArrayObject *
-types::GetOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc)
-{
-    // Make sure that the template object for script/pc has a type indicating
-    // that the object and its copies have copy on write elements.
-    RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>());
-    MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
-
-    if (obj->group()->fromAllocationSite()) {
-        MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
-        return obj;
-    }
-
-    RootedObjectGroup group(cx, TypeScript::InitGroup(cx, script, pc, JSProto_Array));
-    if (!group)
-        return nullptr;
-
-    group->addFlags(OBJECT_FLAG_COPY_ON_WRITE);
-
-    // Update type information in the initializer object group.
-    MOZ_ASSERT(obj->slotSpan() == 0);
-    for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
-        const Value &v = obj->getDenseElement(i);
-        AddTypePropertyId(cx, group, JSID_VOID, v);
-    }
-
-    obj->setGroup(group);
-    return obj;
-}
-
-ArrayObject *
-types::GetCopyOnWriteObject(JSScript *script, jsbytecode *pc)
-{
-    // GetOrFixupCopyOnWriteObject should already have been called for
-    // script/pc, ensuring that the template object has a group with the
-    // COPY_ON_WRITE flag. We don't assert this here, due to a corner case
-    // where this property doesn't hold. See jsop_newarray_copyonwrite in
-    // IonBuilder.
-    ArrayObject *obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>();
-    MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
-
-    return obj;
-}
-
 void
 types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
     if (!script->hasBaselineScript())
@@ -3692,76 +3072,21 @@ types::TypeMonitorResult(JSContext *cx, 
 
     AutoEnterAnalysis enter(cx);
 
     Type type = GetValueType(rval);
     StackTypeSet *types = TypeScript::BytecodeTypes(script, pc);
     if (types->hasType(type))
         return;
 
-    InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
-              script->id(), script->pcToOffset(pc), TypeString(type));
+    InferSpew(ISpewOps, "bytecodeType: %p %05u: %s",
+              script, script->pcToOffset(pc), TypeString(type));
     types->addType(cx, type);
 }
 
-bool
-types::UseSingletonForClone(JSFunction *fun)
-{
-    if (!fun->isInterpreted())
-        return false;
-
-    if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite())
-        return true;
-
-    if (fun->isArrow())
-        return false;
-
-    if (fun->isSingleton())
-        return false;
-
-    /*
-     * When a function is being used as a wrapper for another function, it
-     * improves precision greatly to distinguish between different instances of
-     * the wrapper; otherwise we will conflate much of the information about
-     * the wrapped functions.
-     *
-     * An important example is the Class.create function at the core of the
-     * Prototype.js library, which looks like:
-     *
-     * var Class = {
-     *   create: function() {
-     *     return function() {
-     *       this.initialize.apply(this, arguments);
-     *     }
-     *   }
-     * };
-     *
-     * Each instance of the innermost function will have a different wrapped
-     * initialize method. We capture this, along with similar cases, by looking
-     * for short scripts which use both .apply and arguments. For such scripts,
-     * whenever creating a new instance of the function we both give that
-     * instance a singleton type and clone the underlying script.
-     */
-
-    uint32_t begin, end;
-    if (fun->hasScript()) {
-        if (!fun->nonLazyScript()->usesArgumentsApplyAndThis())
-            return false;
-        begin = fun->nonLazyScript()->sourceStart();
-        end = fun->nonLazyScript()->sourceEnd();
-    } else {
-        if (!fun->lazyScript()->usesArgumentsApplyAndThis())
-            return false;
-        begin = fun->lazyScript()->begin();
-        end = fun->lazyScript()->end();
-    }
-
-    return end - begin <= 100;
-}
-
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
 bool
 JSScript::makeTypes(JSContext *cx)
 {
     MOZ_ASSERT(!types_);
@@ -3776,48 +3101,48 @@ JSScript::makeTypes(JSContext *cx)
         return false;
 
     types_ = typeScript;
     setTypesGeneration(cx->zone()->types.generation);
 
 #ifdef DEBUG
     StackTypeSet *typeArray = typeScript->typeArray();
     for (unsigned i = 0; i < nTypeSets(); i++) {
-        InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
+        InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p",
                   InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
-                  i, id());
+                  i, this);
     }
     TypeSet *thisTypes = TypeScript::ThisTypes(this);
-    InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
+    InferSpew(ISpewOps, "typeSet: %sT%p%s this %p",
               InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
-              id());
+              this);
     unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
     for (unsigned i = 0; i < nargs; i++) {
         TypeSet *types = TypeScript::ArgTypes(this, i);
-        InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
+        InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u %p",
                   InferSpewColor(types), types, InferSpewColorReset(),
-                  i, id());
+                  i, this);
     }
 #endif
 
     return true;
 }
 
 /* static */ bool
 JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun,
                                        bool singleton /* = false */)
 {
     if (singleton) {
         if (!setSingleton(cx, fun))
             return false;
     } else {
         RootedObject funProto(cx, fun->getProto());
         Rooted<TaggedProto> taggedProto(cx, TaggedProto(funProto));
-        ObjectGroup *group =
-            cx->compartment()->types.newObjectGroup(cx, &JSFunction::class_, taggedProto);
+        ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_,
+                                                               taggedProto);
         if (!group)
             return false;
 
         fun->setGroup(group);
         group->setInterpretedFunction(fun);
     }
 
     return true;
@@ -4207,32 +3532,28 @@ TypeNewScript::maybeAnalyze(JSContext *c
     // existing group to represent fully initialized objects with all
     // definite properties in the prefix shape, and make a new group to
     // represent partially initialized objects.
     MOZ_ASSERT(prefixShape->slotSpan() > templateObject()->slotSpan());
 
     ObjectGroupFlags initialFlags = group->flags() & OBJECT_FLAG_DYNAMIC_MASK;
 
     Rooted<TaggedProto> protoRoot(cx, group->proto());
-    ObjectGroup *initialGroup =
-        cx->compartment()->types.newObjectGroup(cx, group->clasp(), protoRoot, initialFlags);
+    ObjectGroup *initialGroup = ObjectGroupCompartment::makeGroup(cx, group->clasp(), protoRoot,
+                                                                  initialFlags);
     if (!initialGroup)
         return false;
 
     if (!initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty()))
         return false;
     if (!group->addDefiniteProperties(cx, prefixShape))
         return false;
 
-    NewObjectGroupTable &table = cx->compartment()->newObjectGroups;
-    NewObjectGroupTable::Lookup lookup(nullptr, group->proto(), function());
-
-    MOZ_ASSERT(table.lookup(lookup)->group == group);
-    table.remove(lookup);
-    table.putNew(lookup, NewObjectGroupEntry(initialGroup, function()));
+    cx->compartment()->objectGroups.replaceDefaultNewGroup(nullptr, group->proto(), function(),
+                                                           initialGroup);
 
     templateObject()->setGroup(initialGroup);
 
     // Transfer this TypeNewScript from the fully initialized group to the
     // partially initialized group.
     group->setNewScript(nullptr);
     initialGroup->setNewScript(this);
 
@@ -4363,391 +3684,16 @@ TypeNewScript::trace(JSTracer *trc)
 void
 TypeNewScript::sweep()
 {
     if (preliminaryObjects)
         preliminaryObjects->sweep();
 }
 
 /////////////////////////////////////////////////////////////////////
-// JSObject
-/////////////////////////////////////////////////////////////////////
-
-bool
-JSObject::shouldSplicePrototype(JSContext *cx)
-{
-    /*
-     * During bootstrapping, if inference is enabled we need to make sure not
-     * to splice a new prototype in for Function.prototype or the global
-     * object if their __proto__ had previously been set to null, as this
-     * will change the prototype for all other objects with the same type.
-     */
-    if (getProto() != nullptr)
-        return false;
-    return isSingleton();
-}
-
-bool
-JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto)
-{
-    MOZ_ASSERT(cx->compartment() == compartment());
-
-    RootedObject self(cx, this);
-
-    /*
-     * For singleton groups representing only a single JSObject, the proto
-     * can be rearranged as needed without destroying type information for
-     * the old or new types.
-     */
-    MOZ_ASSERT(self->isSingleton());
-
-    // Inner objects may not appear on prototype chains.
-    MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
-
-    if (proto.isObject() && !proto.toObject()->setDelegate(cx))
-        return false;
-
-    // Force type instantiation when splicing lazy group.
-    RootedObjectGroup group(cx, self->getGroup(cx));
-    if (!group)
-        return false;
-    RootedObjectGroup protoGroup(cx, nullptr);
-    if (proto.isObject()) {
-        protoGroup = proto.toObject()->getGroup(cx);
-        if (!protoGroup)
-            return false;
-    }
-
-    group->setClasp(clasp);
-    group->setProto(cx, proto);
-    return true;
-}
-
-/* static */ ObjectGroup *
-JSObject::makeLazyGroup(JSContext *cx, HandleObject obj)
-{
-    MOZ_ASSERT(obj->hasLazyGroup());
-    MOZ_ASSERT(cx->compartment() == obj->compartment());
-
-    /* De-lazification of functions can GC, so we need to do it up here. */
-    if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
-        RootedFunction fun(cx, &obj->as<JSFunction>());
-        if (!fun->getOrCreateScript(cx))
-            return nullptr;
-    }
-
-    // Find flags which need to be specified immediately on the object.
-    // Don't track whether singletons are packed.
-    ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED;
-
-    if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
-        initialFlags |= OBJECT_FLAG_ITERATED;
-
-    if (obj->isIndexed())
-        initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
-
-    if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
-        initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
-
-    Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
-    ObjectGroup *group = cx->compartment()->types.newObjectGroup(cx, obj->getClass(), proto,
-                                                                 initialFlags);
-    if (!group)
-        return nullptr;
-
-    AutoEnterAnalysis enter(cx);
-
-    /* Fill in the type according to the state of this object. */
-
-    group->initSingleton(obj);
-
-    if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
-        group->setInterpretedFunction(&obj->as<JSFunction>());
-
-    obj->group_ = group;
-
-    return group;
-}
-
-/* static */ inline HashNumber
-NewObjectGroupEntry::hash(const Lookup &lookup)
-{
-    return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^
-           PointerHasher<const Class *, 3>::hash(lookup.clasp) ^
-           PointerHasher<JSObject *, 3>::hash(lookup.associated);
-}
-
-/* static */ inline bool
-NewObjectGroupEntry::match(const NewObjectGroupEntry &key, const Lookup &lookup)
-{
-    return key.group->proto() == lookup.matchProto &&
-           (!lookup.clasp || key.group->clasp() == lookup.clasp) &&
-           key.associated == lookup.associated;
-}
-
-#ifdef DEBUG
-bool
-JSObject::hasNewGroup(const Class *clasp, ObjectGroup *group)
-{
-    NewObjectGroupTable &table = compartment()->newObjectGroups;
-
-    if (!table.initialized())
-        return false;
-
-    NewObjectGroupTable::Ptr p =
-        table.lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(this), nullptr));
-    return p && p->group == group;
-}
-#endif /* DEBUG */
-
-/* static */ bool
-JSObject::setNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj)
-{
-    if (!obj->setFlag(cx, js::BaseShape::NEW_GROUP_UNKNOWN))
-        return false;
-
-    // If the object already has a new group, mark that group as unknown.
-    NewObjectGroupTable &table = cx->compartment()->newObjectGroups;
-    if (table.initialized()) {
-        Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj));
-        NewObjectGroupTable::Ptr p =
-            table.lookup(NewObjectGroupTable::Lookup(clasp, taggedProto, nullptr));
-        if (p)
-            MarkObjectGroupUnknownProperties(cx, p->group);
-    }
-
-    return true;
-}
-
-/*
- * This class is used to add a post barrier on the newObjectGroups set, as the
- * key is calculated from a prototype object which may be moved by generational
- * GC.
- */
-class NewObjectGroupsSetRef : public BufferableRef
-{
-    NewObjectGroupTable *set;
-    const Class *clasp;
-    JSObject *proto;
-    JSObject *associated;
-
-  public:
-    NewObjectGroupsSetRef(NewObjectGroupTable *s, const Class *clasp, JSObject *proto, JSObject *associated)
-        : set(s), clasp(clasp), proto(proto), associated(associated)
-    {}
-
-    void mark(JSTracer *trc) {
-        JSObject *prior = proto;
-        trc->setTracingLocation(&*prior);
-        Mark(trc, &proto, "newObjectGroups set prototype");
-        if (prior == proto)
-            return;
-
-        NewObjectGroupTable::Ptr p =
-            set->lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto),
-                                                    associated));
-        if (!p)
-            return;
-
-        set->rekeyAs(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated),
-                     NewObjectGroupTable::Lookup(clasp, TaggedProto(proto), associated), *p);
-    }
-};
-
-static void
-ObjectGroupTablePostBarrier(ExclusiveContext *cx, NewObjectGroupTable *table,
-                           const Class *clasp, TaggedProto proto, JSObject *associated)
-{
-    MOZ_ASSERT_IF(associated, !IsInsideNursery(associated));
-
-    if (!proto.isObject())
-        return;
-
-    if (!cx->isJSContext()) {
-        MOZ_ASSERT(!IsInsideNursery(proto.toObject()));
-        return;
-    }
-
-    if (IsInsideNursery(proto.toObject())) {
-        StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer;
-        sb.putGeneric(NewObjectGroupsSetRef(table, clasp, proto.toObject(), associated));
-    }
-}
-
-ObjectGroup *
-ExclusiveContext::getNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated)
-{
-    MOZ_ASSERT_IF(associated, proto.isObject());
-    MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>());
-    MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject()));
-
-    // A null lookup clasp is used for 'new' groups with an associated
-    // function. The group starts out as a plain object but might mutate into an
-    // unboxed plain object.
-    MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>()));
-
-    NewObjectGroupTable &newObjectGroups = compartment()->newObjectGroups;
-
-    if (!newObjectGroups.initialized() && !newObjectGroups.init())
-        return nullptr;
-
-    if (associated && associated->is<JSFunction>()) {
-        MOZ_ASSERT(!clasp);
-
-        // Canonicalize new functions to use the original one associated with its script.
-        JSFunction *fun = &associated->as<JSFunction>();
-        if (fun->hasScript())
-            associated = fun->nonLazyScript()->functionNonDelazifying();
-        else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
-            associated = fun->lazyScript()->functionNonDelazifying();
-        else
-            associated = nullptr;
-
-        // If we have previously cleared the 'new' script information for this
-        // function, don't try to construct another one.
-        if (associated && associated->wasNewScriptCleared())
-            associated = nullptr;
-
-        if (!associated)
-            clasp = &PlainObject::class_;
-    }
-
-    NewObjectGroupTable::AddPtr p =
-        newObjectGroups.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, associated));
-    if (p) {
-        ObjectGroup *group = p->group;
-        MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
-        MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
-                              group->clasp() == &UnboxedPlainObject::class_);
-        MOZ_ASSERT(group->proto() == proto);
-        return group;
-    }
-
-    AutoEnterAnalysis enter(this);
-
-    if (proto.isObject() && !proto.toObject()->setDelegate(this))
-        return nullptr;
-
-    ObjectGroupFlags initialFlags = 0;
-    if (!proto.isObject() || proto.toObject()->isNewGroupUnknown())
-        initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
-
-    Rooted<TaggedProto> protoRoot(this, proto);
-    ObjectGroup *group = compartment()->types.newObjectGroup(this,
-                                                             clasp ? clasp : &PlainObject::class_,
-                                                             protoRoot, initialFlags);
-    if (!group)
-        return nullptr;
-
-    if (!newObjectGroups.add(p, NewObjectGroupEntry(group, associated)))
-        return nullptr;
-
-    ObjectGroupTablePostBarrier(this, &newObjectGroups, clasp, proto, associated);
-
-    if (proto.isObject()) {
-        RootedObject obj(this, proto.toObject());
-
-        if (associated) {
-            if (associated->is<JSFunction>())
-                TypeNewScript::make(asJSContext(), group, &associated->as<JSFunction>());
-            else
-                group->setTypeDescr(&associated->as<TypeDescr>());
-        }
-
-        /*
-         * Some builtin objects have slotful native properties baked in at
-         * creation via the Shape::{insert,get}initialShape mechanism. Since
-         * these properties are never explicitly defined on new objects, update
-         * the type information for them here.
-         */
-
-        if (obj->is<RegExpObject>()) {
-            AddTypePropertyId(this, group, NameToId(names().source), Type::StringType());
-            AddTypePropertyId(this, group, NameToId(names().global), Type::BooleanType());
-            AddTypePropertyId(this, group, NameToId(names().ignoreCase), Type::BooleanType());
-            AddTypePropertyId(this, group, NameToId(names().multiline), Type::BooleanType());
-            AddTypePropertyId(this, group, NameToId(names().sticky), Type::BooleanType());
-            AddTypePropertyId(this, group, NameToId(names().lastIndex), Type::Int32Type());
-        }
-
-        if (obj->is<StringObject>())
-            AddTypePropertyId(this, group, NameToId(names().length), Type::Int32Type());
-
-        if (obj->is<ErrorObject>()) {
-            AddTypePropertyId(this, group, NameToId(names().fileName), Type::StringType());
-            AddTypePropertyId(this, group, NameToId(names().lineNumber), Type::Int32Type());
-            AddTypePropertyId(this, group, NameToId(names().columnNumber), Type::Int32Type());
-            AddTypePropertyId(this, group, NameToId(names().stack), Type::StringType());
-        }
-    }
-
-    return group;
-}
-
-ObjectGroup *
-ExclusiveContext::getLazySingletonGroup(const Class *clasp, TaggedProto proto)
-{
-    MOZ_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment());
-
-    AutoEnterAnalysis enter(this);
-
-    NewObjectGroupTable &table = compartment()->lazyObjectGroups;
-
-    if (!table.initialized() && !table.init())
-        return nullptr;
-
-    NewObjectGroupTable::AddPtr p = table.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, nullptr));
-    if (p) {
-        ObjectGroup *group = p->group;
-        MOZ_ASSERT(group->lazy());
-
-        return group;
-    }
-
-    Rooted<TaggedProto> protoRoot(this, proto);
-    ObjectGroup *group = compartment()->types.newObjectGroup(this, clasp, protoRoot);
-    if (!group)
-        return nullptr;
-
-    if (!table.add(p, NewObjectGroupEntry(group, nullptr)))
-        return nullptr;
-
-    ObjectGroupTablePostBarrier(this, &table, clasp, proto, nullptr);
-
-    group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON);
-    MOZ_ASSERT(group->singleton(), "created group must be a proper singleton");
-
-    return group;
-}
-
-bool
-ExclusiveContext::findAllocationSiteForType(Type type, JSScript **script, uint32_t *offset) const
-{
-    *script = nullptr;
-    *offset = 0;
-
-    if (type.isUnknown() || type.isAnyObject() || !type.isGroup())
-        return false;
-    ObjectGroup *obj = type.group();
-
-    const AllocationSiteTable *table = compartment()->types.allocationSiteTable;
-    if (!table)
-        return false;
-
-    for (AllocationSiteTable::Range r = table->all(); !r.empty(); r.popFront()) {
-        if (obj == r.front().value()) {
-            *script = r.front().key().script;
-            *offset = r.front().key().offset;
-            return true;
-        }
-    }
-    return false;
-}
-
-/////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 void
 ConstraintTypeSet::sweep(Zone *zone, AutoClearTypeInferenceStateOnOOM &oom)
 {
     MOZ_ASSERT(zone->isGCSweepingOrCompacting());
 
@@ -4950,219 +3896,16 @@ ObjectGroup::maybeSweep(AutoClearTypeInf
                 addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES);
                 clearProperties();
                 return;
             }
         }
     }
 }
 
-void
-TypeCompartment::clearTables()
-{
-    if (allocationSiteTable && allocationSiteTable->initialized())
-        allocationSiteTable->clear();
-    if (arrayTypeTable && arrayTypeTable->initialized())
-        arrayTypeTable->clear();
-    if (objectTypeTable && objectTypeTable->initialized())
-        objectTypeTable->clear();
-}
-
-void
-TypeCompartment::sweep(FreeOp *fop)
-{
-    /*
-     * Iterate through the array/object group tables and remove all entries
-     * referencing collected data. These tables only hold weak references.
-     */
-
-    if (arrayTypeTable) {
-        for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) {
-            ArrayTableKey key = e.front().key();
-            MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton());
-
-            bool remove = false;
-            if (!key.type.isUnknown() && key.type.isGroup()) {
-                ObjectGroup *group = key.type.groupNoBarrier();
-                if (IsObjectGroupAboutToBeFinalized(&group))
-                    remove = true;
-                else
-                    key.type = Type::ObjectType(group);
-            }
-            if (key.proto && key.proto != TaggedProto::LazyProto &&
-                IsObjectAboutToBeFinalized(&key.proto))
-            {
-                remove = true;
-            }
-            if (IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet()))
-                remove = true;
-
-            if (remove)
-                e.removeFront();
-            else if (key != e.front().key())
-                e.rekeyFront(key);
-        }
-    }
-
-    if (objectTypeTable) {
-        for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
-            const ObjectTableKey &key = e.front().key();
-            ObjectTableEntry &entry = e.front().value();
-
-            bool remove = false;
-            if (IsObjectGroupAboutToBeFinalized(entry.group.unsafeGet()))
-                remove = true;
-            if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet()))
-                remove = true;
-            for (unsigned i = 0; !remove && i < key.nproperties; i++) {
-                if (JSID_IS_STRING(key.properties[i])) {
-                    JSString *str = JSID_TO_STRING(key.properties[i]);
-                    if (IsStringAboutToBeFinalized(&str))
-                        remove = true;
-                    MOZ_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
-                } else if (JSID_IS_SYMBOL(key.properties[i])) {
-                    JS::Symbol *sym = JSID_TO_SYMBOL(key.properties[i]);
-                    if (IsSymbolAboutToBeFinalized(&sym))
-                        remove = true;
-                }
-
-                MOZ_ASSERT(!entry.types[i].isSingleton());
-                ObjectGroup *group = nullptr;
-                if (entry.types[i].isGroup()) {
-                    group = entry.types[i].groupNoBarrier();
-                    if (IsObjectGroupAboutToBeFinalized(&group))
-                        remove = true;
-                    else if (group != entry.types[i].groupNoBarrier())
-                        entry.types[i] = Type::ObjectType(group);
-                }
-            }
-
-            if (remove) {
-                js_free(key.properties);
-                js_free(entry.types);
-                e.removeFront();
-            }
-        }
-    }
-
-    if (allocationSiteTable) {
-        for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
-            AllocationSiteKey key = e.front().key();
-            bool keyDying = IsScriptAboutToBeFinalized(&key.script);
-            bool valDying = IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet());
-            if (keyDying || valDying)
-                e.removeFront();
-            else if (key.script != e.front().key().script)
-                e.rekeyFront(key);
-        }
-    }
-}
-
-void
-JSCompartment::sweepNewObjectGroupTable(NewObjectGroupTable &table)
-{
-    MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting());
-    if (table.initialized()) {
-        for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) {
-            NewObjectGroupEntry entry = e.front();
-            if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) ||
-                (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated)))
-            {
-                e.removeFront();
-            } else {
-                /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */
-                MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet());
-                MOZ_ASSERT(entry.associated == e.front().associated);
-            }
-        }
-    }
-}
-
-void
-JSCompartment::fixupNewObjectGroupTable(NewObjectGroupTable &table)
-{
-    /*
-     * Each entry's hash depends on the object's prototype and we can't tell
-     * whether that has been moved or not in sweepNewObjectGroupTable().
-     */
-    MOZ_ASSERT(zone()->isCollecting());
-    if (table.initialized()) {
-        for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) {
-            NewObjectGroupEntry entry = e.front();
-            bool needRekey = false;
-            if (IsForwarded(entry.group.get())) {
-                entry.group.set(Forwarded(entry.group.get()));
-                needRekey = true;
-            }
-            TaggedProto proto = entry.group->proto();
-            if (proto.isObject() && IsForwarded(proto.toObject())) {
-                proto = TaggedProto(Forwarded(proto.toObject()));
-                needRekey = true;
-            }
-            if (entry.associated && IsForwarded(entry.associated)) {
-                entry.associated = Forwarded(entry.associated);
-                needRekey = true;
-            }
-            if (needRekey) {
-                const Class *clasp = entry.group->clasp();
-                if (entry.associated && entry.associated->is<JSFunction>())
-                    clasp = nullptr;
-                NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated);
-                e.rekeyFront(lookup, entry);
-            }
-        }
-    }
-}
-
-#ifdef JSGC_HASH_TABLE_CHECKS
-
-void
-JSCompartment::checkObjectGroupTablesAfterMovingGC()
-{
-    checkObjectGroupTableAfterMovingGC(newObjectGroups);
-    checkObjectGroupTableAfterMovingGC(lazyObjectGroups);
-}
-
-void
-JSCompartment::checkObjectGroupTableAfterMovingGC(NewObjectGroupTable &table)
-{
-    /*
-     * Assert that nothing points into the nursery or needs to be relocated, and
-     * that the hash table entries are discoverable.
-     */
-    if (!table.initialized())
-        return;
-
-    for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) {
-        NewObjectGroupEntry entry = e.front();
-        CheckGCThingAfterMovingGC(entry.group.get());
-        TaggedProto proto = entry.group->proto();
-        if (proto.isObject())
-            CheckGCThingAfterMovingGC(proto.toObject());
-        CheckGCThingAfterMovingGC(entry.associated);
-
-        const Class *clasp = entry.group->clasp();
-        if (entry.associated && entry.associated->is<JSFunction>())
-            clasp = nullptr;
-
-        NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated);
-        NewObjectGroupTable::Ptr ptr = table.lookup(lookup);
-        MOZ_ASSERT(ptr.found() && &*ptr == &e.front());
-    }
-}
-
-#endif // JSGC_HASH_TABLE_CHECKS
-
-TypeCompartment::~TypeCompartment()
-{
-    js_delete(arrayTypeTable);
-    js_delete(objectTypeTable);
-    js_delete(allocationSiteTable);
-}
-
 /* static */ void
 JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM *oom)
 {
     if (!types_ || typesGeneration() == zone()->types.generation)
         return;
 
     setTypesGeneration(zone()->types.generation);
 
@@ -5216,55 +3959,16 @@ Zone::addSizeOfIncludingThis(mozilla::Ma
 {
     *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
     if (jitZone()) {
         *baselineStubsOptimized +=
             jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf);
     }
 }
 
-void
-TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                        size_t *allocationSiteTables,
-                                        size_t *arrayTypeTables,
-                                        size_t *objectTypeTables)
-{
-    if (allocationSiteTable)
-        *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
-
-    if (arrayTypeTable)
-        *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf);
-
-    if (objectTypeTable) {
-        *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf);
-
-        for (ObjectTypeTable::Enum e(*objectTypeTable);
-             !e.empty();
-             e.popFront())
-        {
-            const ObjectTableKey &key = e.front().key();
-            const ObjectTableEntry &value = e.front().value();
-
-            /* key.ids and values.types have the same length. */
-            *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
-        }
-    }
-}
-
-size_t
-ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
-{
-    size_t n = 0;
-    if (TypeNewScript *newScript = newScriptDontCheckGeneration())
-        n += newScript->sizeOfIncludingThis(mallocSizeOf);
-    if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration())
-        n += layout->sizeOfIncludingThis(mallocSizeOf);
-    return n;
-}
-
 TypeZone::TypeZone(Zone *zone)
   : zone_(zone),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     generation(0),
     compilerOutputs(nullptr),
     sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     sweepCompilerOutputs(nullptr),
     sweepReleaseTypes(false),
@@ -5320,19 +4024,16 @@ TypeZone::beginSweep(FreeOp *fop, bool r
         sweepCompilerOutputs = compilerOutputs;
         compilerOutputs = newCompilerOutputs;
     }
 
     // All existing RecompileInfos are stale and will be updated to the new
     // compiler outputs list later during the sweep. Don't worry about overflow
     // here, since stale indexes will persist only until the sweep finishes.
     generation++;
-
-    for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next())
-        comp->types.sweep(fop);
 }
 
 void
 TypeZone::endSweep(JSRuntime *rt)
 {
     js_delete(sweepCompilerOutputs);
     sweepCompilerOutputs = nullptr;
 
@@ -5374,17 +4075,17 @@ TypeScript::printTypes(JSContext *cx, Ha
     AutoEnterAnalysis enter(nullptr, script->zone());
 
     if (script->functionNonDelazifying())
         fprintf(stderr, "Function");
     else if (script->isForEval())
         fprintf(stderr, "Eval");
     else
         fprintf(stderr, "Main");
-    fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno());
+    fprintf(stderr, " %p %s:%d ", script.get(), script->filename(), (int) script->lineno());
 
     if (script->functionNonDelazifying()) {
         if (js::PropertyName *name = script->functionNonDelazifying()->name())
             name->dumpCharsNoNewline();
     }
 
     fprintf(stderr, "\n    this:");
     TypeScript::ThisTypes(script)->print();
@@ -5395,17 +4096,17 @@ TypeScript::printTypes(JSContext *cx, Ha
     {
         fprintf(stderr, "\n    arg%u:", i);
         TypeScript::ArgTypes(script, i)->print();
     }
     fprintf(stderr, "\n");
 
     for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
         {
-            fprintf(stderr, "#%u:", script->id());
+            fprintf(stderr, "%p:", script.get());
             Sprinter sprinter(cx);
             if (!sprinter.init())
                 return;
             js_Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
             fprintf(stderr, "%s", sprinter.string());
         }
 
         if (js_CodeSpec[*pc].format & JOF_TYPESET) {
@@ -5414,28 +4115,8 @@ TypeScript::printTypes(JSContext *cx, Ha
             types->print();
             fprintf(stderr, "\n");
         }
     }
 
     fprintf(stderr, "\n");
 }
 #endif /* DEBUG */
-
-void
-ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */)
-{
-    MOZ_ASSERT(!needsSweep());
-    MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
-
-    if (writeBarrier) {
-        // Manually trigger barriers if we are clearing a TypeNewScript. Other
-        // kinds of addendums are immutable.
-        if (newScript())
-            TypeNewScript::writeBarrierPre(newScript());
-        else
-            MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind);
-    }
-
-    flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK;
-    flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
-    addendum_ = addendum;
-}
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -18,110 +18,20 @@
 #include "ds/IdValuePair.h"
 #include "ds/LifoAlloc.h"
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "jit/IonTypes.h"
 #include "js/UbiNode.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
+#include "vm/ObjectGroup.h"
 
 namespace js {
 
-class TypeDescr;
-class UnboxedLayout;
-
-class TaggedProto
-{
-  public:
-    static JSObject * const LazyProto;
-
-    TaggedProto() : proto(nullptr) {}
-    explicit TaggedProto(JSObject *proto) : proto(proto) {}
-
-    uintptr_t toWord() const { return uintptr_t(proto); }
-
-    bool isLazy() const {
-        return proto == LazyProto;
-    }
-    bool isObject() const {
-        /* Skip nullptr and LazyProto. */
-        return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto);
-    }
-    JSObject *toObject() const {
-        MOZ_ASSERT(isObject());
-        return proto;
-    }
-    JSObject *toObjectOrNull() const {
-        MOZ_ASSERT(!proto || isObject());
-        return proto;
-    }
-    JSObject *raw() const { return proto; }
-
-    bool operator ==(const TaggedProto &other) { return proto == other.proto; }
-    bool operator !=(const TaggedProto &other) { return proto != other.proto; }
-
-  private:
-    JSObject *proto;
-};
-
-template <>
-struct RootKind<TaggedProto>
-{
-    static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
-};
-
-template <> struct GCMethods<const TaggedProto>
-{
-    static TaggedProto initial() { return TaggedProto(); }
-    static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
-};
-
-template <> struct GCMethods<TaggedProto>
-{
-    static TaggedProto initial() { return TaggedProto(); }
-    static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
-};
-
-template<class Outer>
-class TaggedProtoOperations
-{
-    const TaggedProto *value() const {
-        return static_cast<const Outer*>(this)->extract();
-    }
-
-  public:
-    uintptr_t toWord() const { return value()->toWord(); }
-    inline bool isLazy() const { return value()->isLazy(); }
-    inline bool isObject() const { return value()->isObject(); }
-    inline JSObject *toObject() const { return value()->toObject(); }
-    inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); }
-    JSObject *raw() const { return value()->raw(); }
-};
-
-template <>
-class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto> >
-{
-    friend class TaggedProtoOperations<Handle<TaggedProto> >;
-    const TaggedProto * extract() const {
-        return static_cast<const Handle<TaggedProto>*>(this)->address();
-    }
-};
-
-template <>
-class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto> >
-{
-    friend class TaggedProtoOperations<Rooted<TaggedProto> >;
-    const TaggedProto *extract() const {
-        return static_cast<const Rooted<TaggedProto> *>(this)->address();
-    }
-};
-
-class CallObject;
-
 namespace jit {
     struct IonScript;
     class JitAllocPolicy;
     class TempAllocator;
 }
 
 namespace types {
 
@@ -346,95 +256,16 @@ enum : uint32_t {
      * If the property is definite, mask and shift storing the slot + 1.
      * Otherwise these bits are clear.
      */
     TYPE_FLAG_DEFINITE_MASK       = 0xfffc0000,
     TYPE_FLAG_DEFINITE_SHIFT      = 18
 };
 typedef uint32_t TypeFlags;
 
-/* Flags and other state stored in ObjectGroup::flags */
-enum : uint32_t {
-    /* Whether this group is associated with some allocation site. */
-    OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,
-
-    /*
-     * If set, the object's prototype might be in the nursery and can't be
-     * used during Ion compilation (which may be occurring off thread).
-     */
-    OBJECT_FLAG_NURSERY_PROTO         = 0x2,
-
-    /* Mask/shift for the number of properties in propertySet */
-    OBJECT_FLAG_PROPERTY_COUNT_MASK   = 0xfff8,
-    OBJECT_FLAG_PROPERTY_COUNT_SHIFT  = 3,
-    OBJECT_FLAG_PROPERTY_COUNT_LIMIT  =
-        OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
-
-    /* Whether any objects this represents may have sparse indexes. */
-    OBJECT_FLAG_SPARSE_INDEXES        = 0x00010000,
-
-    /* Whether any objects this represents may not have packed dense elements. */
-    OBJECT_FLAG_NON_PACKED            = 0x00020000,
-
-    /*
-     * Whether any objects this represents may be arrays whose length does not
-     * fit in an int32.
-     */
-    OBJECT_FLAG_LENGTH_OVERFLOW       = 0x00040000,
-
-    /* Whether any objects have been iterated over. */
-    OBJECT_FLAG_ITERATED              = 0x00080000,
-
-    /* For a global object, whether flags were set on the RegExpStatics. */
-    OBJECT_FLAG_REGEXP_FLAGS_SET      = 0x00100000,
-
-    /*
-     * For the function on a run-once script, whether the function has actually
-     * run multiple times.
-     */
-    OBJECT_FLAG_RUNONCE_INVALIDATED   = 0x00200000,
-
-    /*
-     * For a global object, whether any array buffers in this compartment with
-     * typed object views have been neutered.
-     */
-    OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000,
-
-    /*
-     * Whether objects with this type should be allocated directly in the
-     * tenured heap.
-     */
-    OBJECT_FLAG_PRE_TENURE            = 0x00800000,
-
-    /* Whether objects with this type might have copy on write elements. */
-    OBJECT_FLAG_COPY_ON_WRITE         = 0x01000000,
-
-    /* Whether this type has had its 'new' script cleared in the past. */
-    OBJECT_FLAG_NEW_SCRIPT_CLEARED    = 0x02000000,
-
-    /*
-     * Whether all properties of this object are considered unknown.
-     * If set, all other flags in DYNAMIC_MASK will also be set.
-     */
-    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x04000000,
-
-    /* Flags which indicate dynamic properties of represented objects. */
-    OBJECT_FLAG_DYNAMIC_MASK          = 0x07ff0000,
-
-    // Mask/shift for the kind of addendum attached to this group.
-    OBJECT_FLAG_ADDENDUM_MASK         = 0x38000000,
-    OBJECT_FLAG_ADDENDUM_SHIFT        = 27,
-
-    // Mask/shift for this group's generation. If out of sync with the
-    // TypeZone's generation, this group hasn't been swept yet.
-    OBJECT_FLAG_GENERATION_MASK       = 0x40000000,
-    OBJECT_FLAG_GENERATION_SHIFT      = 30,
-};
-typedef uint32_t ObjectGroupFlags;
-
 class StackTypeSet;
 class HeapTypeSet;
 class TemporaryTypeSet;
 
 /*
  * Information about the set of types associated with an lvalue. There are
  * three kinds of type sets:
  *
@@ -642,18 +473,16 @@ class HeapTypeSet : public ConstraintTyp
 
     /* Mark this type set as representing a non-writable property. */
     inline void setNonWritableProperty(ExclusiveContext *cx);
 
     // Mark this type set as being non-constant.
     inline void setNonConstantProperty(ExclusiveContext *cx);
 };
 
-class CompilerConstraintList;
-
 CompilerConstraintList *
 NewCompilerConstraintList(jit::TempAllocator &alloc);
 
 class TemporaryTypeSet : public TypeSet
 {
   public:
     TemporaryTypeSet() {}
     TemporaryTypeSet(LifoAlloc *alloc, Type type);
@@ -768,40 +597,16 @@ class TemporaryTypeSet : public TypeSet
 
 bool
 AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, ObjectGroup *group, HandleId id);
 
 bool
 AddClearDefiniteFunctionUsesInScript(JSContext *cx, ObjectGroup *group,
                                      JSScript *script, JSScript *calleeScript);
 
-/* Is this a reasonable PC to be doing inlining on? */
-inline bool isInlinableCall(jsbytecode *pc);
-
-/* Type information about a property. */
-struct Property
-{
-    /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
-    HeapId id;
-
-    /* Possible types for this property, including types inherited from prototypes. */
-    HeapTypeSet types;
-
-    explicit Property(jsid id)
-      : id(id)
-    {}
-
-    Property(const Property &o)
-      : id(o.id.get()), types(o.types)
-    {}
-
-    static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); }
-    static jsid getKey(Property *p) { return p->id; }
-};
-
 // For types where only a small number of objects have been allocated, this
 // structure keeps track of all objects with the type in existence. Once
 // COUNT objects have been allocated, this structure is cleared and the objects
 // are analyzed, to perform the new script properties analyses or determine if
 // an unboxed representation can be used.
 class PreliminaryObjectArray
 {
   public:
@@ -959,430 +764,78 @@ class TypeNewScript
 
     bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group);
 
     static void make(JSContext *cx, ObjectGroup *group, JSFunction *fun);
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 };
 
-/*
- * 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.
- *
- * We can recover the type information for the object from examining it,
- * so don't normally track the possible types of its properties as it is
- * updated. Property type sets for the object are only constructed when an
- * analyzed script attaches constraints to it: the script is querying that
- * property off the object or another which delegates to it, and the analysis
- * information is sensitive to changes in the property's type. Future changes
- * to the property (whether those uncovered by analysis or those occurring
- * in the VM) will treat these properties like those of any other object group.
- */
-
-/* Type information about an object accessed by a script. */
-struct ObjectGroup : public gc::TenuredCell
-{
-  private:
-    /* Class shared by objects in this group. */
-    const Class *clasp_;
-
-    /* Prototype shared by objects in this group. */
-    HeapPtrObject proto_;
-
-    /*
-     * Whether there is a singleton JS object with this group. That JS object
-     * must appear in type sets instead of this; we include the back reference
-     * here to allow reverting the JS object to a lazy group.
-     */
-    HeapPtrObject singleton_;
-
-  public:
-
-    const Class *clasp() const {
-        return clasp_;
-    }
-
-    void setClasp(const Class *clasp) {
-        clasp_ = clasp;
-    }
-
-    TaggedProto proto() const {
-        return TaggedProto(proto_);
-    }
-
-    JSObject *singleton() const {
-        return singleton_;
-    }
-
-    // For use during marking, don't call otherwise.
-    HeapPtrObject &protoRaw() { return proto_; }
-    HeapPtrObject &singletonRaw() { return singleton_; }
-
-    void setProto(JSContext *cx, TaggedProto proto);
-    void setProtoUnchecked(TaggedProto proto) {
-        proto_ = proto.raw();
-    }
-
-    void initSingleton(JSObject *singleton) {
-        singleton_ = singleton;
-    }
-
-    /*
-     * Value held by singleton if this is a standin group for a singleton JS
-     * object whose group has not been constructed yet.
-     */
-    static const size_t LAZY_SINGLETON = 1;
-    bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; }
-
-  private:
-    /* Flags for this group. */
-    ObjectGroupFlags flags_;
-
-    // Kinds of addendums which can be attached to ObjectGroups.
-    enum AddendumKind {
-        Addendum_None,
-
-        // When used by interpreted function, the addendum stores the
-        // canonical JSFunction object.
-        Addendum_InterpretedFunction,
-
-        // When used by the 'new' group when constructing an interpreted
-        // function, the addendum stores a TypeNewScript.
-        Addendum_NewScript,
-
-        // When objects in this group have an unboxed representation, the
-        // addendum stores an UnboxedLayout (which might have a TypeNewScript
-        // as well, if the group is also constructed using 'new').
-        Addendum_UnboxedLayout,
-
-        // When used by typed objects, the addendum stores a TypeDescr.
-        Addendum_TypeDescr
-    };
-
-    // If non-null, holds additional information about this object, whose
-    // format is indicated by the object's addendum kind.
-    void *addendum_;
-
-    void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true);
-
-    AddendumKind addendumKind() const {
-        return (AddendumKind)
-            ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT);
-    }
-
-    TypeNewScript *newScriptDontCheckGeneration() const {
-        if (addendumKind() == Addendum_NewScript)
-            return reinterpret_cast<TypeNewScript *>(addendum_);
-        return nullptr;
-    }
-
-    UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const {
-        if (addendumKind() == Addendum_UnboxedLayout)
-            return reinterpret_cast<UnboxedLayout *>(addendum_);
-        return nullptr;
-    }
-
-    TypeNewScript *anyNewScript();
-    void detachNewScript(bool writeBarrier);
-
-  public:
-
-    ObjectGroupFlags flags() {
-        maybeSweep(nullptr);
-        return flags_;
-    }
-
-    void addFlags(ObjectGroupFlags flags) {
-        maybeSweep(nullptr);
-        flags_ |= flags;
-    }
-
-    void clearFlags(ObjectGroupFlags flags) {
-        maybeSweep(nullptr);
-        flags_ &= ~flags;
-    }
-
-    TypeNewScript *newScript() {
-        maybeSweep(nullptr);
-        return newScriptDontCheckGeneration();
-    }
-
-    void setNewScript(TypeNewScript *newScript) {
-        setAddendum(Addendum_NewScript, newScript);
-    }
-
-    UnboxedLayout *maybeUnboxedLayout() {
-        maybeSweep(nullptr);
-        return maybeUnboxedLayoutDontCheckGeneration();
-    }
-
-    UnboxedLayout &unboxedLayout() {
-        MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
-        return *maybeUnboxedLayout();
-    }
-
-    void setUnboxedLayout(UnboxedLayout *layout) {
-        setAddendum(Addendum_UnboxedLayout, layout);
-    }
-
-    TypeDescr *maybeTypeDescr() {
-        // Note: there is no need to sweep when accessing the type descriptor
-        // of an object, as it is strongly held and immutable.
-        if (addendumKind() == Addendum_TypeDescr)
-            return reinterpret_cast<TypeDescr *>(addendum_);
-        return nullptr;
-    }
-
-    TypeDescr &typeDescr() {
-        MOZ_ASSERT(addendumKind() == Addendum_TypeDescr);
-        return *maybeTypeDescr();
-    }
-
-    void setTypeDescr(TypeDescr *descr) {
-        setAddendum(Addendum_TypeDescr, descr);
-    }
-
-    JSFunction *maybeInterpretedFunction() {
-        // Note: as with type descriptors, there is no need to sweep when
-        // accessing the interpreted function associated with an object.
-        if (addendumKind() == Addendum_InterpretedFunction)
-            return reinterpret_cast<JSFunction *>(addendum_);
-        return nullptr;
-    }
-
-    void setInterpretedFunction(JSFunction *fun) {
-        setAddendum(Addendum_InterpretedFunction, fun);
-    }
-
-  private:
-    /*
-     * Properties of this object. This may contain JSID_VOID, representing the
-     * types of all integer indexes of the object, and/or JSID_EMPTY, holding
-     * constraints listening to changes to the object's state.
-     *
-     * The type sets in the properties of a group describe the possible values
-     * that can be read out of that property in actual JS objects. In native
-     * objects, property types account for plain data properties (those with a
-     * slot and no getter or setter hook) and dense elements. In typed objects
-     * and unboxed objects, property types account for object and value
-     * properties and elements in the object.
-     *
-     * For accesses on these properties, the correspondence is as follows:
-     *
-     * 1. If the group has unknownProperties(), the possible properties and
-     *    value types for associated JSObjects are unknown.
-     *
-     * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property
-     *    in |obj|, before obj->getProperty(id) the property in |group| for
-     *    |id| must reflect the result of the getProperty.
-     *
-     * There are several exceptions to this:
-     *
-     * 1. For properties of global JS objects which are undefined at the point
-     *    where the property was (lazily) generated, the property type set will
-     *    remain empty, and the 'undefined' type will only be added after a
-     *    subsequent assignment or deletion. After these properties have been
-     *    assigned a defined value, the only way they can become undefined
-     *    again is after such an assign or deletion.
-     *
-     * 2. Array lengths are special cased by the compiler and VM and are not
-     *    reflected in property types.
-     *
-     * 3. In typed objects (but not unboxed objects), the initial values of
-     *    properties (null pointers and undefined values) are not reflected in
-     *    the property types. These values are always possible when reading the
-     *    property.
-     *
-     * We establish these by using write barriers on calls to setProperty and
-     * defineProperty which are on native properties, and on any jitcode which
-     * might update the property with a new type.
-     */
-    Property **propertySet;
-  public:
-
-    inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags);
-
-    bool hasAnyFlags(ObjectGroupFlags flags) {
-        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
-        return !!(this->flags() & flags);
-    }
-    bool hasAllFlags(ObjectGroupFlags flags) {
-        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
-        return (this->flags() & flags) == flags;
-    }
-
-    bool unknownProperties() {
-        MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
-                      hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
-        return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
-    }
-
-    bool shouldPreTenure() {
-        return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
-    }
-
-    bool hasTenuredProto() {
-        return !(flags() & OBJECT_FLAG_NURSERY_PROTO);
-    }
-
-    gc::InitialHeap initialHeap(CompilerConstraintList *constraints);
-
-    bool canPreTenure() {
-        return !unknownProperties();
-    }
-
-    bool fromAllocationSite() {
-        return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE;
-    }
-
-    void setShouldPreTenure(ExclusiveContext *cx) {
-        MOZ_ASSERT(canPreTenure());
-        setFlags(cx, OBJECT_FLAG_PRE_TENURE);
-    }
-
-    /*
-     * Get or create a property of this object. Only call this for properties which
-     * a script accesses explicitly.
-     */
-    inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id);
-
-    /* Get a property only if it already exists. */
-    inline HeapTypeSet *maybeGetProperty(jsid id);
-
-    inline unsigned getPropertyCount();
-    inline Property *getProperty(unsigned i);
-
-    /* Helpers */
-
-    void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types);
-    bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape);
-    bool matchDefiniteProperties(HandleObject obj);
-    void markPropertyNonData(ExclusiveContext *cx, jsid id);
-    void markPropertyNonWritable(ExclusiveContext *cx, jsid id);
-    void markStateChange(ExclusiveContext *cx);
-    void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags);
-    void markUnknown(ExclusiveContext *cx);
-    void maybeClearNewScriptOnOOM();
-    void clearNewScript(ExclusiveContext *cx);
-    bool isPropertyNonData(jsid id);
-    bool isPropertyNonWritable(jsid id);
-
-    void print();
-
-    inline void clearProperties();
-    void maybeSweep(AutoClearTypeInferenceStateOnOOM *oom);
-
-  private:
-#ifdef DEBUG
-    bool needsSweep();
-#endif
-
-    uint32_t generation() {
-        return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT;
-    }
-
-  public:
-    void setGeneration(uint32_t generation) {
-        MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT));
-        flags_ &= ~OBJECT_FLAG_GENERATION_MASK;
-        flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
-    }
-
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
-
-    inline void finalize(FreeOp *fop);
-    void fixupAfterMovingGC() {}
-
-    static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; }
-
-    static inline uint32_t offsetOfClasp() {
-        return offsetof(ObjectGroup, clasp_);
-    }
-
-    static inline uint32_t offsetOfProto() {
-        return offsetof(ObjectGroup, proto_);
-    }
-
-    static inline uint32_t offsetOfAddendum() {
-        return offsetof(ObjectGroup, addendum_);
-    }
-
-    static inline uint32_t offsetOfFlags() {
-        return offsetof(ObjectGroup, flags_);
-    }
-
-  private:
-    inline uint32_t basePropertyCount();
-    inline void setBasePropertyCount(uint32_t count);
-
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto));
-    }
-};
+/* Is this a reasonable PC to be doing inlining on? */
+inline bool isInlinableCall(jsbytecode *pc);
 
 /*
- * Entries for the per-compartment set of groups which are the default
- * types to use for some prototype. An optional associated object is used which
- * allows multiple groups to be created with the same prototype. The
- * associated object may be a function (for types constructed with 'new') or a
- * type descriptor (for typed objects). These entries are also used for the set
- * of lazy groups in the compartment, which use a null associated object
- * (though there are only a few of these per compartment).
+ * Type information about a property.
+ *
+ * The type sets in the properties of a group describe the possible values
+ * that can be read out of that property in actual JS objects. In native
+ * objects, property types account for plain data properties (those with a
+ * slot and no getter or setter hook) and dense elements. In typed objects
+ * and unboxed objects, property types account for object and value
+ * properties and elements in the object.
+ *
+ * For accesses on these properties, the correspondence is as follows:
+ *
+ * 1. If the group has unknownProperties(), the possible properties and
+ *    value types for associated JSObjects are unknown.
+ *
+ * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property
+ *    in |obj|, before obj->getProperty(id) the property in |group| for
+ *    |id| must reflect the result of the getProperty.
+ *
+ * There are several exceptions to this:
+ *
+ * 1. For properties of global JS objects which are undefined at the point
+ *    where the property was (lazily) generated, the property type set will
+ *    remain empty, and the 'undefined' type will only be added after a
+ *    subsequent assignment or deletion. After these properties have been
+ *    assigned a defined value, the only way they can become undefined
+ *    again is after such an assign or deletion.
+ *
+ * 2. Array lengths are special cased by the compiler and VM and are not
+ *    reflected in property types.
+ *
+ * 3. In typed objects (but not unboxed objects), the initial values of
+ *    properties (null pointers and undefined values) are not reflected in
+ *    the property types. These values are always possible when reading the
+ *    property.
+ *
+ * We establish these by using write barriers on calls to setProperty and
+ * defineProperty which are on native properties, and on any jitcode which
+ * might update the property with a new type.
  */
-struct NewObjectGroupEntry
+struct Property
 {
-    ReadBarrieredObjectGroup group;
+    /* Identifier for this property, JSID_VOID for the aggregate integer index property. */
+    HeapId id;
 
-    // Note: This pointer is only used for equality and does not need a read barrier.
-    JSObject *associated;
+    /* Possible types for this property, including types inherited from prototypes. */
+    HeapTypeSet types;
 
-    NewObjectGroupEntry(ObjectGroup *group, JSObject *associated)
-      : group(group), associated(associated)
+    explicit Property(jsid id)
+      : id(id)
     {}
 
-    struct Lookup {
-        const Class *clasp;
-        TaggedProto hashProto;
-        TaggedProto matchProto;
-        JSObject *associated;
-
-        Lookup(const Class *clasp, TaggedProto proto, JSObject *associated)
-          : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated)
-        {}
+    Property(const Property &o)
+      : id(o.id.get()), types(o.types)
+    {}
 
-        /*
-         * For use by generational post barriers only.  Look up an entry whose
-         * proto has been moved, but was hashed with the original value.
-         */
-        Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject *associated)
-            : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated)
-        {}
-
-    };
-
-    static inline HashNumber hash(const Lookup &lookup);
-    static inline bool match(const NewObjectGroupEntry &key, const Lookup &lookup);
-    static void rekey(NewObjectGroupEntry &k, const NewObjectGroupEntry& newKey) { k = newKey; }
+    static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); }
+    static jsid getKey(Property *p) { return p->id; }
 };
-typedef HashSet<NewObjectGroupEntry, NewObjectGroupEntry, SystemAllocPolicy> NewObjectGroupTable;
-
-// Whether to make a singleton when calling 'new' at script/pc.
-bool
-UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc);
-
-// Whether to make a deep cloned singleton when cloning fun.
-bool
-UseSingletonForClone(JSFunction *fun);
 
 /*
  * Whether Array.prototype, or an object on its proto chain, has an
  * indexed property.
  */
 bool
 ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSScript *script);
 
@@ -1421,20 +874,16 @@ class TypeScript
 
     /* Get the type set for values observed at an opcode. */
     static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
 
     template <typename TYPESET>
     static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
                                          uint32_t *hint, TYPESET *typeArray);
 
-    /* Get a group for an allocation site in this script. */
-    static inline ObjectGroup *InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc,
-                                         JSProtoKey kind);
-
     /*
      * Monitor a bytecode pushing any value. This must be called for any opcode
      * which is JOF_TYPESET, and where either the script has not been analyzed
      * by type inference or where the pc has type barriers. For simplicity, we
      * always monitor JOF_TYPESET opcodes in the interpreter and stub calls,
      * and only look at barriers when generating JIT code for the script.
      */
     static inline void Monitor(JSContext *cx, JSScript *script, jsbytecode *pc,
@@ -1472,52 +921,30 @@ class TypeScript
 #ifdef DEBUG
     void printTypes(JSContext *cx, HandleScript script) const;
 #endif
 };
 
 void
 FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap);
 
-ArrayObject *
-GetOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc);
-
-ArrayObject *
-GetCopyOnWriteObject(JSScript *script, jsbytecode *pc);
-
 class RecompileInfo;
 
 // Allocate a CompilerOutput for a finished compilation and generate the type
 // constraints for the compilation. Returns whether the type constraints
 // still hold.
 bool
 FinishCompilation(JSContext *cx, HandleScript script, CompilerConstraintList *constraints,
                   RecompileInfo *precompileInfo);
 
 // Update the actual types in any scripts queried by constraints with any
 // speculative types added during the definite properties analysis.
 void
 FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints);
 
-struct ArrayTableKey;
-typedef HashMap<ArrayTableKey,
-                ReadBarrieredObjectGroup,
-                ArrayTableKey,
-                SystemAllocPolicy> ArrayTypeTable;
-
-struct ObjectTableKey;
-struct ObjectTableEntry;
-typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
-
-struct AllocationSiteKey;
-typedef HashMap<AllocationSiteKey,
-                ReadBarrieredObjectGroup,
-                AllocationSiteKey,
-                SystemAllocPolicy> AllocationSiteTable;
-
 class HeapTypeSetKey;
 
 // Type set entry for either a JSObject with singleton type or a non-singleton ObjectGroup.
 struct TypeSetObjectKey
 {
     static intptr_t keyBits(TypeSetObjectKey *obj) { return (intptr_t) obj; }
     static TypeSetObjectKey *getKey(TypeSetObjectKey *obj) { return obj; }
 
@@ -1678,65 +1105,16 @@ class RecompileInfo
 
     CompilerOutput *compilerOutput(TypeZone &types) const;
     CompilerOutput *compilerOutput(JSContext *cx) const;
     bool shouldSweep(TypeZone &types);
 };
 
 typedef Vector<RecompileInfo, 0, SystemAllocPolicy> RecompileInfoVector;
 
-/* Type information for a compartment. */
-struct TypeCompartment
-{
-    /* Number of scripts in this compartment. */
-    unsigned scriptCount;
-
-    /* Table for referencing types of objects keyed to an allocation site. */
-    AllocationSiteTable *allocationSiteTable;
-
-    /* Tables for determining types of singleton/JSON objects. */
-    ArrayTypeTable *arrayTypeTable;
-    ObjectTypeTable *objectTypeTable;
-
-  private:
-    void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type);
-
-  public:
-    void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj);
-    void fixObjectGroup(ExclusiveContext *cx, PlainObject *obj);
-    void fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj);
-
-    JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties);
-
-    TypeCompartment();
-    ~TypeCompartment();
-
-    inline JSCompartment *compartment();
-
-    // Prints results of this compartment if spew is enabled or force is set.
-    void print(JSContext *cx, bool force);
-
-    ObjectGroup *newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto,
-                                ObjectGroupFlags initialFlags = 0);
-
-    // Get or make a group for an allocation site, and add to the allocation site table.
-    ObjectGroup *addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key);
-
-    void clearTables();
-    void sweep(FreeOp *fop);
-    void finalizeObjects();
-
-    void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
-                                size_t *allocationSiteTables,
-                                size_t *arrayTypeTables,
-                                size_t *objectTypeTables);
-};
-
-void FixRestArgumentsType(ExclusiveContext *cxArg, ArrayObject *obj);
-
 struct AutoEnterAnalysis;
 
 struct TypeZone
 {
     JS::Zone *zone_;
 
     /* Pool for type information in this zone. */
     static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024;
@@ -1814,20 +1192,24 @@ inline void InferSpew(SpewChannel which,
 inline const char * TypeString(Type type) { return nullptr; }
 inline const char * ObjectGroupString(ObjectGroup *group) { return nullptr; }
 
 #endif
 
 /* Print a warning, dump state and abort the program. */
 MOZ_NORETURN MOZ_COLD void TypeFailure(JSContext *cx, const char *fmt, ...);
 
+// Prints type information for a context if spew is enabled or force is set.
+void
+PrintTypes(JSContext *cx, JSCompartment *comp, bool force);
+
 } /* namespace types */
 } /* namespace js */
 
 // JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances
 // with no associated compartment.
 namespace JS {
 namespace ubi {
-template<> struct Concrete<js::types::ObjectGroup> : TracerConcrete<js::types::ObjectGroup> { };
+template<> struct Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> { };
 }
 }
 
 #endif /* jsinfer_h */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -318,97 +318,16 @@ struct AutoEnterAnalysis
             zone->types.activeAnalysis = this;
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // Interface functions
 /////////////////////////////////////////////////////////////////////
 
-inline const Class *
-GetClassForProtoKey(JSProtoKey key)
-{
-    switch (key) {
-      case JSProto_Null:
-      case JSProto_Object:
-        return &PlainObject::class_;
-      case JSProto_Array:
-        return &ArrayObject::class_;
-
-      case JSProto_Number:
-        return &NumberObject::class_;
-      case JSProto_Boolean:
-        return &BooleanObject::class_;
-      case JSProto_String:
-        return &StringObject::class_;
-      case JSProto_Symbol:
-        return &SymbolObject::class_;
-      case JSProto_RegExp:
-        return &RegExpObject::class_;
-
-      case JSProto_Int8Array:
-      case JSProto_Uint8Array:
-      case JSProto_Int16Array:
-      case JSProto_Uint16Array:
-      case JSProto_Int32Array:
-      case JSProto_Uint32Array:
-      case JSProto_Float32Array:
-      case JSProto_Float64Array:
-      case JSProto_Uint8ClampedArray:
-        return &TypedArrayObject::classes[key - JSProto_Int8Array];
-
-      case JSProto_SharedInt8Array:
-      case JSProto_SharedUint8Array:
-      case JSProto_SharedInt16Array:
-      case JSProto_SharedUint16Array:
-      case JSProto_SharedInt32Array:
-      case JSProto_SharedUint32Array:
-      case JSProto_SharedFloat32Array:
-      case JSProto_SharedFloat64Array:
-      case JSProto_SharedUint8ClampedArray:
-        return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array];
-
-      case JSProto_ArrayBuffer:
-        return &ArrayBufferObject::class_;
-
-      case JSProto_SharedArrayBuffer:
-        return &SharedArrayBufferObject::class_;
-
-      case JSProto_DataView:
-        return &DataViewObject::class_;
-
-      default:
-        MOZ_CRASH("Bad proto key");
-    }
-}
-
-/*
- * Get the default 'new' group for a given standard class, per the currently
- * active global.
- */
-inline ObjectGroup *
-GetNewObjectGroup(JSContext *cx, JSProtoKey key)
-{
-    RootedObject proto(cx);
-    if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto))
-        return nullptr;
-    return cx->getNewGroup(GetClassForProtoKey(key), TaggedProto(proto.get()));
-}
-
-/* Get a group for the immediate allocation site within a native. */
-inline ObjectGroup *
-GetCallerInitGroup(JSContext *cx, JSProtoKey key)
-{
-    jsbytecode *pc;
-    RootedScript script(cx, cx->currentScript(&pc));
-    if (script)
-        return TypeScript::InitGroup(cx, script, pc, key);
-    return GetNewObjectGroup(cx, key);
-}
-
 void MarkIteratorUnknownSlow(JSContext *cx);
 
 void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args,
                          bool constructing);
 
 /*
  * Monitor a javascript call, either on entry to the interpreter or made
  * from within the interpreter.
@@ -564,33 +483,16 @@ IsTypePropertyIdMarkedNonWritable(JSObje
 /* Mark a state change on a particular object. */
 inline void
 MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj)
 {
     if (!obj->hasLazyGroup() && !obj->group()->unknownProperties())
         obj->group()->markStateChange(cx);
 }
 
-/*
- * For an array or object which has not yet escaped and been referenced elsewhere,
- * pick a new type based on the object's current contents.
- */
-
-inline void
-FixArrayGroup(ExclusiveContext *cx, ArrayObject *obj)
-{
-    cx->compartment()->types.fixArrayGroup(cx, obj);
-}
-
-inline void
-FixObjectGroup(ExclusiveContext *cx, PlainObject *obj)
-{
-    cx->compartment()->types.fixObjectGroup(cx, obj);
-}
-
 /* Interface helpers for JSScript*. */
 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               const js::Value &rval);
 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               js::types::Type type);
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
@@ -674,95 +576,16 @@ TypeScript::BytecodeTypes(JSScript *scri
     TypeScript *types = script->types();
     if (!types)
         return nullptr;
     uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
     return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
                          hint, types->typeArray());
 }
 
-struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
-    JSScript *script;
-
-    uint32_t offset : 24;
-    JSProtoKey kind : 8;
-
-    static const uint32_t OFFSET_LIMIT = (1 << 23);
-
-    AllocationSiteKey() { mozilla::PodZero(this); }
-
-    static inline uint32_t hash(AllocationSiteKey key) {
-        return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind);
-    }
-
-    static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
-        return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
-    }
-};
-
-/* Whether to use a singleton kind for an initializer opcode at script/pc. */
-js::NewObjectKind
-UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key);
-
-js::NewObjectKind
-UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp);
-
-/* static */ inline ObjectGroup *
-TypeScript::InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind)
-{
-    MOZ_ASSERT(!UseSingletonForInitializer(script, pc, kind));
-
-    uint32_t offset = script->pcToOffset(pc);
-
-    if (offset >= AllocationSiteKey::OFFSET_LIMIT)
-        return GetNewObjectGroup(cx, kind);
-
-    AllocationSiteKey key;
-    key.script = script;
-    key.offset = offset;
-    key.kind = kind;
-
-    if (!cx->compartment()->types.allocationSiteTable)
-        return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key);
-
-    AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key);
-
-    if (p)
-        return p->value();
-    return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key);
-}
-
-/* Set the group to use for obj according to the site it was allocated at. */
-static inline bool
-SetInitializerObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj,
-                          NewObjectKind kind)
-{
-    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
-    MOZ_ASSERT(key != JSProto_Null);
-    MOZ_ASSERT(kind == UseSingletonForInitializer(script, pc, key));
-
-    if (kind == SingletonObject) {
-        MOZ_ASSERT(obj->isSingleton());
-
-        /*
-         * Inference does not account for types of run-once initializer
-         * objects, as these may not be created until after the script
-         * has been analyzed.
-         */
-        TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
-    } else {
-        types::ObjectGroup *group = TypeScript::InitGroup(cx, script, pc, key);
-        if (!group)
-            return false;
-        obj->uninlinedSetGroup(group);
-    }
-
-    return true;
-}
-
 /* static */ inline void
 TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     TypeMonitorResult(cx, script, pc, rval);
 }
 
 /* static */ inline void
 TypeScript::Monitor(JSContext *cx, const js::Value &rval)
@@ -803,18 +626,18 @@ TypeScript::SetThis(JSContext *cx, JSScr
 {
     StackTypeSet *types = ThisTypes(script);
     if (!types)
         return;
 
     if (!types->hasType(type)) {
         AutoEnterAnalysis enter(cx);
 
-        InferSpew(ISpewOps, "externalType: setThis #%u: %s",
-                  script->id(), TypeString(type));
+        InferSpew(ISpewOps, "externalType: setThis %p: %s",
+                  script, TypeString(type));
         types->addType(cx, type);
     }
 }
 
 /* static */ inline void
 TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
 {
     SetThis(cx, script, GetValueType(value));
@@ -825,40 +648,30 @@ TypeScript::SetArgument(JSContext *cx, J
 {
     StackTypeSet *types = ArgTypes(script, arg);
     if (!types)
         return;
 
     if (!types->hasType(type)) {
         AutoEnterAnalysis enter(cx);
 
-        InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
-                  script->id(), arg, TypeString(type));
+        InferSpew(ISpewOps, "externalType: setArg %p %u: %s",
+                  script, arg, TypeString(type));
         types->addType(cx, type);
     }
 }
 
 /* static */ inline void
 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
 {
     Type type = GetValueType(value);
     SetArgument(cx, script, arg, type);
 }
 
 /////////////////////////////////////////////////////////////////////
-// TypeCompartment
-/////////////////////////////////////////////////////////////////////
-
-inline JSCompartment *
-TypeCompartment::compartment()
-{
-    return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
-}
-
-/////////////////////////////////////////////////////////////////////
 // TypeSet
 /////////////////////////////////////////////////////////////////////
 
 /*
  * The sets of objects and scripts in a type set grow monotonically, are usually
  * empty, almost always small, and sometimes big.  For empty or singleton sets,
  * the pointer refers directly to the value.  For sets fitting into SET_ARRAY_SIZE,
  * an array of this length is used to store the elements.  For larger sets, a hash
@@ -1184,76 +997,69 @@ TypeSet::getObjectClass(unsigned i) cons
     if (JSObject *object = getSingleton(i))
         return object->getClass();
     if (ObjectGroup *group = getGroup(i))
         return group->clasp();
     return nullptr;
 }
 
 /////////////////////////////////////////////////////////////////////
-// ObjectGroup
+// TypeNewScript
 /////////////////////////////////////////////////////////////////////
 
-inline
-ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags)
+inline void
+TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
 {
-    mozilla::PodZero(this);
-
-    /* Inner objects may not appear on prototype chains. */
-    MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
+    if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier())
+        return;
 
-    this->clasp_ = clasp;
-    this->proto_ = proto.raw();
-    this->flags_ = initialFlags;
-
-    setGeneration(zone()->types.generation);
-
-    InferSpew(ISpewOps, "newGroup: %s", ObjectGroupString(this));
+    JS::Zone *zone = newScript->function()->zoneFromAnyThread();
+    if (zone->needsIncrementalBarrier())
+        newScript->trace(zone->barrierTracer());
 }
 
-inline void
-ObjectGroup::finalize(FreeOp *fop)
-{
-    fop->delete_(newScriptDontCheckGeneration());
-    fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
-}
+} // namespace types
+
+/////////////////////////////////////////////////////////////////////
+// ObjectGroup
+/////////////////////////////////////////////////////////////////////
 
 inline uint32_t
 ObjectGroup::basePropertyCount()
 {
     return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT;
 }
 
 inline void
 ObjectGroup::setBasePropertyCount(uint32_t count)
 {
     // Note: Callers must ensure they are performing threadsafe operations.
     MOZ_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT);
     flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK)
            | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT);
 }
 
-inline HeapTypeSet *
+inline types::HeapTypeSet *
 ObjectGroup::getProperty(ExclusiveContext *cx, jsid id)
 {
     MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
-    MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
+    MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == types::IdToTypeId(id));
     MOZ_ASSERT(!unknownProperties());
 
-    if (HeapTypeSet *types = maybeGetProperty(id))
+    if (types::HeapTypeSet *types = maybeGetProperty(id))
         return types;
 
-    Property *base = cx->typeLifoAlloc().new_<Property>(id);
+    types::Property *base = cx->typeLifoAlloc().new_<types::Property>(id);
     if (!base) {
         markUnknown(cx);
         return nullptr;
     }
 
     uint32_t propertyCount = basePropertyCount();
-    Property **pprop = HashSetInsert<jsid,Property,Property>
+    types::Property **pprop = types::HashSetInsert<jsid,types::Property,types::Property>
         (cx->typeLifoAlloc(), propertySet, propertyCount, id);
     if (!pprop) {
         markUnknown(cx);
         return nullptr;
     }
 
     MOZ_ASSERT(!*pprop);
 
@@ -1267,77 +1073,49 @@ ObjectGroup::getProperty(ExclusiveContex
         // the object unknown so that new properties will not be added in the
         // future.
         markUnknown(cx);
     }
 
     return &base->types;
 }
 
-inline HeapTypeSet *
+inline types::HeapTypeSet *
 ObjectGroup::maybeGetProperty(jsid id)
 {
     MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
-    MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
+    MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == types::IdToTypeId(id));
     MOZ_ASSERT(!unknownProperties());
 
-    Property *prop = HashSetLookup<jsid,Property,Property>
+    types::Property *prop = types::HashSetLookup<jsid,types::Property,types::Property>
         (propertySet, basePropertyCount(), id);
 
     return prop ? &prop->types : nullptr;
 }
 
 inline unsigned
 ObjectGroup::getPropertyCount()
 {
     uint32_t count = basePropertyCount();
-    if (count > SET_ARRAY_SIZE)
-        return HashSetCapacity(count);
+    if (count > types::SET_ARRAY_SIZE)
+        return types::HashSetCapacity(count);
     return count;
 }
 
-inline Property *
+inline types::Property *
 ObjectGroup::getProperty(unsigned i)
 {
     MOZ_ASSERT(i < getPropertyCount());
     if (basePropertyCount() == 1) {
         MOZ_ASSERT(i == 0);
-        return (Property *) propertySet;
+        return (types::Property *) propertySet;
     }
     return propertySet[i];
 }
 
-inline void
-TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
-{
-    if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier())
-        return;
-
-    JS::Zone *zone = newScript->function()->zoneFromAnyThread();
-    if (zone->needsIncrementalBarrier())
-        newScript->trace(zone->barrierTracer());
-}
-
-} } /* namespace js::types */
-
-inline js::types::TypeScript *
-JSScript::types()
-{
-    maybeSweepTypes(nullptr);
-    return types_;
-}
-
-inline bool
-JSScript::ensureHasTypes(JSContext *cx)
-{
-    return types() || makeTypes(cx);
-}
-
-namespace js {
-
 template <>
 struct GCMethods<const types::Type>
 {
     static types::Type initial() { return types::Type::UnknownType(); }
     static bool poisoned(const types::Type &v) {
         return (v.isGroup() && IsPoisonedPtr(v.group()))
             || (v.isSingleton() && IsPoisonedPtr(v.singleton()));
     }
@@ -1350,9 +1128,22 @@ struct GCMethods<types::Type>
     static bool poisoned(const types::Type &v) {
         return (v.isGroup() && IsPoisonedPtr(v.group()))
             || (v.isSingleton() && IsPoisonedPtr(v.singleton()));
     }
 };
 
 } // namespace js
 
+inline js::types::TypeScript *
+JSScript::types()
+{
+    maybeSweepTypes(nullptr);
+    return types_;
+}
+
+inline bool
+JSScript::ensureHasTypes(JSContext *cx)
+{
+    return types() || makeTypes(cx);
+}
+
 #endif /* jsinferinlines_h */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -478,18 +478,18 @@ Compare(T *a, T *b, size_t c)
     }
     return true;
 }
 
 static inline PropertyIteratorObject *
 NewPropertyIteratorObject(JSContext *cx, unsigned flags)
 {
     if (flags & JSITER_ENUMERATE) {
-        RootedObjectGroup group(cx, cx->getNewGroup(&PropertyIteratorObject::class_,
-                                                    TaggedProto(nullptr)));
+        RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_,
+                                                                 TaggedProto(nullptr)));
         if (!group)
             return nullptr;
 
         JSObject *metadata = nullptr;
         if (!NewObjectMetadata(cx, &metadata))
             return nullptr;
 
         const Class *clasp = &PropertyIteratorObject::class_;
@@ -575,17 +575,17 @@ RegisterEnumerator(JSContext *cx, Proper
 static inline bool
 VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
                     uint32_t slength, uint32_t key, MutableHandleObject objp)
 {
     MOZ_ASSERT(!(flags & JSITER_FOREACH));
 
     if (obj->isSingleton() && !obj->setIteratedSingleton(cx))
         return false;
-    types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
+    types::MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
 
     Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
     if (!ni)
         return false;
@@ -618,17 +618,17 @@ VectorToKeyIterator(JSContext *cx, Handl
 static bool
 VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
                       MutableHandleObject objp)
 {
     MOZ_ASSERT(flags & JSITER_FOREACH);
 
     if (obj->isSingleton() && !obj->setIteratedSingleton(cx))
         return false;
-    types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
+    types::MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
 
     Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
     if (!ni)
         return false;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1054,29 +1054,16 @@ js::DefineProperties(JSContext *cx, Hand
     for (size_t i = 0, len = ids.length(); i < len; i++) {
         if (!DefinePropertyOnObject(cx, obj.as<NativeObject>(), ids[i], descs[i], true, &dummy))
             return false;
     }
 
     return true;
 }
 
-js::types::ObjectGroup*
-JSObject::uninlinedGetGroup(JSContext *cx)
-{
-    return getGroup(cx);
-}
-
-void
-JSObject::uninlinedSetGroup(js::types::ObjectGroup *group)
-{
-    setGroup(group);
-}
-
-
 /*** Seal and freeze *****************************************************************************/
 
 static unsigned
 GetSealedOrFrozenAttributes(unsigned attrs, IntegrityLevel level)
 {
     /* Make all attributes permanent; if freezing, make data attributes read-only. */
     if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
         return JSPROP_PERMANENT | JSPROP_READONLY;
@@ -1263,17 +1250,17 @@ NewObjectGCKind(const js::Class *clasp)
     if (clasp == &ArrayObject::class_)
         return gc::FINALIZE_OBJECT8;
     if (clasp == &JSFunction::class_)
         return gc::FINALIZE_OBJECT2;
     return gc::FINALIZE_OBJECT4;
 }
 
 static inline JSObject *
-NewObject(ExclusiveContext *cx, types::ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind,
+NewObject(ExclusiveContext *cx, ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind,
           NewObjectKind newKind)
 {
     const Class *clasp = groupArg->clasp();
 
     MOZ_ASSERT(clasp != &ArrayObject::class_);
     MOZ_ASSERT_IF(clasp == &JSFunction::class_,
                   kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
     MOZ_ASSERT_IF(parent, &parent->global() == cx->global());
@@ -1362,17 +1349,17 @@ js::NewObjectWithGivenProto(ExclusiveCon
                 gcNumber = rt->gc.gcNumber();
             }
         }
     }
 
     Rooted<TaggedProto> proto(cxArg, protoArg);
     RootedObject parent(cxArg, parentArg);
 
-    types::ObjectGroup *group = cxArg->getNewGroup(clasp, proto, nullptr);
+    ObjectGroup *group = ObjectGroup::defaultNewGroup(cxArg, clasp, proto, nullptr);
     if (!group)
         return nullptr;
 
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
     if (!parent && proto.isObject())
@@ -1544,17 +1531,17 @@ js::NewObjectWithClassProtoCommon(Exclus
 
     RootedObject parent(cxArg, parentArg);
     RootedObject proto(cxArg, protoArg);
 
     if (!FindProto(cxArg, clasp, &proto))
         return nullptr;
 
     Rooted<TaggedProto> taggedProto(cxArg, TaggedProto(proto));
-    types::ObjectGroup *group = cxArg->getNewGroup(clasp, taggedProto);
+    ObjectGroup *group = ObjectGroup::defaultNewGroup(cxArg, clasp, taggedProto);
     if (!group)
         return nullptr;
 
     JSObject *obj = NewObject(cxArg, group, parent, allocKind, newKind);
     if (!obj)
         return nullptr;
 
     if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots() &&
@@ -1613,26 +1600,26 @@ js::NewObjectWithGroupCommon(JSContext *
 }
 
 bool
 js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj)
 {
     jsbytecode *pc;
     RootedScript script(cx, cx->currentScript(&pc));
     gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
-    NewObjectKind newKind = script
-                            ? UseSingletonForInitializer(script, pc, &PlainObject::class_)
-                            : GenericObject;
+    NewObjectKind newKind = GenericObject;
+    if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_))
+        newKind = SingletonObject;
     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
     if (!obj)
         return false;
 
     if (script) {
         /* Try to specialize the group of the object to the scripted call site. */
-        if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind))
+        if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject))
             return false;
     }
 
     pobj.set(obj);
     return true;
 }
 
 JSObject*
@@ -1702,30 +1689,30 @@ CreateThisForFunctionWithGroup(JSContext
 
 JSObject *
 js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
                                    NewObjectKind newKind /* = GenericObject */)
 {
     RootedObject res(cx);
 
     if (proto) {
-        RootedObjectGroup group(cx, cx->getNewGroup(nullptr, TaggedProto(proto),
-                                                    &callee->as<JSFunction>()));
+        RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
+                                                                 &callee->as<JSFunction>()));
         if (!group)
             return nullptr;
 
         if (group->newScript() && !group->newScript()->analyzed()) {
             bool regenerate;
             if (!group->newScript()->maybeAnalyze(cx, group, &regenerate))
                 return nullptr;
             if (regenerate) {
                 // The script was analyzed successfully and may have changed
                 // the new type table, so refetch the group.
-                group = cx->getNewGroup(nullptr, TaggedProto(proto),
-                                        &callee->as<JSFunction>());
+                group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto),
+                                                     &callee->as<JSFunction>());
                 MOZ_ASSERT(group && group->newScript());
             }
         }
 
         res = CreateThisForFunctionWithGroup(cx, group, callee->getParent(), newKind);
     } else {
         gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
         res = NewObjectWithProto<PlainObject>(cx, proto, callee->getParent(), allocKind, newKind);
@@ -1975,19 +1962,19 @@ js::DeepCloneObjectLiteral(JSContext *cx
         }
         clone->setSlot(i, v);
     }
 
     if (obj->isSingleton()) {
         if (!JSObject::setSingleton(cx, clone))
             return nullptr;
     } else if (obj->is<ArrayObject>()) {
-        FixArrayGroup(cx, &clone->as<ArrayObject>());
+        ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>());
     } else {
-        FixObjectGroup(cx, &clone->as<PlainObject>());
+        ObjectGroup::fixPlainObjectGroup(cx, &clone->as<PlainObject>());
     }
 
     if (obj->is<ArrayObject>() && obj->denseElementsAreCopyOnWrite()) {
         if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
             return nullptr;
     }
 
     return clone;
@@ -2185,19 +2172,19 @@ js::XDRObjectLiteral(XDRState<mode> *xdr
     if (!xdr->codeUint32(&isSingletonTyped))
         return false;
 
     if (mode == XDR_DECODE) {
         if (isSingletonTyped) {
             if (!JSObject::setSingleton(cx, obj))
                 return false;
         } else if (isArray) {
-            FixArrayGroup(cx, &obj->as<ArrayObject>());
+            ObjectGroup::fixArrayGroup(cx, &obj->as<ArrayObject>());
         } else {
-            FixObjectGroup(cx, &obj->as<PlainObject>());
+            ObjectGroup::fixPlainObjectGroup(cx, &obj->as<PlainObject>());
         }
     }
 
     {
         uint32_t frozen;
         bool extensible;
         if (mode == XDR_ENCODE) {
             if (!IsExtensible(cx, obj, &extensible))
@@ -2238,17 +2225,18 @@ js::CloneObjectLiteral(JSContext *cx, Ha
 {
     if (srcObj->is<PlainObject>()) {
         AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as<PlainObject>().numFixedSlots()));
         MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind());
 
         JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx);
         if (!proto)
             return nullptr;
-        RootedObjectGroup group(cx, cx->getNewGroup(&PlainObject::class_, TaggedProto(proto)));
+        RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
+                                                                 TaggedProto(proto)));
         if (!group)
             return nullptr;
 
         RootedShape shape(cx, srcObj->lastProperty());
         return NewReshapedObject(cx, group, parent, kind, shape);
     }
 
     RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
@@ -2743,17 +2731,17 @@ js::SetClassAndProto(JSContext *cx, Hand
     }
 
     if (proto.isObject()) {
         RootedObject protoObj(cx, proto.toObject());
         if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
             return false;
     }
 
-    ObjectGroup *group = cx->getNewGroup(clasp, proto);
+    ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, clasp, proto);
     if (!group)
         return false;
 
     /*
      * Setting __proto__ on an object that has escaped and may be referenced by
      * other heap objects can only be done if the properties of both objects
      * are unknown. Type sets containing this object will contain the original
      * type but not the new type of the object, so we need to treat all such
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -113,17 +113,17 @@ class JSObject : public js::gc::Cell
     friend class js::NewObjectCache;
     friend class js::Nursery;
     friend class js::gc::RelocationOverlay;
     friend bool js::PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded);
     friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj,
                                           bool *succeeded);
 
     // Make a new group to use for a singleton object.
-    static js::types::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj);
+    static js::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj);
 
   public:
     js::Shape * lastProperty() const {
         MOZ_ASSERT(shape_);
         return shape_;
     }
 
     bool isNative() const {
@@ -138,22 +138,22 @@ class JSObject : public js::gc::Cell
     }
     bool hasClass(const js::Class *c) const {
         return getClass() == c;
     }
     const js::ObjectOps *getOps() const {
         return &getClass()->ops;
     }
 
-    js::types::ObjectGroup *group() const {
+    js::ObjectGroup *group() const {
         MOZ_ASSERT(!hasLazyGroup());
         return groupRaw();
     }
 
-    js::types::ObjectGroup *groupRaw() const {
+    js::ObjectGroup *groupRaw() const {
         return group_;
     }
 
     /*
      * Whether this is the only object which has its specified gbroup. This
      * object will have its group constructed lazily as needed by analysis.
      */
     bool isSingleton() const {
@@ -318,19 +318,17 @@ class JSObject : public js::gc::Cell
     bool hasIdempotentProtoChain() const;
 
     /*
      * Marks this object as having a singleton type, and leave the group lazy.
      * Constructs a new, unique shape for the object.
      */
     static inline bool setSingleton(js::ExclusiveContext *cx, js::HandleObject obj);
 
-    // uninlinedGetGroup() is the same as getGroup(), but not inlined.
-    inline js::types::ObjectGroup* getGroup(JSContext *cx);
-    js::types::ObjectGroup* uninlinedGetGroup(JSContext *cx);
+    inline js::ObjectGroup* getGroup(JSContext *cx);
 
     const js::HeapPtrObjectGroup &groupFromGC() const {
         /* Direct field access for use by GC. */
         return group_;
     }
 
     /*
      * We allow the prototype of an object to be lazily computed if the object
@@ -385,23 +383,17 @@ class JSObject : public js::gc::Cell
 
     // True iff this object's [[Prototype]] is immutable.  Must not be called
     // on proxies with lazy [[Prototype]]!
     bool nonLazyPrototypeIsImmutable() const {
         MOZ_ASSERT(!hasLazyPrototype());
         return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE);
     }
 
-    // uninlinedSetGroup() is the same as setGroup(), but not inlined.
-    inline void setGroup(js::types::ObjectGroup *group);
-    void uninlinedSetGroup(js::types::ObjectGroup *group);
-
-#ifdef DEBUG
-    bool hasNewGroup(const js::Class *clasp, js::types::ObjectGroup *group);
-#endif
+    inline void setGroup(js::ObjectGroup *group);
 
     /*
      * Mark an object that has been iterated over and is a singleton. We need
      * to recover this information in the object's type information after it
      * is purged on GC.
      */
     bool isIteratedSingleton() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::ITERATED_SINGLETON);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -70,40 +70,40 @@ JSObject::finalize(js::FreeOp *fop)
         shape_->listp = nullptr;
 }
 
 /* static */ inline bool
 JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj)
 {
     MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj));
 
-    js::types::ObjectGroup *group = cx->getLazySingletonGroup(obj->getClass(),
-                                                              obj->getTaggedProto());
+    js::ObjectGroup *group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
+                                                                 obj->getTaggedProto());
     if (!group)
         return false;
 
     obj->group_ = group;
     return true;
 }
 
-inline js::types::ObjectGroup*
+inline js::ObjectGroup*
 JSObject::getGroup(JSContext *cx)
 {
     MOZ_ASSERT(cx->compartment() == compartment());
     if (hasLazyGroup()) {
         JS::RootedObject self(cx, this);
         if (cx->compartment() != compartment())
             MOZ_CRASH();
         return makeLazyGroup(cx, self);
     }
     return group_;
 }
 
 inline void
-JSObject::setGroup(js::types::ObjectGroup *group)
+JSObject::setGroup(js::ObjectGroup *group)
 {
     MOZ_ASSERT(group);
     MOZ_ASSERT(!isSingleton());
     group_ = group;
 }
 
 
 /*** Standard internal methods *******************************************************************/
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -870,23 +870,16 @@ class JSScript : public js::gc::TenuredC
     uint32_t        sourceEnd_;
 
     uint32_t        warmUpCount; /* Number of times the script has been called
                                   * or has had backedges taken. When running in
                                   * ion, also increased for any inlined scripts.
                                   * Reset if the script's JIT code is forcibly
                                   * discarded. */
 
-#ifdef DEBUG
-    // Unique identifier within the compartment for this script, used for
-    // printing analysis information.
-    uint32_t        id_;
-    uint32_t        idpad;
-#endif
-
     // 16-bit fields.
 
     uint16_t        version;    /* JS version under which script was compiled */
 
     uint16_t        funLength_; /* ES6 function length */
 
     uint16_t        nTypeSets_; /* number of type sets used in this script for
                                    dynamic type monitoring */
@@ -1454,22 +1447,16 @@ class JSScript : public js::gc::TenuredC
     const char *filename() const { return scriptSource()->filename(); }
     const char *maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); }
 
   public:
 
     /* Return whether this script was compiled for 'eval' */
     bool isForEval() { return isCachedEval() || isActiveEval(); }
 
-#ifdef DEBUG
-    unsigned id();
-#else
-    unsigned id() { return 0; }
-#endif
-
     /* Ensure the script has a TypeScript. */
     inline bool ensureHasTypes(JSContext *cx);
 
     inline js::types::TypeScript *types();
 
     void maybeSweepTypes(js::types::AutoClearTypeInferenceStateOnOOM *oom);
 
     inline js::GlobalObject &global() const;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3780,17 +3780,17 @@ js::str_split(JSContext *cx, unsigned ar
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Steps 1-2. */
     RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
-    RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array));
+    RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
     if (!group)
         return false;
     AddTypePropertyId(cx, group, JSID_VOID, Type::StringType());
 
     /* Step 5: Use the second argument as the split limit, if given. */
     uint32_t limit;
     if (args.hasDefined(1)) {
         double d;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -248,16 +248,17 @@ UNIFIED_SOURCES += [
     'vm/GlobalObject.cpp',
     'vm/HelperThreads.cpp',
     'vm/Id.cpp',
     'vm/Interpreter.cpp',
     'vm/JSONParser.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/Monitor.cpp',
     'vm/NativeObject.cpp',
+    'vm/ObjectGroup.cpp',
     'vm/PIC.cpp',
     'vm/Probes.cpp',
     'vm/ProxyObject.cpp',
     'vm/RegExpObject.cpp',
     'vm/RegExpStatics.cpp',
     'vm/Runtime.cpp',
     'vm/SavedStacks.cpp',
     'vm/ScopeObject.cpp',
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -162,17 +162,17 @@ ArgumentsObject::create(JSContext *cx, H
 {
     RootedObject proto(cx, callee->global().getOrCreateObjectPrototype(cx));
     if (!proto)
         return nullptr;
 
     bool strict = callee->strict();
     const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_;
 
-    RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(proto.get())));
+    RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto.get())));
     if (!group)
         return nullptr;
 
     JSObject *metadata = nullptr;
     if (!NewObjectMetadata(cx, &metadata))
         return nullptr;
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -518,17 +518,17 @@ ArrayBufferObject::neuter(JSContext *cx,
     // accesses such views needs to be deoptimized so that neuter checks are
     // performed. This is done by setting a compartment wide flag indicating
     // that buffers with typed object views have been neutered.
     if (buffer->hasTypedObjectViews()) {
         // Make sure the global object's group has been instantiated, so the
         // flag change will be observed.
         if (!cx->global()->getGroup(cx))
             CrashAtUnhandlableOOM("ArrayBufferObject::neuter");
-        types::MarkObjectGroupFlags(cx, cx->global(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED);
+        types::MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED);
         cx->compartment()->neuteredTypedObjects = 1;
     }
 
     // Neuter all views on the buffer, clear out the list of views and the
     // buffer's data.
 
     if (InnerViewTable::ViewVector *views = cx->compartment()->innerViews.maybeViewsUnbarriered(buffer)) {
         for (size_t i = 0; i < views->length(); i++)
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -17,17 +17,17 @@ namespace js {
 
 inline void
 ArrayObject::setLength(ExclusiveContext *cx, uint32_t length)
 {
     MOZ_ASSERT(lengthIsWritable());
 
     if (length > INT32_MAX) {
         /* Track objects with overflowing lengths in type information. */
-        types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_LENGTH_OVERFLOW);
+        types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW);
     }
 
     getElementsHeader()->length = length;
 }
 
 /* static */ inline ArrayObject *
 ArrayObject::createArrayInternal(ExclusiveContext *cx, gc::AllocKind kind, gc::InitialHeap heap,
                                  HandleShape shape, HandleObjectGroup group)
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -891,17 +891,17 @@ GlobalHelperThreadState::finishParseTask
     // Point the prototypes of any objects in the script's compartment to refer
     // to the corresponding prototype in the new compartment. This will briefly
     // create cross compartment pointers, which will be fixed by the
     // MergeCompartments call below.
     for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_OBJECT_GROUP);
          !iter.done();
          iter.next())
     {
-        types::ObjectGroup *group = iter.get<types::ObjectGroup>();
+        ObjectGroup *group = iter.get<ObjectGroup>();
         TaggedProto proto(group->proto());
         if (!proto.isObject())
             continue;
 
         JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject());
         if (key == JSProto_Null)
             continue;
         MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -504,17 +504,17 @@ js::Invoke(JSContext *cx, CallArgs args,
     InvokeState state(cx, args, initial);
 
     // Check to see if createSingleton flag should be set for this frame.
     if (construct) {
         FrameIter iter(cx);
         if (!iter.done() && iter.hasScript()) {
             JSScript *script = iter.script();
             jsbytecode *pc = iter.pc();
-            if (UseSingletonForNewObject(cx, script, pc))
+            if (ObjectGroup::useSingletonForNewObject(cx, script, pc))
                 state.setCreateSingleton();
         }
     }
 
     bool ok = RunScript(cx, state);
 
     MOZ_ASSERT_IF(ok && construct, args.rval().isObject());
     return ok;
@@ -2559,17 +2559,17 @@ CASE(JSOP_FUNCALL)
         }
         Value *newsp = args.spAfterCall();
         TypeScript::Monitor(cx, script, REGS.pc, newsp[-1]);
         REGS.sp = newsp;
         ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH);
     }
 
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
-    bool createSingleton = UseSingletonForNewObject(cx, script, REGS.pc);
+    bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc);
 
     TypeMonitorCall(cx, args, construct);
 
     {
         InvokeState state(cx, args, initial);
         if (createSingleton)
             state.setCreateSingleton();
 
@@ -3081,49 +3081,59 @@ CASE(JSOP_HOLE)
 END_CASE(JSOP_HOLE)
 
 CASE(JSOP_NEWINIT)
 {
     uint8_t i = GET_UINT8(REGS.pc);
     MOZ_ASSERT(i == JSProto_Array || i == JSProto_Object);
 
     RootedObject &obj = rootObject0;
-    NewObjectKind newKind;
+    NewObjectKind newKind = GenericObject;
     if (i == JSProto_Array) {
-        newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_);
+        if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_))
+            newKind = SingletonObject;
         obj = NewDenseEmptyArray(cx, nullptr, newKind);
     } else {
         gc::AllocKind allocKind = GuessObjectGCKind(0);
-        newKind = UseSingletonForInitializer(script, REGS.pc, &PlainObject::class_);
+        if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &PlainObject::class_))
+            newKind = SingletonObject;
         obj = NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind);
     }
-    if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind))
+    if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj,
+                                                           newKind == SingletonObject))
+    {
         goto error;
+    }
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWINIT)
 
 CASE(JSOP_NEWARRAY)
 {
     unsigned count = GET_UINT24(REGS.pc);
     RootedObject &obj = rootObject0;
-    NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_);
+    NewObjectKind newKind = GenericObject;
+    if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_))
+        newKind = SingletonObject;
     obj = NewDenseFullyAllocatedArray(cx, count, nullptr, newKind);
-    if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind))
+    if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj,
+                                                           newKind == SingletonObject))
+    {
         goto error;
+    }
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWARRAY)
 
 CASE(JSOP_NEWARRAY_COPYONWRITE)
 {
     RootedObject &baseobj = rootObject0;
-    baseobj = types::GetOrFixupCopyOnWriteObject(cx, script, REGS.pc);
+    baseobj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc);
     if (!baseobj)
         goto error;
 
     RootedObject &obj = rootObject1;
     obj = NewDenseCopyOnWriteArray(cx, baseobj.as<ArrayObject>(), gc::DefaultHeap);
     if (!obj)
         goto error;
 
@@ -3132,20 +3142,25 @@ CASE(JSOP_NEWARRAY_COPYONWRITE)
 END_CASE(JSOP_NEWARRAY_COPYONWRITE)
 
 CASE(JSOP_NEWOBJECT)
 {
     RootedObject &baseobj = rootObject0;
     baseobj = script->getObject(REGS.pc);
 
     RootedObject &obj = rootObject1;
-    NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, baseobj->getClass());
+    NewObjectKind newKind = GenericObject;
+    if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, baseobj->getClass()))
+        newKind = SingletonObject;
     obj = CopyInitializerObject(cx, baseobj.as<PlainObject>(), newKind);
-    if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind))
+    if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj,
+                                                           newKind == SingletonObject))
+    {
         goto error;
+    }
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWOBJECT)
 
 CASE(JSOP_MUTATEPROTO)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
@@ -3960,17 +3975,17 @@ js::RunOnceScriptPrologue(JSContext *cx,
     }
 
     // Force instantiation of the script's function's group to ensure the flag
     // is preserved in type information.
     if (!script->functionNonDelazifying()->getGroup(cx))
         return false;
 
     types::MarkObjectGroupFlags(cx, script->functionNonDelazifying(),
-                                types::OBJECT_FLAG_RUNONCE_INVALIDATED);
+                                OBJECT_FLAG_RUNONCE_INVALIDATED);
     return true;
 }
 
 bool
 js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id,
                               HandleObject val)
 {
     MOZ_ASSERT(val->isCallable());
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -576,22 +576,22 @@ JSONParser<CharT>::advanceAfterProperty(
     error("expected ',' or '}' after property value in object");
     return token(Error);
 }
 
 JSObject *
 JSONParserBase::createFinishedObject(PropertyVector &properties)
 {
     /*
-     * Look for an existing cached type and shape for objects with this set of
+     * Look for an existing cached group and shape for objects with this set of
      * properties.
      */
     {
-        JSObject *obj = cx->compartment()->types.newTypedObject(cx, properties.begin(),
-                                                                properties.length());
+        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.
      */
@@ -606,21 +606,21 @@ JSONParserBase::createFinishedObject(Pro
     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 type to the object with type information for its
+     * 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.
      */
-    cx->compartment()->types.fixObjectGroup(cx, obj);
+    ObjectGroup::fixPlainObjectGroup(cx, obj);
 
     return obj;
 }
 
 inline bool
 JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector &properties)
 {
     MOZ_ASSERT(&properties == &stack.back().properties());
@@ -640,18 +640,18 @@ inline bool
 JSONParserBase::finishArray(MutableHandleValue vp, ElementVector &elements)
 {
     MOZ_ASSERT(&elements == &stack.back().elements());
 
     ArrayObject *obj = NewDenseCopiedArray(cx, elements.length(), elements.begin());
     if (!obj)
         return false;
 
-    /* Try to assign a new type to the array according to its elements. */
-    cx->compartment()->types.fixArrayGroup(cx, obj);
+    /* Try to assign a new group to the array according to its elements. */
+    ObjectGroup::fixArrayGroup(cx, obj);
 
     vp.setObject(*obj);
     if (!freeElements.append(&elements))
         return false;
     stack.popBack();
     return true;
 }
 
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -545,17 +545,17 @@ StatsCellCallback(JSRuntime *rt, void *d
         const BaseShape *base = shape->base();
         const Class *clasp = base->clasp();
         const char *className = clasp->name;
         AddClassInfo(granularity, cStats, className, info);
         break;
       }
 
       case JSTRACE_OBJECT_GROUP: {
-        types::ObjectGroup *group = static_cast<types::ObjectGroup *>(thing);
+        ObjectGroup *group = static_cast<ObjectGroup *>(thing);
         zStats->objectGroupsGCHeap += thingSize;
         zStats->objectGroupsMallocHeap += group->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
       default:
         MOZ_CRASH("invalid traceKind in StatsCellCallback");
     }
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -94,42 +94,42 @@ NativeObject::initDenseElementWithType(E
     MOZ_ASSERT(!shouldConvertDoubleElements());
     types::AddTypePropertyId(cx, this, JSID_VOID, val);
     initDenseElement(index, val);
 }
 
 inline void
 NativeObject::setDenseElementHole(ExclusiveContext *cx, uint32_t index)
 {
-    types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED);
+    types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED);
     setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE));
 }
 
 /* static */ inline void
 NativeObject::removeDenseElementForSparseIndex(ExclusiveContext *cx,
                                                HandleNativeObject obj, uint32_t index)
 {
     types::MarkObjectGroupFlags(cx, obj,
-                                types::OBJECT_FLAG_NON_PACKED |
-                                types::OBJECT_FLAG_SPARSE_INDEXES);
+                                OBJECT_FLAG_NON_PACKED |
+                                OBJECT_FLAG_SPARSE_INDEXES);
     if (obj->containsDenseElement(index))
         obj->setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE));
 }
 
 inline bool
 NativeObject::writeToIndexWouldMarkNotPacked(uint32_t index)
 {
     return getElementsHeader()->initializedLength < index;
 }
 
 inline void
 NativeObject::markDenseElementsNotPacked(ExclusiveContext *cx)
 {
     MOZ_ASSERT(isNative());
-    MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED);
+    types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED);
 }
 
 inline void
 NativeObject::ensureDenseInitializedLengthNoPackedCheck(ExclusiveContext *cx, uint32_t index,
                                                         uint32_t extra)
 {
     MOZ_ASSERT(!denseElementsAreCopyOnWrite());
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ObjectGroup.cpp
@@ -0,0 +1,1558 @@
+/* -*- 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/ObjectGroup.h"
+
+#include "jshashutil.h"
+#include "jsobj.h"
+
+#include "gc/StoreBuffer.h"
+#include "gc/Zone.h"
+#include "vm/ArrayObject.h"
+#include "vm/UnboxedObject.h"
+
+#include "jsgcinlines.h"
+#include "jsinferinlines.h"
+#include "jsobjinlines.h"
+
+using namespace js;
+using namespace js::types;
+
+using mozilla::PodZero;
+
+/////////////////////////////////////////////////////////////////////
+// ObjectGroup
+/////////////////////////////////////////////////////////////////////
+
+ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags)
+{
+    PodZero(this);
+
+    /* Inner objects may not appear on prototype chains. */
+    MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
+
+    this->clasp_ = clasp;
+    this->proto_ = proto.raw();
+    this->flags_ = initialFlags;
+
+    setGeneration(zone()->types.generation);
+}
+
+void
+ObjectGroup::finalize(FreeOp *fop)
+{
+    fop->delete_(newScriptDontCheckGeneration());
+    fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
+}
+
+void
+ObjectGroup::setProto(JSContext *cx, TaggedProto proto)
+{
+    MOZ_ASSERT(singleton());
+
+    if (proto.isObject() && IsInsideNursery(proto.toObject()))
+        addFlags(OBJECT_FLAG_NURSERY_PROTO);
+
+    setProtoUnchecked(proto);
+}
+
+size_t
+ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    size_t n = 0;
+    if (TypeNewScript *newScript = newScriptDontCheckGeneration())
+        n += newScript->sizeOfIncludingThis(mallocSizeOf);
+    if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration())
+        n += layout->sizeOfIncludingThis(mallocSizeOf);
+    return n;
+}
+
+void
+ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */)
+{
+    MOZ_ASSERT(!needsSweep());
+    MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
+
+    if (writeBarrier) {
+        // Manually trigger barriers if we are clearing a TypeNewScript. Other
+        // kinds of addendums are immutable.
+        if (newScript())
+            TypeNewScript::writeBarrierPre(newScript());
+        else
+            MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind);
+    }
+
+    flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK;
+    flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
+    addendum_ = addendum;
+}
+
+/* static */ bool
+ObjectGroup::useSingletonForClone(JSFunction *fun)
+{
+    if (!fun->isInterpreted())
+        return false;
+
+    if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite())
+        return true;
+
+    if (fun->isArrow())
+        return false;
+
+    if (fun->isSingleton())
+        return false;
+
+    /*
+     * When a function is being used as a wrapper for another function, it
+     * improves precision greatly to distinguish between different instances of
+     * the wrapper; otherwise we will conflate much of the information about
+     * the wrapped functions.
+     *
+     * An important example is the Class.create function at the core of the
+     * Prototype.js library, which looks like:
+     *
+     * var Class = {
+     *   create: function() {
+     *     return function() {
+     *       this.initialize.apply(this, arguments);
+     *     }
+     *   }
+     * };
+     *
+     * Each instance of the innermost function will have a different wrapped
+     * initialize method. We capture this, along with similar cases, by looking
+     * for short scripts which use both .apply and arguments. For such scripts,
+     * whenever creating a new instance of the function we both give that
+     * instance a singleton type and clone the underlying script.
+     */
+
+    uint32_t begin, end;
+    if (fun->hasScript()) {
+        if (!fun->nonLazyScript()->usesArgumentsApplyAndThis())
+            return false;
+        begin = fun->nonLazyScript()->sourceStart();
+        end = fun->nonLazyScript()->sourceEnd();
+    } else {
+        if (!fun->lazyScript()->usesArgumentsApplyAndThis())
+            return false;
+        begin = fun->lazyScript()->begin();
+        end = fun->lazyScript()->end();
+    }
+
+    return end - begin <= 100;
+}
+
+/* static */ bool
+ObjectGroup::useSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+    /*
+     * Make a heuristic guess at a use of JSOP_NEW that the constructed object
+     * should have a fresh group. We do this when the NEW is immediately
+     * followed by a simple assignment to an object's .prototype field.
+     * This is designed to catch common patterns for subclassing in JS:
+     *
+     * function Super() { ... }
+     * function Sub1() { ... }
+     * function Sub2() { ... }
+     *
+     * Sub1.prototype = new Super();
+     * Sub2.prototype = new Super();
+     *
+     * Using distinct groups for the particular prototypes of Sub1 and
+     * Sub2 lets us continue to distinguish the two subclasses and any extra
+     * properties added to those prototype objects.
+     */
+    if (script->isGenerator())
+        return false;
+    if (JSOp(*pc) != JSOP_NEW)
+        return false;
+    pc += JSOP_NEW_LENGTH;
+    if (JSOp(*pc) == JSOP_SETPROP) {
+        if (script->getName(pc) == cx->names().prototype)
+            return true;
+    }
+    return false;
+}
+
+/* static */ bool
+ObjectGroup::useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, JSProtoKey key)
+{
+    // The return value of this method can either be tested like a boolean or
+    // passed to a NewObject method.
+    JS_STATIC_ASSERT(GenericObject == 0);
+
+    /*
+     * Objects created outside loops in global and eval scripts should have
+     * singleton types. For now this is only done for plain objects and typed
+     * arrays, but not normal arrays.
+     */
+
+    if (script->functionNonDelazifying() && !script->treatAsRunOnce())
+        return GenericObject;
+
+    if (key != JSProto_Object &&
+        !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) &&
+        !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray))
+    {
+        return GenericObject;
+    }
+
+    /*
+     * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
+     * indicating their boundary.
+     */
+
+    if (!script->hasTrynotes())
+        return SingletonObject;
+
+    unsigned offset = script->pcToOffset(pc);
+
+    JSTryNote *tn = script->trynotes()->vector;
+    JSTryNote *tnlimit = tn + script->trynotes()->length;
+    for (; tn < tnlimit; tn++) {
+        if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP)
+            continue;
+
+        unsigned startOffset = script->mainOffset() + tn->start;
+        unsigned endOffset = startOffset + tn->length;
+
+        if (offset >= startOffset && offset < endOffset)
+            return GenericObject;
+    }
+
+    return SingletonObject;
+}
+
+/* static */ bool
+ObjectGroup::useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, const Class *clasp)
+{
+    return useSingletonForAllocationSite(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
+}
+
+/////////////////////////////////////////////////////////////////////
+// JSObject
+/////////////////////////////////////////////////////////////////////
+
+bool
+JSObject::shouldSplicePrototype(JSContext *cx)
+{
+    /*
+     * During bootstrapping, if inference is enabled we need to make sure not
+     * to splice a new prototype in for Function.prototype or the global
+     * object if their __proto__ had previously been set to null, as this
+     * will change the prototype for all other objects with the same type.
+     */
+    if (getProto() != nullptr)
+        return false;
+    return isSingleton();
+}
+
+bool
+JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto)
+{
+    MOZ_ASSERT(cx->compartment() == compartment());
+
+    RootedObject self(cx, this);
+
+    /*
+     * For singleton groups representing only a single JSObject, the proto
+     * can be rearranged as needed without destroying type information for
+     * the old or new types.
+     */
+    MOZ_ASSERT(self->isSingleton());
+
+    // Inner objects may not appear on prototype chains.
+    MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject);
+
+    if (proto.isObject() && !proto.toObject()->setDelegate(cx))
+        return false;
+
+    // Force type instantiation when splicing lazy group.
+    RootedObjectGroup group(cx, self->getGroup(cx));
+    if (!group)
+        return false;
+    RootedObjectGroup protoGroup(cx, nullptr);
+    if (proto.isObject()) {
+        protoGroup = proto.toObject()->getGroup(cx);
+        if (!protoGroup)
+            return false;
+    }
+
+    group->setClasp(clasp);
+    group->setProto(cx, proto);
+    return true;
+}
+
+/* static */ ObjectGroup *
+JSObject::makeLazyGroup(JSContext *cx, HandleObject obj)
+{
+    MOZ_ASSERT(obj->hasLazyGroup());
+    MOZ_ASSERT(cx->compartment() == obj->compartment());
+
+    /* De-lazification of functions can GC, so we need to do it up here. */
+    if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
+        RootedFunction fun(cx, &obj->as<JSFunction>());
+        if (!fun->getOrCreateScript(cx))
+            return nullptr;
+    }
+
+    // Find flags which need to be specified immediately on the object.
+    // Don't track whether singletons are packed.
+    ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED;
+
+    if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
+        initialFlags |= OBJECT_FLAG_ITERATED;
+
+    if (obj->isIndexed())
+        initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
+
+    if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
+        initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
+
+    Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
+    ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto,
+                                                           initialFlags);
+    if (!group)
+        return nullptr;
+
+    AutoEnterAnalysis enter(cx);
+
+    /* Fill in the type according to the state of this object. */
+
+    group->initSingleton(obj);
+
+    if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
+        group->setInterpretedFunction(&obj->as<JSFunction>());
+
+    obj->group_ = group;
+
+    return group;
+}
+
+/* static */ bool
+JSObject::setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj)
+{
+    ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj);
+    return obj->setFlag(cx, BaseShape::NEW_GROUP_UNKNOWN);
+}
+
+/////////////////////////////////////////////////////////////////////
+// ObjectGroupCompartment NewTable
+/////////////////////////////////////////////////////////////////////
+
+/*
+ * Entries for the per-compartment set of groups which are the default
+ * types to use for some prototype. An optional associated object is used which
+ * allows multiple groups to be created with the same prototype. The
+ * associated object may be a function (for types constructed with 'new') or a
+ * type descriptor (for typed objects). These entries are also used for the set
+ * of lazy groups in the compartment, which use a null associated object
+ * (though there are only a few of these per compartment).
+ */
+struct ObjectGroupCompartment::NewEntry
+{
+    ReadBarrieredObjectGroup group;
+
+    // Note: This pointer is only used for equality and does not need a read barrier.
+    JSObject *associated;
+
+    NewEntry(ObjectGroup *group, JSObject *associated)
+      : group(group), associated(associated)
+    {}
+
+    struct Lookup {
+        const Class *clasp;
+        TaggedProto hashProto;
+        TaggedProto matchProto;
+        JSObject *associated;
+
+        Lookup(const Class *clasp, TaggedProto proto, JSObject *associated)
+          : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated)
+        {}
+
+        /*
+         * For use by generational post barriers only.  Look up an entry whose
+         * proto has been moved, but was hashed with the original value.
+         */
+        Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject *associated)
+            : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated)
+        {}
+    };
+
+    static inline HashNumber hash(const Lookup &lookup) {
+        return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^
+               PointerHasher<const Class *, 3>::hash(lookup.clasp) ^
+               PointerHasher<JSObject *, 3>::hash(lookup.associated);
+    }
+
+    static inline bool match(const NewEntry &key, const Lookup &lookup) {
+        return key.group->proto() == lookup.matchProto &&
+               (!lookup.clasp || key.group->clasp() == lookup.clasp) &&
+               key.associated == lookup.associated;
+    }
+
+    static void rekey(NewEntry &k, const NewEntry& newKey) { k = newKey; }
+};
+
+// This class is used to add a post barrier on a NewTable entry, as the key is
+// calculated from a prototype object which may be moved by generational GC.
+class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef
+{
+    NewTable *table;
+    const Class *clasp;
+    JSObject *proto;
+    JSObject *associated;
+
+  public:
+    NewTableRef(NewTable *table, const Class *clasp, JSObject *proto, JSObject *associated)
+        : table(table), clasp(clasp), proto(proto), associated(associated)
+    {}
+
+    void mark(JSTracer *trc) {
+        JSObject *prior = proto;
+        trc->setTracingLocation(&*prior);
+        Mark(trc, &proto, "newObjectGroups set prototype");
+        if (prior == proto)
+            return;
+
+        NewTable::Ptr p = table->lookup(NewTable::Lookup(clasp, TaggedProto(prior),
+                                                         TaggedProto(proto),
+                                                         associated));
+        if (!p)
+            return;
+
+        table->rekeyAs(NewTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated),
+                       NewTable::Lookup(clasp, TaggedProto(proto), associated), *p);
+    }
+};
+
+/* static */ void
+ObjectGroupCompartment::newTablePostBarrier(ExclusiveContext *cx, NewTable *table,
+                                            const Class *clasp, TaggedProto proto,
+                                            JSObject *associated)
+{
+    MOZ_ASSERT_IF(associated, !IsInsideNursery(associated));
+
+    if (!proto.isObject())
+        return;
+
+    if (!cx->isJSContext()) {
+        MOZ_ASSERT(!IsInsideNursery(proto.toObject()));
+        return;
+    }
+
+    if (IsInsideNursery(proto.toObject())) {
+        gc::StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer;
+        sb.putGeneric(NewTableRef(table, clasp, proto.toObject(), associated));
+    }
+}
+
+/* static */ ObjectGroup *
+ObjectGroup::defaultNewGroup(ExclusiveContext *cx, const Class *clasp,
+                             TaggedProto proto, JSObject *associated)
+{
+    MOZ_ASSERT_IF(associated, proto.isObject());
+    MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>());
+    MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
+
+    // A null lookup clasp is used for 'new' groups with an associated
+    // function. The group starts out as a plain object but might mutate into an
+    // unboxed plain object.
+    MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>()));
+
+    ObjectGroupCompartment::NewTable *&table = cx->compartment()->objectGroups.defaultNewTable;
+
+    if (!table) {
+        table = cx->new_<ObjectGroupCompartment::NewTable>();
+        if (!table || !table->init()) {
+            js_delete(table);
+            table = nullptr;
+            return nullptr;
+        }
+    }
+
+    if (associated && associated->is<JSFunction>()) {
+        MOZ_ASSERT(!clasp);
+
+        // Canonicalize new functions to use the original one associated with its script.
+        JSFunction *fun = &associated->as<JSFunction>();
+        if (fun->hasScript())
+            associated = fun->nonLazyScript()->functionNonDelazifying();
+        else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
+            associated = fun->lazyScript()->functionNonDelazifying();
+        else
+            associated = nullptr;
+
+        // If we have previously cleared the 'new' script information for this
+        // function, don't try to construct another one.
+        if (associated && associated->wasNewScriptCleared())
+            associated = nullptr;
+
+        if (!associated)
+            clasp = &PlainObject::class_;
+    }
+
+    ObjectGroupCompartment::NewTable::AddPtr p =
+        table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated));
+    if (p) {
+        ObjectGroup *group = p->group;
+        MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
+        MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
+                              group->clasp() == &UnboxedPlainObject::class_);
+        MOZ_ASSERT(group->proto() == proto);
+        return group;
+    }
+
+    AutoEnterAnalysis enter(cx);
+
+    if (proto.isObject() && !proto.toObject()->setDelegate(cx))
+        return nullptr;
+
+    ObjectGroupFlags initialFlags = 0;
+    if (!proto.isObject() || proto.toObject()->isNewGroupUnknown())
+        initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
+
+    Rooted<TaggedProto> protoRoot(cx, proto);
+    ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
+                                                           protoRoot, initialFlags);
+    if (!group)
+        return nullptr;
+
+    if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated)))
+        return nullptr;
+
+    ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated);
+
+    if (proto.isObject()) {
+        RootedObject obj(cx, proto.toObject());
+
+        if (associated) {
+            if (associated->is<JSFunction>())
+                TypeNewScript::make(cx->asJSContext(), group, &associated->as<JSFunction>());
+            else
+                group->setTypeDescr(&associated->as<TypeDescr>());
+        }
+
+        /*
+         * Some builtin objects have slotful native properties baked in at
+         * creation via the Shape::{insert,get}initialShape mechanism. Since
+         * these properties are never explicitly defined on new objects, update
+         * the type information for them here.
+         */
+
+        const JSAtomState &names = cx->names();
+
+        if (obj->is<RegExpObject>()) {
+            AddTypePropertyId(cx, group, NameToId(names.source), Type::StringType());
+            AddTypePropertyId(cx, group, NameToId(names.global), Type::BooleanType());
+            AddTypePropertyId(cx, group, NameToId(names.ignoreCase), Type::BooleanType());
+            AddTypePropertyId(cx, group, NameToId(names.multiline), Type::BooleanType());
+            AddTypePropertyId(cx, group, NameToId(names.sticky), Type::BooleanType());
+            AddTypePropertyId(cx, group, NameToId(names.lastIndex), Type::Int32Type());
+        }
+
+        if (obj->is<StringObject>())
+            AddTypePropertyId(cx, group, NameToId(names.length), Type::Int32Type());
+
+        if (obj->is<ErrorObject>()) {
+            AddTypePropertyId(cx, group, NameToId(names.fileName), Type::StringType());
+            AddTypePropertyId(cx, group, NameToId(names.lineNumber), Type::Int32Type());
+            AddTypePropertyId(cx, group, NameToId(names.columnNumber), Type::Int32Type());
+            AddTypePropertyId(cx, group, NameToId(names.stack), Type::StringType());
+        }
+    }
+
+    return group;
+}
+
+/* static */ ObjectGroup *
+ObjectGroup::lazySingletonGroup(ExclusiveContext *cx, const Class *clasp, TaggedProto proto)
+{
+    MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
+
+    ObjectGroupCompartment::NewTable *&table = cx->compartment()->objectGroups.lazyTable;
+
+    if (!table) {
+        table = cx->new_<ObjectGroupCompartment::NewTable>();
+        if (!table || !table->init()) {
+            js_delete(table);
+            table = nullptr;
+            return nullptr;
+        }
+    }
+
+    ObjectGroupCompartment::NewTable::AddPtr p =
+        table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr));
+    if (p) {
+        ObjectGroup *group = p->group;
+        MOZ_ASSERT(group->lazy());
+
+        return group;
+    }
+
+    AutoEnterAnalysis enter(cx);
+
+    Rooted<TaggedProto> protoRoot(cx, proto);
+    ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot);
+    if (!group)
+        return nullptr;
+
+    if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr)))
+        return nullptr;
+
+    ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, nullptr);
+
+    group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON);
+    MOZ_ASSERT(group->singleton(), "created group must be a proper singleton");
+
+    return group;
+}
+
+/* static */ void
+ObjectGroup::setDefaultNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj)
+{
+    // If the object already has a new group, mark that group as unknown.
+    ObjectGroupCompartment::NewTable *table = cx->compartment()->objectGroups.defaultNewTable;
+    if (table) {
+        Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj));
+        ObjectGroupCompartment::NewTable::Ptr p =
+            table->lookup(ObjectGroupCompartment::NewEntry::Lookup(clasp, taggedProto, nullptr));
+        if (p)
+            MarkObjectGroupUnknownProperties(cx, p->group);
+    }
+}
+
+#ifdef DEBUG
+/* static */ bool
+ObjectGroup::hasDefaultNewGroup(JSObject *proto, const Class *clasp, ObjectGroup *group)
+{
+    ObjectGroupCompartment::NewTable *table = proto->compartment()->objectGroups.defaultNewTable;
+
+    if (table) {
+        ObjectGroupCompartment::NewTable::Ptr p =
+            table->lookup(ObjectGroupCompartment::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr));
+        return p && p->group == group;
+    }
+    return false;
+}
+#endif /* DEBUG */
+
+inline const Class *
+GetClassForProtoKey(JSProtoKey key)
+{
+    switch (key) {
+      case JSProto_Null:
+      case JSProto_Object:
+        return &PlainObject::class_;
+      case JSProto_Array:
+        return &ArrayObject::class_;
+
+      case JSProto_Number:
+        return &NumberObject::class_;
+      case JSProto_Boolean:
+        return &BooleanObject::class_;
+      case JSProto_String:
+        return &StringObject::class_;
+      case JSProto_Symbol:
+        return &SymbolObject::class_;
+      case JSProto_RegExp:
+        return &RegExpObject::class_;
+
+      case JSProto_Int8Array:
+      case JSProto_Uint8Array:
+      case JSProto_Int16Array:
+      case JSProto_Uint16Array:
+      case JSProto_Int32Array:
+      case JSProto_Uint32Array:
+      case JSProto_Float32Array:
+      case JSProto_Float64Array:
+      case JSProto_Uint8ClampedArray:
+        return &TypedArrayObject::classes[key - JSProto_Int8Array];
+
+      case JSProto_SharedInt8Array:
+      case JSProto_SharedUint8Array:
+      case JSProto_SharedInt16Array:
+      case JSProto_SharedUint16Array:
+      case JSProto_SharedInt32Array:
+      case JSProto_SharedUint32Array:
+      case JSProto_SharedFloat32Array:
+      case JSProto_SharedFloat64Array:
+      case JSProto_SharedUint8ClampedArray:
+        return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array];
+
+      case JSProto_ArrayBuffer:
+        return &ArrayBufferObject::class_;
+
+      case JSProto_SharedArrayBuffer:
+        return &SharedArrayBufferObject::class_;
+
+      case JSProto_DataView:
+        return &DataViewObject::class_;
+
+      default:
+        MOZ_CRASH("Bad proto key");
+    }
+}
+
+/* static */ ObjectGroup *
+ObjectGroup::defaultNewGroup(JSContext *cx, JSProtoKey key)
+{
+    RootedObject proto(cx);
+    if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto))
+        return nullptr;
+    return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto.get()));
+}
+
+/////////////////////////////////////////////////////////////////////
+// ObjectGroupCompartment ArrayObjectTable
+/////////////////////////////////////////////////////////////////////
+
+struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher<ArrayObjectKey>
+{
+    Type type;
+    JSObject *proto;
+
+    ArrayObjectKey()
+      : type(Type::UndefinedType()), proto(nullptr)
+    {}
+
+    ArrayObjectKey(Type type, JSObject *proto)
+      : type(type), proto(proto)
+    {}
+
+    static inline uint32_t hash(const ArrayObjectKey &v) {
+        return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2));
+    }
+
+    static inline bool match(const ArrayObjectKey &v1, const ArrayObjectKey &v2) {
+        return v1.type == v2.type && v1.proto == v2.proto;
+    }
+
+    bool operator==(const ArrayObjectKey& other) {
+        return type == other.type && proto == other.proto;
+    }
+
+    bool operator!=(const ArrayObjectKey& other) {
+        return !(*this == other);
+    }
+};
+
+static inline bool
+NumberTypes(Type a, Type b)
+{
+    return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
+        && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
+}
+
+/*
+ * As for GetValueType, but requires object types to be non-singletons with
+ * their default prototype. These are the only values that should appear in
+ * arrays and objects whose type can be fixed.
+ */
+static inline Type
+GetValueTypeForTable(const Value &v)
+{
+    Type type = GetValueType(v);
+    MOZ_ASSERT(!type.isSingleton());
+    return type;
+}
+
+/* static */ void
+ObjectGroup::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj)
+{
+    AutoEnterAnalysis enter(cx);
+
+    /*
+     * If the array is of homogenous type, pick a group which will be
+     * shared with all other singleton/JSON arrays of the same type.
+     * If the array is heterogenous, keep the existing group, which has
+     * unknown properties.
+     */
+
+    unsigned len = obj->getDenseInitializedLength();
+    if (len == 0)
+        return;
+
+    Type type = GetValueTypeForTable(obj->getDenseElement(0));
+
+    for (unsigned i = 1; i < len; i++) {
+        Type ntype = GetValueTypeForTable(obj->getDenseElement(i));
+        if (ntype != type) {
+            if (NumberTypes(type, ntype))
+                type = Type::DoubleType();
+            else
+                return;
+        }
+    }
+
+    setGroupToHomogenousArray(cx, obj, type);
+}
+
+/* static */ void
+ObjectGroup::fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj)
+{
+    AutoEnterAnalysis enter(cx);
+
+    // Tracking element types for rest argument arrays is not worth it, but we
+    // still want it to be known that it's a dense array.
+    setGroupToHomogenousArray(cx, obj, Type::UnknownType());
+}
+
+/* static */ void
+ObjectGroup::setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type elementType)
+{
+    MOZ_ASSERT(cx->zone()->types.activeAnalysis);
+
+    ObjectGroupCompartment::ArrayObjectTable *&table =
+        cx->compartment()->objectGroups.arrayObjectTable;
+
+    if (!table) {
+        table = cx->new_<ObjectGroupCompartment::ArrayObjectTable>();
+        if (!table || !table->init()) {
+            js_delete(table);
+            table = nullptr;
+            return;
+        }
+    }
+
+    ObjectGroupCompartment::ArrayObjectKey key(elementType, obj->getProto());
+    DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable> p(cx, *table, key);
+    if (p) {
+        obj->setGroup(p->value());
+    } else {
+        // Make a new group to use for future arrays with the same elements.
+        RootedObject objProto(cx, obj->getProto());
+        Rooted<TaggedProto> taggedProto(cx, TaggedProto(objProto));
+        ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto);
+        if (!group)
+            return;
+        obj->setGroup(group);
+
+        AddTypePropertyId(cx, group, JSID_VOID, elementType);
+
+        key.proto = objProto;
+        (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, 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.nfixed);
+    }
+
+    static inline bool match(const PlainObjectKey &v, const Lookup &lookup) {
+        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;
+    Type *types;
+};
+
+/* static */ void
+ObjectGroupCompartment::updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry,
+                                                    IdValuePair *properties, size_t nproperties)
+{
+    if (entry.group->unknownProperties())
+        return;
+    for (size_t i = 0; i < nproperties; i++) {
+        Type type = entry.types[i];
+        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] = Type::DoubleType();
+            }
+            AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype);
+        }
+    }
+}
+
+/* static */ void
+ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj)
+{
+    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;
+        }
+    }
+
+    /*
+     * 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;
+
+    Vector<IdValuePair> properties(cx);
+    if (!properties.resize(obj->slotSpan()))
+        return;
+
+    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();
+    }
+
+    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;
+    }
+
+    /* 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>(properties.length()));
+    if (!ids)
+        return;
+
+    ScopedJSFreePtr<Type> types(group->zone()->pod_calloc<Type>(properties.length()));
+    if (!types)
+        return;
+
+    for (size_t i = 0; i < properties.length(); i++) {
+        ids[i] = properties[i].id;
+        types[i] = GetValueTypeForTable(obj->getSlot(i));
+        AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]);
+    }
+
+    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;
+
+    obj->setGroup(group);
+
+    p = table->lookupForAdd(lookup);
+    if (table->add(p, key, entry)) {
+        ids.forget();
+        types.forget();
+    }
+}
+
+/* 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.
+     */
+
+    /*
+     * 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_);
+
+    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());
+
+    RootedShape shape(cx, p->value().shape);
+    if (!NativeObject::setLastProperty(cx, obj, shape)) {
+        cx->clearPendingException();
+        return nullptr;
+    }
+
+    ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), properties, nproperties);
+
+    for (size_t i = 0; i < nproperties; i++)
+        obj->setSlot(i, properties[i].value);
+
+    obj->setGroup(p->value().group);
+    return obj;
+}
+
+/////////////////////////////////////////////////////////////////////
+// ObjectGroupCompartment AllocationSiteTable
+/////////////////////////////////////////////////////////////////////
+
+struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
+    JSScript *script;
+
+    uint32_t offset : 24;
+    JSProtoKey kind : 8;
+
+    static const uint32_t OFFSET_LIMIT = (1 << 23);
+
+    AllocationSiteKey() { mozilla::PodZero(this); }
+
+    static inline uint32_t hash(AllocationSiteKey key) {
+        return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind);
+    }
+
+    static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
+        return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
+    }
+};
+
+/* static */ ObjectGroup *
+ObjectGroup::allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc,
+                                 JSProtoKey kind)
+{
+    MOZ_ASSERT(!useSingletonForAllocationSite(script, pc, kind));
+
+    uint32_t offset = script->pcToOffset(pc);
+
+    if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT)
+        return defaultNewGroup(cx, kind);
+
+    ObjectGroupCompartment::AllocationSiteKey key;
+    key.script = script;
+    key.offset = offset;
+    key.kind = kind;
+
+    ObjectGroupCompartment::AllocationSiteTable *&table =
+        cx->compartment()->objectGroups.allocationSiteTable;
+
+    if (!table) {
+        table = cx->new_<ObjectGroupCompartment::AllocationSiteTable>();
+        if (!table || !table->init()) {
+            js_delete(table);
+            table = nullptr;
+            return nullptr;
+        }
+    }
+
+    ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key);
+    if (p)
+        return p->value();
+
+    AutoEnterAnalysis enter(cx);
+
+    RootedObject proto(cx);
+    if (kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto))
+        return nullptr;
+
+    Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
+    ObjectGroup *res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged,
+                                                         OBJECT_FLAG_FROM_ALLOCATION_SITE);
+    if (!res)
+        return nullptr;
+
+    if (JSOp(*pc) == JSOP_NEWOBJECT) {
+        // This object is always constructed the same way and will not be
+        // observed by other code before all properties have been added. Mark
+        // all the properties as definite properties of the object.
+        JSObject *baseobj = script->getObject(GET_UINT32_INDEX(pc));
+        if (!res->addDefiniteProperties(cx, baseobj->lastProperty()))
+            return nullptr;
+    }
+
+    if (!table->add(p, key, res))
+        return nullptr;
+
+    return res;
+}
+
+/* static */ ObjectGroup *
+ObjectGroup::callingAllocationSiteGroup(JSContext *cx, JSProtoKey key)
+{
+    jsbytecode *pc;
+    RootedScript script(cx, cx->currentScript(&pc));
+    if (script)
+        return allocationSiteGroup(cx, script, pc, key);
+    return defaultNewGroup(cx, key);
+}
+
+/* static */ bool
+ObjectGroup::setAllocationSiteObjectGroup(JSContext *cx,
+                                          HandleScript script, jsbytecode *pc,
+                                          HandleObject obj, bool singleton)
+{
+    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
+    MOZ_ASSERT(key != JSProto_Null);
+    MOZ_ASSERT(singleton == useSingletonForAllocationSite(script, pc, key));
+
+    if (singleton) {
+        MOZ_ASSERT(obj->isSingleton());
+
+        /*
+         * Inference does not account for types of run-once initializer
+         * objects, as these may not be created until after the script
+         * has been analyzed.
+         */
+        TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
+    } else {
+        ObjectGroup *group = allocationSiteGroup(cx, script, pc, key);
+        if (!group)
+            return false;
+        obj->setGroup(group);
+    }
+
+    return true;
+}
+
+/* static */ ArrayObject *
+ObjectGroup::getOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc)
+{
+    // Make sure that the template object for script/pc has a type indicating
+    // that the object and its copies have copy on write elements.
+    RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>());
+    MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
+
+    if (obj->group()->fromAllocationSite()) {
+        MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
+        return obj;
+    }
+
+    RootedObjectGroup group(cx, allocationSiteGroup(cx, script, pc, JSProto_Array));
+    if (!group)
+        return nullptr;
+
+    group->addFlags(OBJECT_FLAG_COPY_ON_WRITE);
+
+    // Update type information in the initializer object group.
+    MOZ_ASSERT(obj->slotSpan() == 0);
+    for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
+        const Value &v = obj->getDenseElement(i);
+        AddTypePropertyId(cx, group, JSID_VOID, v);
+    }
+
+    obj->setGroup(group);
+    return obj;
+}
+
+/* static */ ArrayObject *
+ObjectGroup::getCopyOnWriteObject(JSScript *script, jsbytecode *pc)
+{
+    // getOrFixupCopyOnWriteObject should already have been called for
+    // script/pc, ensuring that the template object has a group with the
+    // COPY_ON_WRITE flag. We don't assert this here, due to a corner case
+    // where this property doesn't hold. See jsop_newarray_copyonwrite in
+    // IonBuilder.
+    ArrayObject *obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>();
+    MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
+
+    return obj;
+}
+
+/* static */ bool
+ObjectGroup::findAllocationSiteForType(JSContext *cx, Type type,
+                                       JSScript **script, uint32_t *offset)
+{
+    *script = nullptr;
+    *offset = 0;
+
+    if (type.isUnknown() || type.isAnyObject() || !type.isGroup())
+        return false;
+    ObjectGroup *obj = type.group();
+
+    const ObjectGroupCompartment::AllocationSiteTable *table =
+        cx->compartment()->objectGroups.allocationSiteTable;
+
+    if (!table)
+        return false;
+
+    for (ObjectGroupCompartment::AllocationSiteTable::Range r = table->all();
+         !r.empty();
+         r.popFront())
+    {
+        if (obj == r.front().value()) {
+            *script = r.front().key().script;
+            *offset = r.front().key().offset;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/////////////////////////////////////////////////////////////////////
+// ObjectGroupCompartment
+/////////////////////////////////////////////////////////////////////
+
+ObjectGroupCompartment::ObjectGroupCompartment()
+{
+    PodZero(this);
+}
+
+ObjectGroupCompartment::~ObjectGroupCompartment()
+{
+    js_delete(defaultNewTable);
+    js_delete(lazyTable);
+    js_delete(arrayObjectTable);
+    js_delete(plainObjectTable);
+    js_delete(allocationSiteTable);
+}
+
+void
+ObjectGroupCompartment::removeDefaultNewGroup(const Class *clasp, TaggedProto proto,
+                                              JSObject *associated)
+{
+    NewTable::Ptr p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated));
+    MOZ_ASSERT(p);
+
+    defaultNewTable->remove(p);
+}
+
+void
+ObjectGroupCompartment::replaceDefaultNewGroup(const Class *clasp, TaggedProto proto,
+                                               JSObject *associated, ObjectGroup *group)
+{
+    NewEntry::Lookup lookup(clasp, proto, associated);
+
+    NewTable::Ptr p = defaultNewTable->lookup(lookup);
+    MOZ_ASSERT(p);
+    defaultNewTable->remove(p);
+    defaultNewTable->putNew(lookup, NewEntry(group, associated));
+}
+
+/* static */
+ObjectGroup *
+ObjectGroupCompartment::makeGroup(ExclusiveContext *cx, const Class *clasp,
+                                  Handle<TaggedProto> proto,
+                                  ObjectGroupFlags initialFlags /* = 0 */)
+{
+    MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
+
+    if (cx->isJSContext()) {
+        if (proto.isObject() && IsInsideNursery(proto.toObject()))
+            initialFlags |= OBJECT_FLAG_NURSERY_PROTO;
+    }
+
+    ObjectGroup *group = NewObjectGroup(cx);
+    if (!group)
+        return nullptr;
+    new(group) ObjectGroup(clasp, proto, initialFlags);
+
+    return group;
+}
+
+void
+ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                               size_t *allocationSiteTables,
+                                               size_t *arrayObjectGroupTables,
+                                               size_t *plainObjectGroupTables,
+                                               size_t *compartmentTables)
+{
+    if (allocationSiteTable)
+        *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
+
+    if (arrayObjectTable)
+        *arrayObjectGroupTables += arrayObjectTable->sizeOfIncludingThis(mallocSizeOf);
+
+    if (plainObjectTable) {
+        *plainObjectGroupTables += plainObjectTable->sizeOfIncludingThis(mallocSizeOf);
+
+        for (PlainObjectTable::Enum e(*plainObjectTable);
+             !e.empty();
+             e.popFront())
+        {
+            const PlainObjectKey &key = e.front().key();
+            const PlainObjectEntry &value = e.front().value();
+
+            /* key.ids and values.types have the same length. */
+            *plainObjectGroupTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
+        }
+    }
+
+    if (defaultNewTable)
+        *compartmentTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf);
+
+    if (lazyTable)
+        *compartmentTables += lazyTable->sizeOfIncludingThis(mallocSizeOf);
+}
+
+void
+ObjectGroupCompartment::clearTables()
+{
+    if (allocationSiteTable && allocationSiteTable->initialized())
+        allocationSiteTable->clear();
+    if (arrayObjectTable && arrayObjectTable->initialized())
+        arrayObjectTable->clear();
+    if (plainObjectTable && plainObjectTable->initialized())
+        plainObjectTable->clear();
+    if (defaultNewTable && defaultNewTable->initialized())
+        defaultNewTable->clear();
+    if (lazyTable && lazyTable->initialized())
+        lazyTable->clear();
+}
+
+void
+ObjectGroupCompartment::sweep(FreeOp *fop)
+{
+    /*
+     * Iterate through the array/object group tables and remove all entries
+     * referencing collected data. These tables only hold weak references.
+     */
+
+    if (arrayObjectTable) {
+        for (ArrayObjectTable::Enum e(*arrayObjectTable); !e.empty(); e.popFront()) {
+            ArrayObjectKey key = e.front().key();
+            MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton());
+
+            bool remove = false;
+            if (!key.type.isUnknown() && key.type.isGroup()) {
+                ObjectGroup *group = key.type.groupNoBarrier();
+                if (IsObjectGroupAboutToBeFinalizedFromAnyThread(&group))
+                    remove = true;
+                else
+                    key.type = Type::ObjectType(group);
+            }
+            if (key.proto && key.proto != TaggedProto::LazyProto &&
+                IsObjectAboutToBeFinalizedFromAnyThread(&key.proto))
+            {
+                remove = true;
+            }
+            if (IsObjectGroupAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet()))
+                remove = true;
+
+            if (remove)
+                e.removeFront();
+            else if (key != e.front().key())
+                e.rekeyFront(key);
+        }
+    }
+
+    if (plainObjectTable) {
+        for (PlainObjectTable::Enum e(*plainObjectTable); !e.empty(); e.popFront()) {
+            const PlainObjectKey &key = e.front().key();
+            PlainObjectEntry &entry = e.front().value();
+
+            bool remove = false;
+            if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()))
+                remove = true;
+            if (IsShapeAboutToBeFinalizedFromAnyThread(entry.shape.unsafeGet()))
+                remove = true;
+            for (unsigned i = 0; !remove && i < key.nproperties; i++) {
+                if (JSID_IS_STRING(key.properties[i])) {
+                    JSString *str = JSID_TO_STRING(key.properties[i]);
+                    if (IsStringAboutToBeFinalizedFromAnyThread(&str))
+                        remove = true;
+                    MOZ_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]);
+                } else if (JSID_IS_SYMBOL(key.properties[i])) {
+                    JS::Symbol *sym = JSID_TO_SYMBOL(key.properties[i]);
+                    if (IsSymbolAboutToBeFinalizedFromAnyThread(&sym))
+                        remove = true;
+                }
+
+                MOZ_ASSERT(!entry.types[i].isSingleton());
+                ObjectGroup *group = nullptr;
+                if (entry.types[i].isGroup()) {
+                    group = entry.types[i].groupNoBarrier();
+                    if (IsObjectGroupAboutToBeFinalizedFromAnyThread(&group))
+                        remove = true;
+                    else if (group != entry.types[i].groupNoBarrier())
+                        entry.types[i] = Type::ObjectType(group);
+                }
+            }
+
+            if (remove) {
+                js_free(key.properties);
+                js_free(entry.types);
+                e.removeFront();
+            }
+        }
+    }
+
+    if (allocationSiteTable) {
+        for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
+            AllocationSiteKey key = e.front().key();
+            bool keyDying = IsScriptAboutToBeFinalizedFromAnyThread(&key.script);
+            bool valDying = IsObjectGroupAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet());
+            if (keyDying || valDying)
+                e.removeFront();
+            else if (key.script != e.front().key().script)
+                e.rekeyFront(key);
+        }
+    }
+
+    sweepNewTable(defaultNewTable);
+    sweepNewTable(lazyTable);
+}
+
+void
+ObjectGroupCompartment::sweepNewTable(NewTable *table)
+{
+    if (table && table->initialized()) {
+        for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
+            NewEntry entry = e.front();
+            if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) ||
+                (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated)))
+            {
+                e.removeFront();
+            } else {
+                /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */
+                MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet());
+                MOZ_ASSERT(entry.associated == e.front().associated);
+            }
+        }
+    }
+}
+
+void
+ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable *table)
+{
+    /*
+     * Each entry's hash depends on the object's prototype and we can't tell
+     * whether that has been moved or not in sweepNewObjectGroupTable().
+     */
+    if (table && table->initialized()) {
+        for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
+            NewEntry entry = e.front();
+            bool needRekey = false;
+            if (IsForwarded(entry.group.get())) {
+                entry.group.set(Forwarded(entry.group.get()));
+                needRekey = true;
+            }
+            TaggedProto proto = entry.group->proto();
+            if (proto.isObject() && IsForwarded(proto.toObject())) {
+                proto = TaggedProto(Forwarded(proto.toObject()));
+                needRekey = true;
+            }
+            if (entry.associated && IsForwarded(entry.associated)) {
+                entry.associated = Forwarded(entry.associated);
+                needRekey = true;
+            }
+            if (needRekey) {
+                const Class *clasp = entry.group->clasp();
+                if (entry.associated && entry.associated->is<JSFunction>())
+                    clasp = nullptr;
+                NewEntry::Lookup lookup(clasp, proto, entry.associated);
+                e.rekeyFront(lookup, entry);
+            }
+        }
+    }
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+void
+ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable *table)
+{
+    /*
+     * Assert that nothing points into the nursery or needs to be relocated, and
+     * that the hash table entries are discoverable.
+     */
+    if (!table || !table->initialized())
+        return;
+
+    for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
+        NewEntry entry = e.front();
+        CheckGCThingAfterMovingGC(entry.group.get());
+        TaggedProto proto = entry.group->proto();
+        if (proto.isObject())
+            CheckGCThingAfterMovingGC(proto.toObject());
+        CheckGCThingAfterMovingGC(entry.associated);
+
+        const Class *clasp = entry.group->clasp();
+        if (entry.associated && entry.associated->is<JSFunction>())
+            clasp = nullptr;
+
+        NewEntry::Lookup lookup(clasp, proto, entry.associated);
+        NewTable::Ptr ptr = table->lookup(lookup);
+        MOZ_ASSERT(ptr.found() && &*ptr == &e.front());
+    }
+}
+
+#endif // JSGC_HASH_TABLE_CHECKS
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ObjectGroup.h
@@ -0,0 +1,686 @@
+/* -*- 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/. */
+
+#ifndef vm_ObjectGroup_h
+#define vm_ObjectGroup_h
+
+#include "jsbytecode.h"
+#include "jsfriendapi.h"
+
+#include "ds/IdValuePair.h"
+#include "gc/Barrier.h"
+
+namespace js {
+
+class TypeDescr;
+class UnboxedLayout;
+
+namespace types {
+
+class Type;
+class TypeNewScript;
+class HeapTypeSet;
+struct Property;
+class AutoClearTypeInferenceStateOnOOM;
+class CompilerConstraintList;
+
+} // namespace types
+
+// Information about an object prototype, which can be either a particular
+// object, null, or a lazily generated object. The latter is only used by
+// certain kinds of proxies.
+class TaggedProto
+{
+  public:
+    static JSObject * const LazyProto;
+
+    TaggedProto() : proto(nullptr) {}
+    explicit TaggedProto(JSObject *proto) : proto(proto) {}
+
+    uintptr_t toWord() const { return uintptr_t(proto); }
+
+    bool isLazy() const {
+        return proto == LazyProto;
+    }
+    bool isObject() const {
+        /* Skip nullptr and LazyProto. */
+        return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto);
+    }
+    JSObject *toObject() const {
+        MOZ_ASSERT(isObject());
+        return proto;
+    }
+    JSObject *toObjectOrNull() const {
+        MOZ_ASSERT(!proto || isObject());
+        return proto;
+    }
+    JSObject *raw() const { return proto; }
+
+    bool operator ==(const TaggedProto &other) { return proto == other.proto; }
+    bool operator !=(const TaggedProto &other) { return proto != other.proto; }
+
+  private:
+    JSObject *proto;
+};
+
+template <>
+struct RootKind<TaggedProto>
+{
+    static ThingRootKind rootKind() { return THING_ROOT_OBJECT; }
+};
+
+template <> struct GCMethods<const TaggedProto>
+{
+    static TaggedProto initial() { return TaggedProto(); }
+    static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
+};
+
+template <> struct GCMethods<TaggedProto>
+{
+    static TaggedProto initial() { return TaggedProto(); }
+    static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); }
+};
+
+template<class Outer>
+class TaggedProtoOperations
+{
+    const TaggedProto *value() const {
+        return static_cast<const Outer*>(this)->extract();
+    }
+
+  public:
+    uintptr_t toWord() const { return value()->toWord(); }
+    inline bool isLazy() const { return value()->isLazy(); }
+    inline bool isObject() const { return value()->isObject(); }
+    inline JSObject *toObject() const { return value()->toObject(); }
+    inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); }
+    JSObject *raw() const { return value()->raw(); }
+};
+
+template <>
+class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto> >
+{
+    friend class TaggedProtoOperations<Handle<TaggedProto> >;
+    const TaggedProto * extract() const {
+        return static_cast<const Handle<TaggedProto>*>(this)->address();
+    }
+};
+
+template <>
+class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto> >
+{
+    friend class TaggedProtoOperations<Rooted<TaggedProto> >;
+    const TaggedProto *extract() const {
+        return static_cast<const Rooted<TaggedProto> *>(this)->address();
+    }
+};
+
+/* Flags and other state stored in ObjectGroupFlags */
+enum : uint32_t {
+    /* Whether this group is associated with some allocation site. */
+    OBJECT_FLAG_FROM_ALLOCATION_SITE  = 0x1,
+
+    /*
+     * If set, the object's prototype might be in the nursery and can't be
+     * used during Ion compilation (which may be occurring off thread).
+     */
+    OBJECT_FLAG_NURSERY_PROTO         = 0x2,
+
+    /* Mask/shift for the number of properties in propertySet */
+    OBJECT_FLAG_PROPERTY_COUNT_MASK   = 0xfff8,
+    OBJECT_FLAG_PROPERTY_COUNT_SHIFT  = 3,
+    OBJECT_FLAG_PROPERTY_COUNT_LIMIT  =
+        OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
+
+    /* Whether any objects this represents may have sparse indexes. */
+    OBJECT_FLAG_SPARSE_INDEXES        = 0x00010000,
+
+    /* Whether any objects this represents may not have packed dense elements. */
+    OBJECT_FLAG_NON_PACKED            = 0x00020000,
+
+    /*
+     * Whether any objects this represents may be arrays whose length does not
+     * fit in an int32.
+     */
+    OBJECT_FLAG_LENGTH_OVERFLOW       = 0x00040000,
+
+    /* Whether any objects have been iterated over. */
+    OBJECT_FLAG_ITERATED              = 0x00080000,
+
+    /* For a global object, whether flags were set on the RegExpStatics. */
+    OBJECT_FLAG_REGEXP_FLAGS_SET      = 0x00100000,
+
+    /*
+     * For the function on a run-once script, whether the function has actually
+     * run multiple times.
+     */
+    OBJECT_FLAG_RUNONCE_INVALIDATED   = 0x00200000,
+
+    /*
+     * For a global object, whether any array buffers in this compartment with
+     * typed object views have been neutered.
+     */
+    OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000,
+
+    /*
+     * Whether objects with this type should be allocated directly in the
+     * tenured heap.
+     */
+    OBJECT_FLAG_PRE_TENURE            = 0x00800000,
+
+    /* Whether objects with this type might have copy on write elements. */
+    OBJECT_FLAG_COPY_ON_WRITE         = 0x01000000,
+
+    /* Whether this type has had its 'new' script cleared in the past. */
+    OBJECT_FLAG_NEW_SCRIPT_CLEARED    = 0x02000000,
+
+    /*
+     * Whether all properties of this object are considered unknown.
+     * If set, all other flags in DYNAMIC_MASK will also be set.
+     */
+    OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x04000000,
+
+    /* Flags which indicate dynamic properties of represented objects. */
+    OBJECT_FLAG_DYNAMIC_MASK          = 0x07ff0000,
+
+    // Mask/shift for the kind of addendum attached to this group.
+    OBJECT_FLAG_ADDENDUM_MASK         = 0x38000000,
+    OBJECT_FLAG_ADDENDUM_SHIFT        = 27,
+
+    // Mask/shift for this group's generation. If out of sync with the
+    // TypeZone's generation, this group hasn't been swept yet.
+    OBJECT_FLAG_GENERATION_MASK       = 0x40000000,
+    OBJECT_FLAG_GENERATION_SHIFT      = 30,
+};
+typedef uint32_t ObjectGroupFlags;
+
+/*
+ * 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.
+ *
+ * We can recover the type information for the object from examining it,
+ * so don't normally track the possible types of its properties as it is
+ * updated. Property type sets for the object are only constructed when an
+ * analyzed script attaches constraints to it: the script is querying that
+ * property off the object or another which delegates to it, and the analysis
+ * information is sensitive to changes in the property's type. Future changes
+ * to the property (whether those uncovered by analysis or those occurring
+ * in the VM) will treat these properties like those of any other object group.
+ */
+
+/* Type information about an object accessed by a script. */
+class ObjectGroup : public gc::TenuredCell
+{
+    /* Class shared by objects in this group. */
+    const Class *clasp_;
+
+    /* Prototype shared by objects in this group. */
+    HeapPtrObject proto_;
+
+    /*
+     * Whether there is a singleton JS object with this group. That JS object
+     * must appear in type sets instead of this; we include the back reference
+     * here to allow reverting the JS object to a lazy group.
+     */
+    HeapPtrObject singleton_;
+
+  public:
+
+    const Class *clasp() const {
+        return clasp_;
+    }
+
+    void setClasp(const Class *clasp) {
+        clasp_ = clasp;
+    }
+
+    TaggedProto proto() const {
+        return TaggedProto(proto_);
+    }
+
+    JSObject *singleton() const {
+        return singleton_;
+    }
+
+    // For use during marking, don't call otherwise.
+    HeapPtrObject &protoRaw() { return proto_; }
+    HeapPtrObject &singletonRaw() { return singleton_; }
+
+    void setProto(JSContext *cx, TaggedProto proto);
+    void setProtoUnchecked(TaggedProto proto) {
+        proto_ = proto.raw();
+    }
+
+    void initSingleton(JSObject *singleton) {
+        singleton_ = singleton;
+    }
+
+    /*
+     * Value held by singleton if this is a standin group for a singleton JS
+     * object whose group has not been constructed yet.
+     */
+    static const size_t LAZY_SINGLETON = 1;
+    bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; }
+
+  private:
+    /* Flags for this group. */
+    ObjectGroupFlags flags_;
+
+    // Kinds of addendums which can be attached to ObjectGroups.
+    enum AddendumKind {
+        Addendum_None,
+
+        // When used by interpreted function, the addendum stores the
+        // canonical JSFunction object.
+        Addendum_InterpretedFunction,
+
+        // When used by the 'new' group when constructing an interpreted
+        // function, the addendum stores a TypeNewScript.
+        Addendum_NewScript,
+
+        // When objects in this group have an unboxed representation, the
+        // addendum stores an UnboxedLayout (which might have a TypeNewScript
+        // as well, if the group is also constructed using 'new').
+        Addendum_UnboxedLayout,
+
+        // When used by typed objects, the addendum stores a TypeDescr.
+        Addendum_TypeDescr
+    };
+
+    // If non-null, holds additional information about this object, whose
+    // format is indicated by the object's addendum kind.
+    void *addendum_;
+
+    void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true);
+
+    AddendumKind addendumKind() const {
+        return (AddendumKind)
+            ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT);
+    }
+
+    types::TypeNewScript *newScriptDontCheckGeneration() const {
+        if (addendumKind() == Addendum_NewScript)
+            return reinterpret_cast<types::TypeNewScript *>(addendum_);
+        return nullptr;
+    }
+
+    UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const {
+        if (addendumKind() == Addendum_UnboxedLayout)
+            return reinterpret_cast<UnboxedLayout *>(addendum_);
+        return nullptr;
+    }
+
+    types::TypeNewScript *anyNewScript();
+    void detachNewScript(bool writeBarrier);
+
+  public:
+
+    ObjectGroupFlags flags() {
+        maybeSweep(nullptr);
+        return flags_;
+    }
+
+    void addFlags(ObjectGroupFlags flags) {
+        maybeSweep(nullptr);
+        flags_ |= flags;
+    }
+
+    void clearFlags(ObjectGroupFlags flags) {
+        maybeSweep(nullptr);
+        flags_ &= ~flags;
+    }
+
+    types::TypeNewScript *newScript() {
+        maybeSweep(nullptr);
+        return newScriptDontCheckGeneration();
+    }
+
+    void setNewScript(types::TypeNewScript *newScript) {
+        setAddendum(Addendum_NewScript, newScript);
+    }
+
+    UnboxedLayout *maybeUnboxedLayout() {
+        maybeSweep(nullptr);
+        return maybeUnboxedLayoutDontCheckGeneration();
+    }
+
+    UnboxedLayout &unboxedLayout() {
+        MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
+        return *maybeUnboxedLayout();
+    }
+
+    void setUnboxedLayout(UnboxedLayout *layout) {
+        setAddendum(Addendum_UnboxedLayout, layout);
+    }
+
+    TypeDescr *maybeTypeDescr() {
+        // Note: there is no need to sweep when accessing the type descriptor
+        // of an object, as it is strongly held and immutable.
+        if (addendumKind() == Addendum_TypeDescr)
+            return reinterpret_cast<TypeDescr *>(addendum_);
+        return nullptr;
+    }
+
+    TypeDescr &typeDescr() {
+        MOZ_ASSERT(addendumKind() == Addendum_TypeDescr);
+        return *maybeTypeDescr();
+    }
+
+    void setTypeDescr(TypeDescr *descr) {
+        setAddendum(Addendum_TypeDescr, descr);
+    }
+
+    JSFunction *maybeInterpretedFunction() {
+        // Note: as with type descriptors, there is no need to sweep when
+        // accessing the interpreted function associated with an object.
+        if (addendumKind() == Addendum_InterpretedFunction)
+            return reinterpret_cast<JSFunction *>(addendum_);
+        return nullptr;
+    }
+
+    void setInterpretedFunction(JSFunction *fun) {
+        setAddendum(Addendum_InterpretedFunction, fun);
+    }
+
+  private:
+    // Properties of this object. This may contain JSID_VOID, representing the
+    // types of all integer indexes of the object, and/or JSID_EMPTY, holding
+    // constraints listening to changes to the object's state.
+    //
+    // See types::Property for more detail about the types represented here.
+    types::Property **propertySet;
+  public:
+
+    inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags);
+
+    inline bool hasAnyFlags(ObjectGroupFlags flags) {
+        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
+        return !!(this->flags() & flags);
+    }
+    bool hasAllFlags(ObjectGroupFlags flags) {
+        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
+        return (this->flags() & flags) == flags;
+    }
+
+    bool unknownProperties() {
+        MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
+                      hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
+        return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
+    }
+
+    bool shouldPreTenure() {
+        return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
+    }
+
+    bool hasTenuredProto() {
+        return !(flags() & OBJECT_FLAG_NURSERY_PROTO);
+    }
+
+    gc::InitialHeap initialHeap(types::CompilerConstraintList *constraints);
+
+    bool canPreTenure() {
+        return !unknownProperties();
+    }
+
+    bool fromAllocationSite() {
+        return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE;
+    }
+
+    void setShouldPreTenure(ExclusiveContext *cx) {
+        MOZ_ASSERT(canPreTenure());
+        setFlags(cx, OBJECT_FLAG_PRE_TENURE);
+    }
+
+    /*
+     * Get or create a property of this object. Only call this for properties which
+     * a script accesses explicitly.
+     */
+    inline types::HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id);
+
+    /* Get a property only if it already exists. */
+    inline types::HeapTypeSet *maybeGetProperty(jsid id);
+
+    inline unsigned getPropertyCount();
+    inline types::Property *getProperty(unsigned i);
+
+    /* Helpers */
+
+    void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, types::HeapTypeSet *types);
+    bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape);
+    bool matchDefiniteProperties(HandleObject obj);
+    void markPropertyNonData(ExclusiveContext *cx, jsid id);
+    void markPropertyNonWritable(ExclusiveContext *cx, jsid id);
+    void markStateChange(ExclusiveContext *cx);
+    void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags);
+    void markUnknown(ExclusiveContext *cx);
+    void maybeClearNewScriptOnOOM();
+    void clearNewScript(ExclusiveContext *cx);
+    bool isPropertyNonData(jsid id);
+    bool isPropertyNonWritable(jsid id);
+
+    void print();
+
+    inline void clearProperties();
+    void maybeSweep(types::AutoClearTypeInferenceStateOnOOM *oom);
+
+  private:
+#ifdef DEBUG
+    bool needsSweep();
+#endif
+
+    uint32_t generation() {
+        return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT;
+    }
+
+  public:
+    void setGeneration(uint32_t generation) {
+        MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT));
+        flags_ &= ~OBJECT_FLAG_GENERATION_MASK;
+        flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT;
+    }
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+    void finalize(FreeOp *fop);
+    void fixupAfterMovingGC() {}
+
+    static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; }
+
+    static inline uint32_t offsetOfClasp() {
+        return offsetof(ObjectGroup, clasp_);
+    }
+
+    static inline uint32_t offsetOfProto() {
+        return offsetof(ObjectGroup, proto_);
+    }
+
+    static inline uint32_t offsetOfAddendum() {
+        return offsetof(ObjectGroup, addendum_);
+    }
+
+    static inline uint32_t offsetOfFlags() {
+        return offsetof(ObjectGroup, flags_);
+    }
+
+  private:
+    inline uint32_t basePropertyCount();
+    inline void setBasePropertyCount(uint32_t count);
+
+    static void staticAsserts() {
+        JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto));
+    }
+
+  public:
+    // Whether to make a deep cloned singleton when cloning fun.
+    static bool useSingletonForClone(JSFunction *fun);
+
+    // Whether to make a singleton when calling 'new' at script/pc.
+    static bool useSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc);
+
+    // Whether to make a singleton object at an allocation site.
+    static bool useSingletonForAllocationSite(JSScript *script, jsbytecode *pc,
+                                              JSProtoKey key);
+    static bool useSingletonForAllocationSite(JSScript *script, jsbytecode *pc,
+                                              const Class *clasp);
+
+    // Static accessors for ObjectGroupCompartment NewTable.
+
+    static ObjectGroup *defaultNewGroup(ExclusiveContext *cx, const Class *clasp,
+                                        TaggedProto proto,
+                                        JSObject *associated = nullptr);
+    static ObjectGroup *lazySingletonGroup(ExclusiveContext *cx, const Class *clasp,
+                                           TaggedProto proto);
+
+    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 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);
+
+    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);
+
+    // Get a non-singleton group to use for objects created in a JSNative call.
+    static ObjectGroup *callingAllocationSiteGroup(JSContext *cx, JSProtoKey key);
+
+    // Set the group or singleton-ness of an object created for an allocation site.
+    static bool
+    setAllocationSiteObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc,
+                                 HandleObject obj, bool singleton);
+
+    static ArrayObject *getOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script,
+                                                    jsbytecode *pc);
+    static ArrayObject *getCopyOnWriteObject(JSScript *script, jsbytecode *pc);
+
+    // Returns false if not found.
+    static bool findAllocationSiteForType(JSContext *cx, types::Type type,
+                                          JSScript **script, uint32_t *offset);
+
+  private:
+    static ObjectGroup *defaultNewGroup(JSContext *cx, JSProtoKey key);
+    static void setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj, types::Type type);
+};
+
+// Structure used to manage the groups in a compartment.
+class ObjectGroupCompartment
+{
+    friend class ObjectGroup;
+
+    struct NewEntry;
+    typedef HashSet<NewEntry, NewEntry, SystemAllocPolicy> NewTable;
+    class NewTableRef;
+
+    // Set of default 'new' or lazy groups in the compartment.
+    NewTable *defaultNewTable;
+    NewTable *lazyTable;
+
+    struct ArrayObjectKey;
+    typedef HashMap<ArrayObjectKey,
+                    ReadBarrieredObjectGroup,
+                    ArrayObjectKey,
+                    SystemAllocPolicy> ArrayObjectTable;
+
+    struct PlainObjectKey;
+    struct PlainObjectEntry;
+    typedef HashMap<PlainObjectKey,
+                    PlainObjectEntry,
+                    PlainObjectKey,
+                    SystemAllocPolicy> PlainObjectTable;
+
+    // Tables for managing groups common to the contents of large script
+    // singleton objects and JSON objects. These are vanilla ArrayObjects and
+    // PlainObjects, so we distinguish the groups of different ones by looking
+    // at the types of their properties.
+    //
+    // All singleton/JSON arrays which have the same prototype, are homogenous
+    // and of the same element type will share a group. All singleton/JSON
+    // objects which have the same shape and property types will also share a
+    // group. We don't try to collate arrays or objects with type mismatches.
+    ArrayObjectTable *arrayObjectTable;
+    PlainObjectTable *plainObjectTable;
+
+    struct AllocationSiteKey;
+    typedef HashMap<AllocationSiteKey,
+                    ReadBarrieredObjectGroup,
+                    AllocationSiteKey,
+                    SystemAllocPolicy> AllocationSiteTable;
+
+    // Table for referencing types of objects keyed to an allocation site.
+    AllocationSiteTable *allocationSiteTable;
+
+  public:
+    ObjectGroupCompartment();
+    ~ObjectGroupCompartment();
+
+    void removeDefaultNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated);
+    void replaceDefaultNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated,
+                                ObjectGroup *group);
+
+    static ObjectGroup *makeGroup(ExclusiveContext *cx, const Class *clasp,
+                                  Handle<TaggedProto> proto,
+                                  ObjectGroupFlags initialFlags = 0);
+
+    void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
+                                size_t *allocationSiteTables,
+                                size_t *arrayGroupTables,
+                                size_t *plainObjectGroupTables,
+                                size_t *compartmentTables);
+
+    void clearTables();
+
+    void sweep(FreeOp *fop);
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+    void checkTablesAfterMovingGC() {
+        checkNewTableAfterMovingGC(defaultNewTable);
+        checkNewTableAfterMovingGC(lazyTable);
+    }
+#endif
+
+    void fixupTablesAfterMovingGC() {
+        fixupNewTableAfterMovingGC(defaultNewTable);
+        fixupNewTableAfterMovingGC(lazyTable);
+    }
+