/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#ifndef Structure_h
#define Structure_h
-#include "Identifier.h"
+#include "ClassInfo.h"
+#include "ConcurrentJITLock.h"
+#include "IndexingType.h"
+#include "JSCJSValue.h"
+#include "JSCell.h"
#include "JSType.h"
-#include "JSValue.h"
-#include "PropertyMapHashTable.h"
+#include "PropertyName.h"
#include "PropertyNameArray.h"
+#include "PropertyOffset.h"
#include "Protect.h"
-#include "StructureChain.h"
+#include "PutPropertySlot.h"
+#include "StructureIDBlob.h"
+#include "StructureRareData.h"
#include "StructureTransitionTable.h"
#include "JSTypeInfo.h"
-#include "UString.h"
-#include "WeakGCPtr.h"
+#include "Watchpoint.h"
+#include "Weak.h"
+#include "WriteBarrierInlines.h"
+#include <wtf/CompilationThread.h>
#include <wtf/PassRefPtr.h>
+#include <wtf/PrintStream.h>
#include <wtf/RefCounted.h>
+#include <wtf/text/StringImpl.h>
-#ifndef NDEBUG
-#define DUMP_PROPERTYMAP_STATS 0
-#else
-#define DUMP_PROPERTYMAP_STATS 0
-#endif
namespace JSC {
- class MarkStack;
- class PropertyNameArray;
- class PropertyNameArrayData;
-
- enum EnumerationMode {
- ExcludeDontEnumProperties,
- IncludeDontEnumProperties
- };
-
- class Structure : public RefCounted<Structure> {
- public:
- friend class JIT;
- friend class StructureTransitionTable;
- static PassRefPtr<Structure> create(JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount)
- {
- return adoptRef(new Structure(prototype, typeInfo, anonymousSlotCount));
- }
+class DeferGC;
+class LLIntOffsetsExtractor;
+class PropertyNameArray;
+class PropertyNameArrayData;
+class PropertyTable;
+class StructureChain;
+class SlotVisitor;
+class JSString;
+struct DumpContext;
+
+// The out-of-line property storage capacity to use when first allocating out-of-line
+// storage. Note that all objects start out without having any out-of-line storage;
+// this comes into play only on the first property store that exhausts inline storage.
+static const unsigned initialOutOfLineCapacity = 4;
+
+// The factor by which to grow out-of-line storage when it is exhausted, after the
+// initial allocation.
+static const unsigned outOfLineGrowthFactor = 2;
+
+class Structure : public JSCell {
+public:
+ friend class StructureTransitionTable;
+
+ typedef JSCell Base;
+
+ static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
- static void startIgnoringLeaks();
- static void stopIgnoringLeaks();
+ ~Structure();
- static void dumpStatistics();
+protected:
+ void finishCreation(VM& vm)
+ {
+ Base::finishCreation(vm);
+ ASSERT(m_prototype);
+ ASSERT(m_prototype.isObject() || m_prototype.isNull());
+ }
- static PassRefPtr<Structure> addPropertyTransition(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
- static PassRefPtr<Structure> addPropertyTransitionToExistingStructure(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
- static PassRefPtr<Structure> removePropertyTransition(Structure*, const Identifier& propertyName, size_t& offset);
- static PassRefPtr<Structure> changePrototypeTransition(Structure*, JSValue prototype);
- static PassRefPtr<Structure> despecifyFunctionTransition(Structure*, const Identifier&);
- static PassRefPtr<Structure> getterSetterTransition(Structure*);
- static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*);
- static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*);
+ void finishCreation(VM& vm, CreatingEarlyCellTag)
+ {
+ Base::finishCreation(vm, this, CreatingEarlyCell);
+ ASSERT(m_prototype);
+ ASSERT(m_prototype.isNull());
+ ASSERT(!vm.structureStructure);
+ }
- PassRefPtr<Structure> flattenDictionaryStructure(JSObject*);
+public:
+ StructureID id() const { return m_blob.structureID(); }
+ int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
+ int64_t idBlob() const { return m_blob.blob(); }
- ~Structure();
+ bool isProxy() const
+ {
+ JSType type = m_blob.type();
+ return type == ImpureProxyType || type == PureForwardingProxyType;
+ }
- // These should be used with caution.
- size_t addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue);
- size_t removePropertyWithoutTransition(const Identifier& propertyName);
- void setPrototypeWithoutTransition(JSValue prototype) { m_prototype = prototype; }
+ static void dumpStatistics();
+
+ JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext);
+ static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
+ JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
+ static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
+ JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
+ JS_EXPORT_PRIVATE static Structure* despecifyFunctionTransition(VM&, Structure*, PropertyName);
+ static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
+ JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*);
+ static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
+ static Structure* sealTransition(VM&, Structure*);
+ static Structure* freezeTransition(VM&, Structure*);
+ static Structure* preventExtensionsTransition(VM&, Structure*);
+ static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
+
+ bool isSealed(VM&);
+ bool isFrozen(VM&);
+ bool isExtensible() const { return !m_preventExtensions; }
+ bool didTransition() const { return m_didTransition; }
+ bool putWillGrowOutOfLineStorage();
+ size_t suggestedNewOutOfLineStorageCapacity();
+
+ JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
+
+ static const bool needsDestruction = true;
+ static const bool hasImmortalStructure = true;
+ static void destroy(JSCell*);
+
+ // These should be used with caution.
+ JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
+ PropertyOffset removePropertyWithoutTransition(VM&, PropertyName);
+ void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
- bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
- bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
+ bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
+ bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
- const TypeInfo& typeInfo() const { return m_typeInfo; }
+ bool hasBeenFlattenedBefore() const { return m_hasBeenFlattenedBefore; }
- JSValue storedPrototype() const { return m_prototype; }
- JSValue prototypeForLookup(ExecState*) const;
- StructureChain* prototypeChain(ExecState*) const;
+ bool propertyAccessesAreCacheable() { return m_dictionaryKind != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); }
- Structure* previousID() const { return m_previous.get(); }
+ // We use SlowPath in GetByIdStatus for structures that may get new impure properties later to prevent
+ // DFG from inlining property accesses since structures don't transition when a new impure property appears.
+ bool takesSlowPathInDFGForImpureProperty()
+ {
+ return typeInfo().hasImpureGetOwnPropertySlot();
+ }
- void growPropertyStorageCapacity();
- unsigned propertyStorageCapacity() const { return m_propertyStorageCapacity; }
- unsigned propertyStorageSize() const { return m_anonymousSlotCount + (m_propertyTable ? m_propertyTable->keyCount + (m_propertyTable->deletedOffsets ? m_propertyTable->deletedOffsets->size() : 0) : static_cast<unsigned>(m_offset + 1)); }
- bool isUsingInlineStorage() const;
+ // Type accessors.
+ TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); }
+ bool isObject() const { return typeInfo().isObject(); }
- size_t get(const Identifier& propertyName);
- size_t get(const UString::Rep* rep, unsigned& attributes, JSCell*& specificValue);
- size_t get(const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue)
- {
- ASSERT(!propertyName.isNull());
- return get(propertyName.ustring().rep(), attributes, specificValue);
- }
- bool transitionedFor(const JSCell* specificValue)
- {
- return m_specificValueInPrevious == specificValue;
- }
- bool hasTransition(UString::Rep*, unsigned attributes);
- bool hasTransition(const Identifier& propertyName, unsigned attributes)
- {
- return hasTransition(propertyName._ustring.rep(), attributes);
- }
+ IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; }
+ IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); }
+
+ bool mayInterceptIndexedAccesses() const
+ {
+ return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
+ }
+
+ bool anyObjectInChainMayInterceptIndexedAccesses() const;
+ bool holesMustForwardToPrototype(VM&) const;
+
+ bool needsSlowPutIndexing() const;
+ NonPropertyTransition suggestedArrayStorageTransition() const;
+
+ JSGlobalObject* globalObject() const { return m_globalObject.get(); }
+ void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); }
+
+ JSValue storedPrototype() const { return m_prototype.get(); }
+ JSObject* storedPrototypeObject() const;
+ Structure* storedPrototypeStructure() const;
+ JSValue prototypeForLookup(ExecState*) const;
+ JSValue prototypeForLookup(JSGlobalObject*) const;
+ JSValue prototypeForLookup(CodeBlock*) const;
+ StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
+ StructureChain* prototypeChain(ExecState*) const;
+ static void visitChildren(JSCell*, SlotVisitor&);
+
+ // Will just the prototype chain intercept this property access?
+ bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
+
+ bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; }
+
+ Structure* previousID() const
+ {
+ ASSERT(structure()->classInfo() == info());
+ if (m_hasRareData)
+ return rareData()->previousID();
+ return previous();
+ }
+ bool transitivelyTransitionedFrom(Structure* structureToFind);
- bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
- void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; }
+ unsigned outOfLineCapacity() const
+ {
+ ASSERT(checkOffsetConsistency());
+
+ unsigned outOfLineSize = this->outOfLineSize();
- bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
+ if (!outOfLineSize)
+ return 0;
- bool hasAnonymousSlots() const { return !!m_anonymousSlotCount; }
- unsigned anonymousSlotCount() const { return m_anonymousSlotCount; }
-
- bool isEmpty() const { return m_propertyTable ? !m_propertyTable->keyCount : m_offset == noOffset; }
+ if (outOfLineSize <= initialOutOfLineCapacity)
+ return initialOutOfLineCapacity;
- void despecifyDictionaryFunction(const Identifier& propertyName);
- void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
+ ASSERT(outOfLineSize > initialOutOfLineCapacity);
+ COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two);
+ return WTF::roundUpToPowerOfTwo(outOfLineSize);
+ }
+ unsigned outOfLineSize() const
+ {
+ ASSERT(checkOffsetConsistency());
+ ASSERT(structure()->classInfo() == info());
+
+ return numberOfOutOfLineSlotsForLastOffset(m_offset);
+ }
+ bool hasInlineStorage() const
+ {
+ return !!m_inlineCapacity;
+ }
+ unsigned inlineCapacity() const
+ {
+ return m_inlineCapacity;
+ }
+ unsigned inlineSize() const
+ {
+ return std::min<unsigned>(m_offset + 1, m_inlineCapacity);
+ }
+ unsigned totalStorageSize() const
+ {
+ return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
+ }
+ unsigned totalStorageCapacity() const
+ {
+ ASSERT(structure()->classInfo() == info());
+ return outOfLineCapacity() + inlineCapacity();
+ }
- void setEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
- void clearEnumerationCache(JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
- JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
- void getPropertyNames(PropertyNameArray&, EnumerationMode mode);
-
- private:
+ bool isValidOffset(PropertyOffset offset) const
+ {
+ return JSC::isValidOffset(offset)
+ && offset <= m_offset
+ && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset);
+ }
+
+ bool couldHaveIndexingHeader() const
+ {
+ return hasIndexedProperties(indexingType())
+ || isTypedView(m_classInfo->typedArrayStorageType);
+ }
+
+ bool hasIndexingHeader(const JSCell*) const;
+
+ bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject);
- Structure(JSValue prototype, const TypeInfo&, unsigned anonymousSlotCount);
-
- typedef enum {
- NoneDictionaryKind = 0,
- CachedDictionaryKind = 1,
- UncachedDictionaryKind = 2
- } DictionaryKind;
- static PassRefPtr<Structure> toDictionaryTransition(Structure*, DictionaryKind);
-
- size_t put(const Identifier& propertyName, unsigned attributes, JSCell* specificValue);
- size_t remove(const Identifier& propertyName);
-
- void expandPropertyMapHashTable();
- void rehashPropertyMapHashTable();
- void rehashPropertyMapHashTable(unsigned newTableSize);
- void createPropertyMapHashTable();
- void createPropertyMapHashTable(unsigned newTableSize);
- void insertIntoPropertyMapHashTable(const PropertyMapEntry&);
- void checkConsistency();
-
- bool despecifyFunction(const Identifier&);
- void despecifyAllFunctions();
-
- PropertyMapHashTable* copyPropertyTable();
- void materializePropertyMap();
- void materializePropertyMapIfNecessary()
- {
- if (m_propertyTable || !m_previous)
- return;
- materializePropertyMap();
- }
+ PropertyOffset get(VM&, PropertyName);
+ PropertyOffset get(VM&, const WTF::String& name);
+ PropertyOffset get(VM&, PropertyName, unsigned& attributes, JSCell*& specificValue);
- signed char transitionCount() const
- {
- // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
- return m_offset == noOffset ? 0 : m_offset + 1;
- }
+ PropertyOffset getConcurrently(VM&, StringImpl* uid);
+ PropertyOffset getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue);
- typedef std::pair<Structure*, Structure*> Transition;
- typedef HashMap<StructureTransitionTableHash::Key, Transition, StructureTransitionTableHash, StructureTransitionTableHashTraits> TransitionTable;
+ bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
+ bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
+ void setHasGetterSetterProperties(bool is__proto__)
+ {
+ m_hasGetterSetterProperties = true;
+ if (!is__proto__)
+ m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
+ }
+
+ bool hasCustomGetterSetterProperties() const { return m_hasCustomGetterSetterProperties; }
+ void setHasCustomGetterSetterProperties(bool is__proto__)
+ {
+ m_hasCustomGetterSetterProperties = true;
+ if (!is__proto__)
+ m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
+ }
- inline bool transitionTableContains(const StructureTransitionTableHash::Key& key, JSCell* specificValue);
- inline void transitionTableRemove(const StructureTransitionTableHash::Key& key, JSCell* specificValue);
- inline void transitionTableAdd(const StructureTransitionTableHash::Key& key, Structure* structure, JSCell* specificValue);
- inline bool transitionTableHasTransition(const StructureTransitionTableHash::Key& key) const;
- inline Structure* transitionTableGet(const StructureTransitionTableHash::Key& key, JSCell* specificValue) const;
+ void setContainsReadOnlyProperties()
+ {
+ m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
+ }
- TransitionTable* transitionTable() const { ASSERT(!m_isUsingSingleSlot); return m_transitions.m_table; }
- inline void setTransitionTable(TransitionTable* table);
- Structure* singleTransition() const { ASSERT(m_isUsingSingleSlot); return m_transitions.m_singleTransition; }
- void setSingleTransition(Structure* structure) { ASSERT(m_isUsingSingleSlot); m_transitions.m_singleTransition = structure; }
+ bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
- bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
+ bool isEmpty() const
+ {
+ ASSERT(checkOffsetConsistency());
+ return !JSC::isValidOffset(m_offset);
+ }
- static const unsigned emptyEntryIndex = 0;
-
- static const signed char s_maxTransitionLength = 64;
+ JS_EXPORT_PRIVATE void despecifyDictionaryFunction(VM&, PropertyName);
+ void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
- static const signed char noOffset = -1;
+ void setEnumerationCache(VM&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
+ JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
+ void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
- static const unsigned maxSpecificFunctionThrashCount = 3;
+ JSString* objectToStringValue()
+ {
+ if (!m_hasRareData)
+ return 0;
+ return rareData()->objectToStringValue();
+ }
- TypeInfo m_typeInfo;
+ void setObjectToStringValue(VM& vm, JSString* value)
+ {
+ if (!m_hasRareData)
+ allocateRareData(vm);
+ rareData()->setObjectToStringValue(vm, value);
+ }
- JSValue m_prototype;
- mutable RefPtr<StructureChain> m_cachedPrototypeChain;
+ bool staticFunctionsReified()
+ {
+ return m_staticFunctionReified;
+ }
- RefPtr<Structure> m_previous;
- RefPtr<UString::Rep> m_nameInPrevious;
- JSCell* m_specificValueInPrevious;
+ void setStaticFunctionsReified()
+ {
+ m_staticFunctionReified = true;
+ }
+
+ const ClassInfo* classInfo() const { return m_classInfo; }
+
+ static ptrdiff_t structureIDOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
+ }
+
+ static ptrdiff_t prototypeOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_prototype);
+ }
+
+ static ptrdiff_t globalObjectOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_globalObject);
+ }
+
+ static ptrdiff_t classInfoOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_classInfo);
+ }
+
+ static ptrdiff_t indexingTypeOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset();
+ }
+
+ static Structure* createStructure(VM&);
+
+ bool transitionWatchpointSetHasBeenInvalidated() const
+ {
+ return m_transitionWatchpointSet.hasBeenInvalidated();
+ }
+
+ bool transitionWatchpointSetIsStillValid() const
+ {
+ return m_transitionWatchpointSet.isStillValid();
+ }
+
+ void addTransitionWatchpoint(Watchpoint* watchpoint) const
+ {
+ ASSERT(transitionWatchpointSetIsStillValid());
+ m_transitionWatchpointSet.add(watchpoint);
+ }
+
+ void notifyTransitionFromThisStructure() const
+ {
+ m_transitionWatchpointSet.fireAll();
+ }
+
+ InlineWatchpointSet& transitionWatchpointSet() const
+ {
+ return m_transitionWatchpointSet;
+ }
+
+ void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
+ void dumpBrief(PrintStream&, const CString&) const;
+
+ static void dumpContextHeader(PrintStream&);
+
+ DECLARE_EXPORT_INFO;
- // 'm_isUsingSingleSlot' indicates whether we are using the single transition optimisation.
- union {
- TransitionTable* m_table;
- Structure* m_singleTransition;
- } m_transitions;
+private:
+ friend class LLIntOffsetsExtractor;
- WeakGCPtr<JSPropertyNameIterator> m_enumerationCache;
+ JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
+ Structure(VM&);
+ Structure(VM&, Structure*);
- PropertyMapHashTable* m_propertyTable;
+ static Structure* create(VM&, Structure*);
+
+ static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
- uint32_t m_propertyStorageCapacity;
+ // This will return the structure that has a usable property table, that property table,
+ // and the list of structures that we visited before we got to it. If it returns a
+ // non-null structure, it will also lock the structure that it returns; it is your job
+ // to unlock it.
+ void findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*&, PropertyTable*&);
+
+ typedef enum {
+ NoneDictionaryKind = 0,
+ CachedDictionaryKind = 1,
+ UncachedDictionaryKind = 2
+ } DictionaryKind;
+ static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind);
+
+ PropertyOffset putSpecificValue(VM&, PropertyName, unsigned attributes, JSCell* specificValue);
+ PropertyOffset remove(PropertyName);
+
+ void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0);
+ void checkConsistency();
+
+ bool despecifyFunction(VM&, PropertyName);
+ void despecifyAllFunctions(VM&);
+
+ WriteBarrier<PropertyTable>& propertyTable();
+ PropertyTable* takePropertyTableOrCloneIfPinned(VM&);
+ PropertyTable* copyPropertyTable(VM&);
+ PropertyTable* copyPropertyTableForPinning(VM&);
+ JS_EXPORT_PRIVATE void materializePropertyMap(VM&);
+ ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, DeferGC&)
+ {
+ ASSERT(!isCompilationThread());
+ ASSERT(structure()->classInfo() == info());
+ ASSERT(checkOffsetConsistency());
+ if (!propertyTable() && previousID())
+ materializePropertyMap(vm);
+ }
+ ALWAYS_INLINE void materializePropertyMapIfNecessary(VM& vm, PropertyTable*& table)
+ {
+ ASSERT(!isCompilationThread());
+ ASSERT(structure()->classInfo() == info());
+ ASSERT(checkOffsetConsistency());
+ table = propertyTable().get();
+ if (!table && previousID()) {
+ DeferGC deferGC(vm.heap);
+ materializePropertyMap(vm);
+ table = propertyTable().get();
+ }
+ }
+ void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&)
+ {
+ ASSERT(structure()->classInfo() == info());
+ checkOffsetConsistency();
+ if (!propertyTable())
+ materializePropertyMap(vm);
+ }
- // m_offset does not account for anonymous slots
- signed char m_offset;
+ void setPreviousID(VM& vm, Structure* structure)
+ {
+ if (m_hasRareData)
+ rareData()->setPreviousID(vm, structure);
+ else
+ m_previousOrRareData.set(vm, this, structure);
+ }
- unsigned m_dictionaryKind : 2;
- bool m_isPinnedPropertyTable : 1;
- bool m_hasGetterSetterProperties : 1;
- bool m_hasNonEnumerableProperties : 1;
-#if COMPILER(WINSCW)
- // Workaround for Symbian WINSCW compiler that cannot resolve unsigned type of the declared
- // bitfield, when used as argument in make_pair() function calls in structure.ccp.
- // This bitfield optimization is insignificant for the Symbian emulator target.
- unsigned m_attributesInPrevious;
-#else
- unsigned m_attributesInPrevious : 7;
-#endif
- unsigned m_specificFunctionThrashCount : 2;
- unsigned m_anonymousSlotCount : 5;
- unsigned m_isUsingSingleSlot : 1;
- // 4 free bits
- };
+ void clearPreviousID()
+ {
+ if (m_hasRareData)
+ rareData()->clearPreviousID();
+ else
+ m_previousOrRareData.clear();
+ }
- inline size_t Structure::get(const Identifier& propertyName)
+ int transitionCount() const
{
- ASSERT(!propertyName.isNull());
+ // Since the number of transitions is always the same as m_offset, we keep the size of Structure down by not storing both.
+ return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity);
+ }
+
+ bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
+ bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
+
+ void pin();
+
+ Structure* previous() const
+ {
+ ASSERT(!m_hasRareData);
+ return static_cast<Structure*>(m_previousOrRareData.get());
+ }
+
+ StructureRareData* rareData() const
+ {
+ ASSERT(m_hasRareData);
+ return static_cast<StructureRareData*>(m_previousOrRareData.get());
+ }
+
+ bool checkOffsetConsistency() const;
- materializePropertyMapIfNecessary();
- if (!m_propertyTable)
- return WTF::notFound;
+ void allocateRareData(VM&);
+ void cloneRareDataFrom(VM&, const Structure*);
- UString::Rep* rep = propertyName._ustring.rep();
+ static const int s_maxTransitionLength = 64;
+ static const int s_maxTransitionLengthForNonEvalPutById = 512;
- unsigned i = rep->existingHash();
+ static const unsigned maxSpecificFunctionThrashCount = 3;
+
+ // These need to be properly aligned at the beginning of the 'Structure'
+ // part of the object.
+ StructureIDBlob m_blob;
+ TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
-#if DUMP_PROPERTYMAP_STATS
- ++numProbes;
-#endif
+ WriteBarrier<JSGlobalObject> m_globalObject;
+ WriteBarrier<Unknown> m_prototype;
+ mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
- unsigned entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
- if (entryIndex == emptyEntryIndex)
- return WTF::notFound;
+ WriteBarrier<JSCell> m_previousOrRareData;
- if (rep == m_propertyTable->entries()[entryIndex - 1].key)
- return m_propertyTable->entries()[entryIndex - 1].offset;
+ RefPtr<StringImpl> m_nameInPrevious;
+ WriteBarrier<JSCell> m_specificValueInPrevious;
-#if DUMP_PROPERTYMAP_STATS
- ++numCollisions;
-#endif
+ const ClassInfo* m_classInfo;
- unsigned k = 1 | WTF::doubleHash(rep->existingHash());
+ StructureTransitionTable m_transitionTable;
- while (1) {
- i += k;
+ // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
+ WriteBarrier<PropertyTable> m_propertyTableUnsafe;
-#if DUMP_PROPERTYMAP_STATS
- ++numRehashes;
-#endif
+ mutable InlineWatchpointSet m_transitionWatchpointSet;
- entryIndex = m_propertyTable->entryIndices[i & m_propertyTable->sizeMask];
- if (entryIndex == emptyEntryIndex)
- return WTF::notFound;
+ COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
- if (rep == m_propertyTable->entries()[entryIndex - 1].key)
- return m_propertyTable->entries()[entryIndex - 1].offset;
- }
- }
+ // m_offset does not account for anonymous slots
+ PropertyOffset m_offset;
+
+ uint8_t m_inlineCapacity;
+
+ ConcurrentJITLock m_lock;
+
+ unsigned m_dictionaryKind : 2;
+ bool m_hasBeenFlattenedBefore : 1;
+ bool m_isPinnedPropertyTable : 1;
+ bool m_hasGetterSetterProperties : 1;
+ bool m_hasCustomGetterSetterProperties : 1;
+ bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
+ bool m_hasNonEnumerableProperties : 1;
+ unsigned m_attributesInPrevious : 14;
+ unsigned m_specificFunctionThrashCount : 2;
+ unsigned m_preventExtensions : 1;
+ unsigned m_didTransition : 1;
+ unsigned m_staticFunctionReified : 1;
+ bool m_hasRareData : 1;
+};
} // namespace JSC