summaryrefslogtreecommitdiffstats
path: root/js/src/vm/ObjectGroup.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/ObjectGroup.h')
-rw-r--r--js/src/vm/ObjectGroup.h655
1 files changed, 655 insertions, 0 deletions
diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h
new file mode 100644
index 000000000..4e24de9f1
--- /dev/null
+++ b/js/src/vm/ObjectGroup.h
@@ -0,0 +1,655 @@
+/* -*- 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"
+#include "js/CharacterEncoding.h"
+#include "js/GCHashTable.h"
+#include "vm/TaggedProto.h"
+#include "vm/TypeInference.h"
+
+namespace js {
+
+class TypeDescr;
+class UnboxedLayout;
+
+class PreliminaryObjectArrayWithTemplate;
+class TypeNewScript;
+class HeapTypeSet;
+class AutoClearTypeInferenceStateOnOOM;
+class CompilerConstraintList;
+
+namespace gc {
+void MergeCompartments(JSCompartment* source, JSCompartment* target);
+} // namespace gc
+
+/*
+ * The NewObjectKind allows an allocation site to specify the type properties
+ * and lifetime requirements that must be fixed at allocation time.
+ */
+enum NewObjectKind {
+ /* This is the default. Most objects are generic. */
+ GenericObject,
+
+ /*
+ * Singleton objects are treated specially by the type system. This flag
+ * ensures that the new object is automatically set up correctly as a
+ * singleton and is allocated in the tenured heap.
+ */
+ SingletonObject,
+
+ /*
+ * CrossCompartmentWrappers use the common Proxy class, but are allowed
+ * to have nursery lifetime.
+ */
+ NurseryAllocatedProxy,
+
+ /*
+ * Objects which will not benefit from being allocated in the nursery
+ * (e.g. because they are known to have a long lifetime) may be allocated
+ * with this kind to place them immediately into the tenured generation.
+ */
+ TenuredObject
+};
+
+/*
+ * Lazy object groups overview.
+ *
+ * Object groups which represent at most one JS object are constructed lazily.
+ * These include groups for native functions, standard classes, scripted
+ * functions defined at the top level of global/eval scripts, objects which
+ * dynamically become the prototype of some other object, 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
+{
+ friend void gc::MergeCompartments(JSCompartment* source, JSCompartment* target);
+
+ /* Class shared by objects in this group. */
+ const Class* clasp_;
+
+ /* Prototype shared by objects in this group. */
+ GCPtr<TaggedProto> proto_;
+
+ /* Compartment shared by objects in this group. */
+ JSCompartment* compartment_;
+
+ public:
+
+ const Class* clasp() const {
+ return clasp_;
+ }
+
+ void setClasp(const Class* clasp) {
+ MOZ_ASSERT(JS::StringIsASCII(clasp->name));
+ clasp_ = clasp;
+ }
+
+ bool hasDynamicPrototype() const {
+ return proto_.isDynamic();
+ }
+
+ const GCPtr<TaggedProto>& proto() const {
+ return proto_;
+ }
+
+ GCPtr<TaggedProto>& proto() {
+ return proto_;
+ }
+
+ void setProto(TaggedProto proto);
+ void setProtoUnchecked(TaggedProto proto);
+
+ bool singleton() const {
+ return flagsDontCheckGeneration() & OBJECT_FLAG_SINGLETON;
+ }
+
+ bool lazy() const {
+ bool res = flagsDontCheckGeneration() & OBJECT_FLAG_LAZY_SINGLETON;
+ MOZ_ASSERT_IF(res, singleton());
+ return res;
+ }
+
+ JSCompartment* compartment() const { return compartment_; }
+ JSCompartment* maybeCompartment() const { return compartment(); }
+
+ 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,
+
+ // For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate.
+ Addendum_PreliminaryObjects,
+
+ // 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,
+
+ // If this group is used by objects that have been converted from an
+ // unboxed representation and/or have the same allocation kind as such
+ // objects, the addendum points to that unboxed group.
+ Addendum_OriginalUnboxedGroup,
+
+ // 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;
+ }
+
+ TypeNewScript* anyNewScript();
+ void detachNewScript(bool writeBarrier, ObjectGroup* replacement);
+
+ ObjectGroupFlags flagsDontCheckGeneration() const {
+ return flags_;
+ }
+
+ public:
+
+ inline ObjectGroupFlags flags();
+ inline void addFlags(ObjectGroupFlags flags);
+ inline void clearFlags(ObjectGroupFlags flags);
+ inline TypeNewScript* newScript();
+
+ void setNewScript(TypeNewScript* newScript) {
+ setAddendum(Addendum_NewScript, newScript);
+ }
+
+ inline PreliminaryObjectArrayWithTemplate* maybePreliminaryObjects();
+
+ PreliminaryObjectArrayWithTemplate* maybePreliminaryObjectsDontCheckGeneration() {
+ if (addendumKind() == Addendum_PreliminaryObjects)
+ return reinterpret_cast<PreliminaryObjectArrayWithTemplate*>(addendum_);
+ return nullptr;
+ }
+
+ void setPreliminaryObjects(PreliminaryObjectArrayWithTemplate* preliminaryObjects) {
+ setAddendum(Addendum_PreliminaryObjects, preliminaryObjects);
+ }
+
+ void detachPreliminaryObjects() {
+ MOZ_ASSERT(maybePreliminaryObjectsDontCheckGeneration());
+ setAddendum(Addendum_None, nullptr);
+ }
+
+ bool hasUnanalyzedPreliminaryObjects() {
+ return (newScriptDontCheckGeneration() && !newScriptDontCheckGeneration()->analyzed()) ||
+ maybePreliminaryObjectsDontCheckGeneration();
+ }
+
+ inline UnboxedLayout* maybeUnboxedLayout();
+ inline UnboxedLayout& unboxedLayout();
+
+ UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const {
+ if (addendumKind() == Addendum_UnboxedLayout)
+ return reinterpret_cast<UnboxedLayout*>(addendum_);
+ return nullptr;
+ }
+
+ UnboxedLayout& unboxedLayoutDontCheckGeneration() const {
+ MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
+ return *maybeUnboxedLayoutDontCheckGeneration();
+ }
+
+ void setUnboxedLayout(UnboxedLayout* layout) {
+ setAddendum(Addendum_UnboxedLayout, layout);
+ }
+
+ ObjectGroup* maybeOriginalUnboxedGroup() const {
+ if (addendumKind() == Addendum_OriginalUnboxedGroup)
+ return reinterpret_cast<ObjectGroup*>(addendum_);
+ return nullptr;
+ }
+
+ void setOriginalUnboxedGroup(ObjectGroup* group) {
+ setAddendum(Addendum_OriginalUnboxedGroup, group);
+ }
+
+ 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);
+ }
+
+ class Property
+ {
+ public:
+ // Identifier for this property, JSID_VOID for the aggregate integer
+ // index property, or JSID_EMPTY for properties holding constraints
+ // listening to changes in the group's state.
+ GCPtrId id;
+
+ // Possible own types for this property.
+ 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; }
+ };
+
+ private:
+ /*
+ * Properties of this object.
+ *
+ * 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, and expando properties in unboxed
+ * objects.
+ *
+ * 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, JSCompartment* comp,
+ ObjectGroupFlags initialFlags);
+
+ inline bool hasAnyFlags(ObjectGroupFlags flags);
+ inline bool hasAllFlags(ObjectGroupFlags flags);
+
+ bool hasAllFlagsDontCheckGeneration(ObjectGroupFlags flags) {
+ MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
+ return (this->flagsDontCheckGeneration() & flags) == flags;
+ }
+
+ inline bool unknownProperties();
+
+ bool unknownPropertiesDontCheckGeneration() {
+ MOZ_ASSERT_IF(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
+ hasAllFlagsDontCheckGeneration(OBJECT_FLAG_DYNAMIC_MASK));
+ return !!(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
+ }
+
+ inline bool shouldPreTenure();
+
+ gc::InitialHeap initialHeap(CompilerConstraintList* constraints);
+
+ inline bool canPreTenure();
+ inline bool fromAllocationSite();
+ inline void setShouldPreTenure(ExclusiveContext* cx);
+
+ /*
+ * Get or create a property of this object. Only call this for properties which
+ * a script accesses explicitly.
+ */
+ inline HeapTypeSet* getProperty(ExclusiveContext* cx, JSObject* obj, jsid id);
+
+ /* Get a property only if it already exists. */
+ inline HeapTypeSet* maybeGetProperty(jsid id);
+
+ /*
+ * Iterate through the group's properties. getPropertyCount overapproximates
+ * in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
+ * getProperty may return nullptr.
+ */
+ inline unsigned getPropertyCount();
+ inline Property* getProperty(unsigned i);
+
+ /* Helpers */
+
+ void updateNewPropertyTypes(ExclusiveContext* cx, JSObject* obj, jsid id, HeapTypeSet* types);
+ void addDefiniteProperties(ExclusiveContext* cx, Shape* shape);
+ bool matchDefiniteProperties(HandleObject obj);
+ void markPropertyNonData(ExclusiveContext* cx, JSObject* obj, jsid id);
+ void markPropertyNonWritable(ExclusiveContext* cx, JSObject* obj, jsid id);
+ void markStateChange(ExclusiveContext* cx);
+ void setFlags(ExclusiveContext* cx, ObjectGroupFlags flags);
+ void markUnknown(ExclusiveContext* cx);
+ void maybeClearNewScriptOnOOM();
+ void clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement = nullptr);
+
+ void print();
+
+ inline void clearProperties();
+ void traceChildren(JSTracer* trc);
+
+ inline bool needsSweep();
+ inline void maybeSweep(AutoClearTypeInferenceStateOnOOM* oom);
+
+ private:
+ void sweep(AutoClearTypeInferenceStateOnOOM* oom);
+
+ 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);
+
+ static const JS::TraceKind TraceKind = JS::TraceKind::ObjectGroup;
+
+ 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_);
+ }
+
+ const ObjectGroupFlags* addressOfFlags() const {
+ return &flags_;
+ }
+
+ // Get the bit pattern stored in an object's addendum when it has an
+ // original unboxed group.
+ static inline int32_t addendumOriginalUnboxedGroupValue() {
+ return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT;
+ }
+
+ inline uint32_t basePropertyCount();
+
+ private:
+ 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.
+
+ enum class NewArrayKind {
+ Normal, // Specialize array group based on its element type.
+ CopyOnWrite, // Make an array with copy-on-write elements.
+ UnknownIndex // Make an array with an unknown element type.
+ };
+
+ // Create an ArrayObject or UnboxedArrayObject with the specified elements
+ // and a group specialized for the elements.
+ static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length,
+ NewObjectKind newKind,
+ NewArrayKind arrayKind = NewArrayKind::Normal);
+
+ // Create a PlainObject or UnboxedPlainObject with the specified properties
+ // and a group specialized for those properties.
+ static JSObject* newPlainObject(ExclusiveContext* cx,
+ IdValuePair* properties, size_t nproperties,
+ NewObjectKind newKind);
+
+ // 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, HandleObject proto = nullptr);
+
+ // Get a non-singleton group to use for objects created in a JSNative call.
+ static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key,
+ HandleObject proto = nullptr);
+
+ // 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 findAllocationSite(JSContext* cx, ObjectGroup* group,
+ JSScript** script, uint32_t* offset);
+
+ private:
+ static ObjectGroup* defaultNewGroup(JSContext* cx, JSProtoKey key);
+};
+
+// Structure used to manage the groups in a compartment.
+class ObjectGroupCompartment
+{
+ friend class ObjectGroup;
+
+ class NewTable;
+
+ // Set of default 'new' or lazy groups in the compartment.
+ NewTable* defaultNewTable;
+ NewTable* lazyTable;
+
+ struct ArrayObjectKey;
+ using ArrayObjectTable = js::GCRekeyableHashMap<ArrayObjectKey,
+ ReadBarrieredObjectGroup,
+ ArrayObjectKey,
+ SystemAllocPolicy>;
+
+ struct PlainObjectKey;
+ struct PlainObjectEntry;
+ struct PlainObjectTableSweepPolicy {
+ static bool needsSweep(PlainObjectKey* key, PlainObjectEntry* entry);
+ };
+ using PlainObjectTable = JS::GCHashMap<PlainObjectKey,
+ PlainObjectEntry,
+ PlainObjectKey,
+ SystemAllocPolicy,
+ PlainObjectTableSweepPolicy>;
+
+ // 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;
+ class AllocationSiteTable;
+
+ // Table for referencing types of objects keyed to an allocation site.
+ AllocationSiteTable* allocationSiteTable;
+
+ public:
+ struct NewEntry;
+
+ ObjectGroupCompartment();
+ ~ObjectGroupCompartment();
+
+ void replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
+ JSProtoKey kind, ObjectGroup* group);
+
+ 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);
+ }
+
+ private:
+#ifdef JSGC_HASH_TABLE_CHECKS
+ void checkNewTableAfterMovingGC(NewTable* table);
+#endif
+
+ void fixupNewTableAfterMovingGC(NewTable* table);
+};
+
+PlainObject*
+NewPlainObjectWithProperties(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties,
+ NewObjectKind newKind);
+
+bool
+CombineArrayElementTypes(ExclusiveContext* cx, JSObject* newObj,
+ const Value* compare, size_t ncompare);
+
+bool
+CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
+ const Value* compare, size_t ncompare);
+
+} // namespace js
+
+#endif /* vm_ObjectGroup_h */