diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/vm/ObjectGroup.h | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/ObjectGroup.h')
-rw-r--r-- | js/src/vm/ObjectGroup.h | 655 |
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 0000000000..4e24de9f14 --- /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 */ |