/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012, 2013, 2014 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 "PutPropertySlot.h"
+#include "StructureIDBlob.h"
+#include "StructureRareData.h"
#include "StructureTransitionTable.h"
#include "JSTypeInfo.h"
-#include "UString.h"
+#include "Watchpoint.h"
#include "Weak.h"
-#include <wtf/PassOwnPtr.h>
+#include "WriteBarrierInlines.h"
+#include <wtf/CompilationThread.h>
#include <wtf/PassRefPtr.h>
+#include <wtf/PrintStream.h>
#include <wtf/RefCounted.h>
+namespace WTF {
-namespace JSC {
-
- class MarkStack;
- class PropertyNameArray;
- class PropertyNameArrayData;
- class StructureChain;
- typedef MarkStack SlotVisitor;
-
- struct ClassInfo;
-
- enum EnumerationMode {
- ExcludeDontEnumProperties,
- IncludeDontEnumProperties
- };
-
- class Structure : public JSCell {
- public:
- friend class StructureTransitionTable;
- static Structure* create(JSGlobalData& globalData, JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount, const ClassInfo* classInfo)
- {
- ASSERT(globalData.structureStructure);
- ASSERT(classInfo);
- return new (&globalData) Structure(globalData, prototype, typeInfo, anonymousSlotCount, classInfo);
- }
+class UniquedStringImpl;
- static void dumpStatistics();
-
- static Structure* addPropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
- static Structure* addPropertyTransitionToExistingStructure(Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset);
- static Structure* removePropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, size_t& offset);
- static Structure* changePrototypeTransition(JSGlobalData&, Structure*, JSValue prototype);
- static Structure* despecifyFunctionTransition(JSGlobalData&, Structure*, const Identifier&);
- static Structure* getterSetterTransition(JSGlobalData&, Structure*);
- static Structure* toCacheableDictionaryTransition(JSGlobalData&, Structure*);
- static Structure* toUncacheableDictionaryTransition(JSGlobalData&, Structure*);
- static Structure* sealTransition(JSGlobalData&, Structure*);
- static Structure* freezeTransition(JSGlobalData&, Structure*);
- static Structure* preventExtensionsTransition(JSGlobalData&, Structure*);
-
- bool isSealed(JSGlobalData&);
- bool isFrozen(JSGlobalData&);
- bool isExtensible() const { return !m_preventExtensions; }
- bool didTransition() const { return m_didTransition; }
-
- Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*);
-
- ~Structure();
-
- // These should be used with caution.
- size_t addPropertyWithoutTransition(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue);
- size_t removePropertyWithoutTransition(JSGlobalData&, const Identifier& propertyName);
- void setPrototypeWithoutTransition(JSGlobalData& globalData, JSValue prototype) { m_prototype.set(globalData, this, prototype); }
-
- bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
- bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
-
- const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == &s_info); return m_typeInfo; }
-
- JSValue storedPrototype() const { return m_prototype.get(); }
- JSValue prototypeForLookup(ExecState*) const;
- StructureChain* prototypeChain(ExecState*) const;
- void visitChildren(SlotVisitor&);
-
- Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); }
-
- void growPropertyStorageCapacity();
- unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; }
- unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return m_anonymousSlotCount + (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast<unsigned>(m_offset + 1)); }
- bool isUsingInlineStorage() const;
-
- size_t get(JSGlobalData&, const Identifier& propertyName);
- size_t get(JSGlobalData&, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue);
- size_t get(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes, JSCell*& specificValue)
- {
- ASSERT(!propertyName.isNull());
- ASSERT(structure()->classInfo() == &s_info);
- return get(globalData, propertyName.impl(), attributes, specificValue);
- }
+} // namespace WTF
- bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
- void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; }
+namespace JSC {
- bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
+class DeferGC;
+class LLIntOffsetsExtractor;
+class PropertyNameArray;
+class PropertyNameArrayData;
+class PropertyTable;
+class StructureChain;
+class StructureShape;
+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;
+
+struct PropertyMapEntry {
+ UniquedStringImpl* key;
+ PropertyOffset offset;
+ unsigned attributes;
+
+ PropertyMapEntry()
+ : key(nullptr)
+ , offset(invalidOffset)
+ , attributes(0)
+ {
+ }
+
+ PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes)
+ : key(key)
+ , offset(offset)
+ , attributes(attributes)
+ {
+ }
+};
- bool hasAnonymousSlots() const { return !!m_anonymousSlotCount; }
- unsigned anonymousSlotCount() const { return m_anonymousSlotCount; }
-
- bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; }
+class Structure final : public JSCell {
+public:
+ friend class StructureTransitionTable;
- void despecifyDictionaryFunction(JSGlobalData&, const Identifier& propertyName);
- void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; }
+ typedef JSCell Base;
+ static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+
+ static Structure* create(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType = NonArray, unsigned inlineCapacity = 0);
- void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h.
- JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h.
- void getPropertyNames(JSGlobalData&, PropertyNameArray&, EnumerationMode mode);
+ ~Structure();
- const ClassInfo* classInfo() const { return m_classInfo; }
+protected:
+ void finishCreation(VM& vm)
+ {
+ Base::finishCreation(vm);
+ ASSERT(m_prototype);
+ ASSERT(m_prototype.isObject() || m_prototype.isNull());
+ }
- static ptrdiff_t prototypeOffset()
- {
- return OBJECT_OFFSETOF(Structure, m_prototype);
- }
+ void finishCreation(VM& vm, CreatingEarlyCellTag)
+ {
+ Base::finishCreation(vm, this, CreatingEarlyCell);
+ ASSERT(m_prototype);
+ ASSERT(m_prototype.isNull());
+ ASSERT(!vm.structureStructure);
+ }
- static ptrdiff_t typeInfoFlagsOffset()
- {
- return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset();
- }
+public:
+ StructureID id() const { return m_blob.structureID(); }
+ int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); }
+ int64_t idBlob() const { return m_blob.blob(); }
- static ptrdiff_t typeInfoTypeOffset()
- {
- return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset();
- }
+ bool isProxy() const
+ {
+ JSType type = m_blob.type();
+ return type == ImpureProxyType || type == PureForwardingProxyType;
+ }
- static Structure* createStructure(JSGlobalData& globalData)
- {
- ASSERT(!globalData.structureStructure);
- return new (&globalData) Structure(globalData);
- }
+ static void dumpStatistics();
+
+ JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext);
+ static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
+ JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
+ static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
+ JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
+ JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
+ JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*);
+ static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
+ JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);
+ JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*);
+ static Structure* preventExtensionsTransition(VM&, Structure*);
+ JS_EXPORT_PRIVATE static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition);
+
+ JS_EXPORT_PRIVATE bool isSealed(VM&);
+ JS_EXPORT_PRIVATE bool isFrozen(VM&);
+ bool isExtensible() const { return !preventExtensions(); }
+ bool putWillGrowOutOfLineStorage();
+ size_t suggestedNewOutOfLineStorageCapacity();
+
+ JS_EXPORT_PRIVATE Structure* flattenDictionaryStructure(VM&, JSObject*);
+
+ static const bool needsDestruction = true;
+ static void destroy(JSCell*);
+
+ // These should be used with caution.
+ JS_EXPORT_PRIVATE PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes);
+ PropertyOffset removePropertyWithoutTransition(VM&, PropertyName);
+ void setPrototypeWithoutTransition(VM& vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
- static JS_EXPORTDATA const ClassInfo s_info;
+ bool isDictionary() const { return dictionaryKind() != NoneDictionaryKind; }
+ bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; }
+
+ bool propertyAccessesAreCacheable() { return dictionaryKind() != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); }
+
+ // 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();
+ }
- private:
- Structure(JSGlobalData&, JSValue prototype, const TypeInfo&, unsigned anonymousSlotCount, const ClassInfo*);
- Structure(JSGlobalData&);
- Structure(JSGlobalData&, const Structure*);
+ // Type accessors.
+ TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); }
+ bool isObject() const { return typeInfo().isObject(); }
- static Structure* create(JSGlobalData& globalData, const Structure* structure)
- {
- ASSERT(globalData.structureStructure);
- return new (&globalData) Structure(globalData, structure);
- }
+ IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; }
+ IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); }
- typedef enum {
- NoneDictionaryKind = 0,
- CachedDictionaryKind = 1,
- UncachedDictionaryKind = 2
- } DictionaryKind;
- static Structure* toDictionaryTransition(JSGlobalData&, Structure*, DictionaryKind);
-
- size_t putSpecificValue(JSGlobalData&, const Identifier& propertyName, unsigned attributes, JSCell* specificValue);
- size_t remove(const Identifier& propertyName);
-
- void createPropertyMap(unsigned keyCount = 0);
- void checkConsistency();
-
- bool despecifyFunction(JSGlobalData&, const Identifier&);
- void despecifyAllFunctions(JSGlobalData&);
-
- PassOwnPtr<PropertyTable> copyPropertyTable(JSGlobalData&, Structure* owner);
- void materializePropertyMap(JSGlobalData&);
- void materializePropertyMapIfNecessary(JSGlobalData& globalData)
- {
- ASSERT(structure()->classInfo() == &s_info);
- if (!m_propertyTable && m_previous)
- materializePropertyMap(globalData);
- }
-
- 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;
- }
+ 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);
+
+ Structure* previousID() const
+ {
+ ASSERT(structure()->classInfo() == info());
+ if (hasRareData())
+ return rareData()->previousID();
+ return previous();
+ }
+ bool transitivelyTransitionedFrom(Structure* structureToFind);
- bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
+ unsigned outOfLineCapacity() const
+ {
+ ASSERT(checkOffsetConsistency());
+
+ unsigned outOfLineSize = this->outOfLineSize();
- static const signed char s_maxTransitionLength = 64;
+ if (!outOfLineSize)
+ return 0;
- static const signed char noOffset = -1;
+ if (outOfLineSize <= initialOutOfLineCapacity)
+ return initialOutOfLineCapacity;
- static const unsigned maxSpecificFunctionThrashCount = 3;
+ 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();
+ }
- TypeInfo m_typeInfo;
+ 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);
+
+ PropertyOffset get(VM&, PropertyName);
+ PropertyOffset get(VM&, PropertyName, unsigned& attributes);
+
+ // This is a somewhat internalish method. It will call your functor while possibly holding the
+ // Structure's lock. There is no guarantee whether the lock is held or not in any particular
+ // call. So, you have to assume the worst. Also, the functor returns true if it wishes for you
+ // to continue or false if it's done.
+ template<typename Functor>
+ void forEachPropertyConcurrently(const Functor&);
+
+ PropertyOffset getConcurrently(UniquedStringImpl* uid);
+ PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes);
+
+ Vector<PropertyMapEntry> getPropertiesConcurrently();
+
+ void setHasGetterSetterPropertiesWithProtoCheck(bool is__proto__)
+ {
+ setHasGetterSetterProperties(true);
+ if (!is__proto__)
+ setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
+ }
+
+ void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); }
+
+ void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__)
+ {
+ setHasCustomGetterSetterProperties(true);
+ if (!is__proto__)
+ setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true);
+ }
+
+ bool isEmpty() const
+ {
+ ASSERT(checkOffsetConsistency());
+ return !JSC::isValidOffset(m_offset);
+ }
- WriteBarrier<Unknown> m_prototype;
- mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
+ void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
+ JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
+ bool canCachePropertyNameEnumerator() const;
+ bool canAccessPropertiesQuickly() const;
- WriteBarrier<Structure> m_previous;
- RefPtr<StringImpl> m_nameInPrevious;
- WriteBarrier<JSCell> m_specificValueInPrevious;
+ void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
- const ClassInfo* m_classInfo;
+ JSString* objectToStringValue()
+ {
+ if (!hasRareData())
+ return 0;
+ return rareData()->objectToStringValue();
+ }
- StructureTransitionTable m_transitionTable;
+ void setObjectToStringValue(VM& vm, JSString* value)
+ {
+ if (!hasRareData())
+ allocateRareData(vm);
+ rareData()->setObjectToStringValue(vm, value);
+ }
- WriteBarrier<JSPropertyNameIterator> m_enumerationCache;
+ const ClassInfo* classInfo() const { return m_classInfo; }
- OwnPtr<PropertyTable> m_propertyTable;
+ static ptrdiff_t structureIDOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset();
+ }
- uint32_t m_propertyStorageCapacity;
+ static ptrdiff_t prototypeOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_prototype);
+ }
- // m_offset does not account for anonymous slots
- signed char m_offset;
+ static ptrdiff_t globalObjectOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_globalObject);
+ }
- 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_preventExtensions : 1;
- unsigned m_didTransition : 1;
- // 3 free bits
- };
+ static ptrdiff_t classInfoOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_classInfo);
+ }
+
+ static ptrdiff_t indexingTypeOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset();
+ }
- inline size_t Structure::get(JSGlobalData& globalData, const Identifier& propertyName)
+ static Structure* createStructure(VM&);
+
+ bool transitionWatchpointSetHasBeenInvalidated() const
+ {
+ return m_transitionWatchpointSet.hasBeenInvalidated();
+ }
+
+ bool transitionWatchpointSetIsStillValid() const
+ {
+ return m_transitionWatchpointSet.isStillValid();
+ }
+
+ bool dfgShouldWatchIfPossible() const
+ {
+ // FIXME: We would like to not watch things that are unprofitable to watch, like
+ // dictionaries. Unfortunately, we can't do such things: a dictionary could get flattened,
+ // in which case it will start to appear watchable and so the DFG will think that it is
+ // watching it. We should come up with a comprehensive story for not watching things that
+ // aren't profitable to watch.
+ // https://bugs.webkit.org/show_bug.cgi?id=133625
+ return true;
+ }
+
+ bool dfgShouldWatch() const
+ {
+ return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
+ }
+
+ void addTransitionWatchpoint(Watchpoint* watchpoint) const
+ {
+ ASSERT(transitionWatchpointSetIsStillValid());
+ m_transitionWatchpointSet.add(watchpoint);
+ }
+
+ void didTransitionFromThisStructure() const;
+
+ InlineWatchpointSet& transitionWatchpointSet() const
{
- ASSERT(structure()->classInfo() == &s_info);
- materializePropertyMapIfNecessary(globalData);
- if (!m_propertyTable)
- return notFound;
+ return m_transitionWatchpointSet;
+ }
+
+ WatchpointSet* ensurePropertyReplacementWatchpointSet(VM&, PropertyOffset);
+ void startWatchingPropertyForReplacements(VM& vm, PropertyOffset offset)
+ {
+ ensurePropertyReplacementWatchpointSet(vm, offset);
+ }
+ void startWatchingPropertyForReplacements(VM&, PropertyName);
+ WatchpointSet* propertyReplacementWatchpointSet(PropertyOffset);
+ void didReplaceProperty(PropertyOffset);
+ void didCachePropertyReplacement(VM&, PropertyOffset);
+
+ void startWatchingInternalPropertiesIfNecessary(VM& vm)
+ {
+ if (LIKELY(didWatchInternalProperties()))
+ return;
+ startWatchingInternalProperties(vm);
+ }
+
+ void startWatchingInternalPropertiesIfNecessaryForEntireChain(VM& vm)
+ {
+ for (Structure* structure = this; structure; structure = structure->storedPrototypeStructure())
+ structure->startWatchingInternalPropertiesIfNecessary(vm);
+ }
- PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first;
- ASSERT(!entry || entry->offset >= m_anonymousSlotCount);
- return entry ? entry->offset : notFound;
+ PassRefPtr<StructureShape> toStructureShape(JSValue);
+
+ // Determines if the two structures match enough that this one could be used for allocations
+ // of the other one.
+ bool canUseForAllocationsOf(Structure*);
+
+ void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
+ void dumpBrief(PrintStream&, const CString&) const;
+
+ static void dumpContextHeader(PrintStream&);
+
+ DECLARE_EXPORT_INFO;
+
+private:
+ typedef enum {
+ NoneDictionaryKind = 0,
+ CachedDictionaryKind = 1,
+ UncachedDictionaryKind = 2
+ } DictionaryKind;
+
+public:
+#define DEFINE_BITFIELD(type, lowerName, upperName, width, offset) \
+ static const uint32_t s_##lowerName##Shift = offset;\
+ static const uint32_t s_##lowerName##Mask = ((1 << (width - 1)) | ((1 << (width - 1)) - 1));\
+ type lowerName() const { return static_cast<type>((m_bitField >> offset) & s_##lowerName##Mask); }\
+ void set##upperName(type newValue) \
+ {\
+ m_bitField &= ~(s_##lowerName##Mask << offset);\
+ m_bitField |= (newValue & s_##lowerName##Mask) << offset;\
}
- inline bool JSCell::isObject() const
+ DEFINE_BITFIELD(DictionaryKind, dictionaryKind, DictionaryKind, 2, 0);
+ DEFINE_BITFIELD(bool, isPinnedPropertyTable, IsPinnedPropertyTable, 1, 2);
+ DEFINE_BITFIELD(bool, hasGetterSetterProperties, HasGetterSetterProperties, 1, 3);
+ DEFINE_BITFIELD(bool, hasReadOnlyOrGetterSetterPropertiesExcludingProto, HasReadOnlyOrGetterSetterPropertiesExcludingProto, 1, 4);
+ DEFINE_BITFIELD(bool, hasNonEnumerableProperties, HasNonEnumerableProperties, 1, 5);
+ DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6);
+ DEFINE_BITFIELD(bool, preventExtensions, PreventExtensions, 1, 20);
+ DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21);
+ DEFINE_BITFIELD(bool, staticFunctionsReified, StaticFunctionsReified, 1, 22);
+ DEFINE_BITFIELD(bool, hasRareData, HasRareData, 1, 23);
+ DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 24);
+ DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 25);
+ DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 26);
+
+private:
+ friend class LLIntOffsetsExtractor;
+
+ JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity);
+ Structure(VM&);
+ Structure(VM&, Structure*);
+
+ static Structure* create(VM&, Structure*);
+
+ static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
+
+ // 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*&);
+
+ static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind);
+
+ PropertyOffset add(VM&, PropertyName, unsigned attributes);
+ PropertyOffset remove(PropertyName);
+
+ void createPropertyMap(const GCSafeConcurrentJITLocker&, VM&, unsigned keyCount = 0);
+ void checkConsistency();
+
+ 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&)
{
- return m_structure->typeInfo().type() == ObjectType;
+ ASSERT(!isCompilationThread());
+ ASSERT(structure()->classInfo() == info());
+ ASSERT(checkOffsetConsistency());
+ if (!propertyTable() && previousID())
+ materializePropertyMap(vm);
}
-
- inline bool JSCell::isString() const
+ 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&)
{
- return m_structure->typeInfo().type() == StringType;
+ ASSERT(structure()->classInfo() == info());
+ checkOffsetConsistency();
+ if (!propertyTable())
+ materializePropertyMap(vm);
}
- inline const ClassInfo* JSCell::classInfo() const
+ void setPreviousID(VM& vm, Structure* structure)
{
-#if ENABLE(GC_VALIDATION)
- return m_structure.unvalidatedGet()->classInfo();
-#else
- return m_structure->classInfo();
-#endif
+ if (hasRareData())
+ rareData()->setPreviousID(vm, structure);
+ else
+ m_previousOrRareData.set(vm, this, structure);
}
- inline Structure* JSCell::createDummyStructure(JSGlobalData& globalData)
+ void clearPreviousID()
{
- return Structure::create(globalData, jsNull(), TypeInfo(UnspecifiedType), AnonymousSlotCount, &s_dummyCellInfo);
+ if (hasRareData())
+ rareData()->clearPreviousID();
+ else
+ m_previousOrRareData.clear();
}
- inline bool JSValue::needsThisConversion() const
+ int transitionCount() const
{
- if (UNLIKELY(!isCell()))
- return true;
- return asCell()->structure()->typeInfo().needsThisConversion();
+ // 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);
}
- ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell)
+ bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const;
+ bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
+
+ void pin();
+
+ Structure* previous() const
{
- ASSERT(!m_isCheckingForDefaultMarkViolation);
- ASSERT(cell);
- if (Heap::testAndSetMarked(cell))
- return;
- if (cell->structure() && cell->structure()->typeInfo().type() >= CompoundType)
- m_values.append(cell);
+ ASSERT(!hasRareData());
+ return static_cast<Structure*>(m_previousOrRareData.get());
}
- inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure)
+ StructureRareData* rareData() const
{
- // Newer versions of the STL have an std::make_pair function that takes rvalue references.
- // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
- // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details.
- return Hash::Key(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious);
+ ASSERT(hasRareData());
+ return static_cast<StructureRareData*>(m_previousOrRareData.get());
}
+
+ bool checkOffsetConsistency() const;
+
+ JS_EXPORT_PRIVATE void allocateRareData(VM&);
+
+ void startWatchingInternalProperties(VM&);
+
+ static const int s_maxTransitionLength = 64;
+ static const int s_maxTransitionLengthForNonEvalPutById = 512;
+
+ // These need to be properly aligned at the beginning of the 'Structure'
+ // part of the object.
+ StructureIDBlob m_blob;
+ TypeInfo::OutOfLineTypeFlags m_outOfLineTypeFlags;
+
+ WriteBarrier<JSGlobalObject> m_globalObject;
+ WriteBarrier<Unknown> m_prototype;
+ mutable WriteBarrier<StructureChain> m_cachedPrototypeChain;
+
+ WriteBarrier<JSCell> m_previousOrRareData;
+
+ RefPtr<UniquedStringImpl> m_nameInPrevious;
+
+ const ClassInfo* m_classInfo;
+
+ StructureTransitionTable m_transitionTable;
+
+ // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread.
+ WriteBarrier<PropertyTable> m_propertyTableUnsafe;
+
+ mutable InlineWatchpointSet m_transitionWatchpointSet;
+
+ COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits);
+
+ // m_offset does not account for anonymous slots
+ PropertyOffset m_offset;
+
+ uint8_t m_inlineCapacity;
+
+ ConcurrentJITLock m_lock;
+
+ uint32_t m_bitField;
+};
} // namespace JSC