X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174..ed1e77d3adeb83d26fd1dfb16dd84cabdcefd250:/runtime/Structure.h diff --git a/runtime/Structure.h b/runtime/Structure.h index 00bc761..25a97fe 100644 --- a/runtime/Structure.h +++ b/runtime/Structure.h @@ -1,5 +1,5 @@ /* - * 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 @@ -10,10 +10,10 @@ * 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 @@ -27,405 +27,596 @@ #define Structure_h #include "ClassInfo.h" -#include "Identifier.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 +#include "WriteBarrierInlines.h" +#include #include +#include #include +namespace WTF { -namespace JSC { - - class LLIntOffsetsExtractor; - class PropertyNameArray; - class PropertyNameArrayData; - class StructureChain; - class SlotVisitor; - class JSString; - - class Structure : public JSCell { - public: - friend class StructureTransitionTable; - - typedef JSCell Base; - - static Structure* create(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo) - { - ASSERT(globalData.structureStructure); - ASSERT(classInfo); - Structure* structure = new (NotNull, allocateCell(globalData.heap)) Structure(globalData, globalObject, prototype, typeInfo, classInfo); - structure->finishCreation(globalData); - return structure; - } +class UniquedStringImpl; - protected: - void finishCreation(JSGlobalData& globalData) - { - Base::finishCreation(globalData); - ASSERT(m_prototype); - ASSERT(m_prototype.isObject() || m_prototype.isNull()); - } - - void finishCreation(JSGlobalData& globalData, CreatingEarlyCellTag) - { - Base::finishCreation(globalData, this, CreatingEarlyCell); - ASSERT(m_prototype); - ASSERT(m_prototype.isNull()); - ASSERT(!globalData.structureStructure); - } +} // namespace WTF - public: - static void dumpStatistics(); - - JS_EXPORT_PRIVATE static Structure* addPropertyTransition(JSGlobalData&, Structure*, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset); - JS_EXPORT_PRIVATE 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); - JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(JSGlobalData&, Structure*, JSValue prototype); - JS_EXPORT_PRIVATE static Structure* despecifyFunctionTransition(JSGlobalData&, Structure*, const Identifier&); - static Structure* attributeChangeTransition(JSGlobalData&, Structure*, const Identifier& propertyName, unsigned attributes); - 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; } - bool shouldGrowPropertyStorage() { return propertyStorageCapacity() == propertyStorageSize(); } - JS_EXPORT_PRIVATE size_t suggestedNewPropertyStorageSize(); - - Structure* flattenDictionaryStructure(JSGlobalData&, JSObject*); - - static void destroy(JSCell*); - - // These should be used with caution. - JS_EXPORT_PRIVATE 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; } - - // Type accessors. - const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == &s_info); return m_typeInfo; } - bool isObject() const { return typeInfo().isObject(); } - - - JSGlobalObject* globalObject() const { return m_globalObject.get(); } - void setGlobalObject(JSGlobalData& globalData, JSGlobalObject* globalObject) { m_globalObject.set(globalData, this, globalObject); } - - JSValue storedPrototype() const { return m_prototype.get(); } - JSValue prototypeForLookup(ExecState*) const; - StructureChain* prototypeChain(ExecState*) const; - static void visitChildren(JSCell*, SlotVisitor&); - - Structure* previousID() const { ASSERT(structure()->classInfo() == &s_info); return m_previous.get(); } - bool transitivelyTransitionedFrom(Structure* structureToFind); - - void growPropertyStorageCapacity(); - unsigned propertyStorageCapacity() const { ASSERT(structure()->classInfo() == &s_info); return m_propertyStorageCapacity; } - unsigned propertyStorageSize() const { ASSERT(structure()->classInfo() == &s_info); return (m_propertyTable ? m_propertyTable->propertyStorageSize() : static_cast(m_offset + 1)); } - bool isUsingInlineStorage() const; - - size_t get(JSGlobalData&, const Identifier& propertyName); - size_t get(JSGlobalData&, const UString& name); - JS_EXPORT_PRIVATE 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); - } - - 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; - } - void setContainsReadOnlyProperties() - { - m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; - } - - bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } - - bool isEmpty() const { return m_propertyTable ? m_propertyTable->isEmpty() : m_offset == noOffset; } +namespace JSC { - JS_EXPORT_PRIVATE void despecifyDictionaryFunction(JSGlobalData&, const Identifier& propertyName); - void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } +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) + { + } +}; - void setEnumerationCache(JSGlobalData&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. - JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. - void getPropertyNamesFromStructure(JSGlobalData&, PropertyNameArray&, EnumerationMode); +class Structure final : public JSCell { +public: + friend class StructureTransitionTable; - JSString* objectToStringValue() { return m_objectToStringValue.get(); } + 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 setObjectToStringValue(JSGlobalData& globalData, const JSCell* owner, JSString* value) - { - m_objectToStringValue.set(globalData, owner, value); - } + ~Structure(); - bool staticFunctionsReified() - { - return m_staticFunctionReified; - } +protected: + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(m_prototype); + ASSERT(m_prototype.isObject() || m_prototype.isNull()); + } - void setStaticFunctionsReified() - { - m_staticFunctionReified = true; - } + void finishCreation(VM& vm, CreatingEarlyCellTag) + { + Base::finishCreation(vm, this, CreatingEarlyCell); + ASSERT(m_prototype); + ASSERT(m_prototype.isNull()); + ASSERT(!vm.structureStructure); + } - const ClassInfo* classInfo() const { return m_classInfo; } +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 prototypeOffset() - { - return OBJECT_OFFSETOF(Structure, m_prototype); - } + bool isProxy() const + { + JSType type = m_blob.type(); + return type == ImpureProxyType || type == PureForwardingProxyType; + } - static ptrdiff_t typeInfoFlagsOffset() - { - return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset(); - } + 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); } + + 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(); + } - static ptrdiff_t typeInfoTypeOffset() - { - return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset(); - } + // Type accessors. + TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); } + bool isObject() const { return typeInfo().isObject(); } - static Structure* createStructure(JSGlobalData& globalData) - { - ASSERT(!globalData.structureStructure); - Structure* structure = new (NotNull, allocateCell(globalData.heap)) Structure(globalData); - structure->finishCreation(globalData, CreatingEarlyCell); - return structure; - } + IndexingType indexingType() const { return m_blob.indexingType() & AllArrayTypes; } + IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingType(); } - static JS_EXPORTDATA const ClassInfo s_info; - - private: - friend class LLIntOffsetsExtractor; + bool mayInterceptIndexedAccesses() const + { + return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors); + } - JS_EXPORT_PRIVATE Structure(JSGlobalData&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*); - Structure(JSGlobalData&); - Structure(JSGlobalData&, const Structure*); - - static Structure* create(JSGlobalData& globalData, const Structure* structure) - { - ASSERT(globalData.structureStructure); - Structure* newStructure = new (NotNull, allocateCell(globalData.heap)) Structure(globalData, structure); - newStructure->finishCreation(globalData); - return newStructure; - } + bool anyObjectInChainMayInterceptIndexedAccesses() const; + bool holesMustForwardToPrototype(VM&) const; - 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 copyPropertyTable(JSGlobalData&, Structure* owner); - PassOwnPtr copyPropertyTableForPinning(JSGlobalData&, Structure* owner); - JS_EXPORT_PRIVATE void materializePropertyMap(JSGlobalData&); - void materializePropertyMapIfNecessary(JSGlobalData& globalData) - { - ASSERT(structure()->classInfo() == &s_info); - if (!m_propertyTable && m_previous) - materializePropertyMap(globalData); - } - void materializePropertyMapIfNecessaryForPinning(JSGlobalData& globalData) - { - ASSERT(structure()->classInfo() == &s_info); - if (!m_propertyTable) - materializePropertyMap(globalData); - } - - int 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 isValid(ExecState*, StructureChain* cachedPrototypeChain) 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&); - void pin(); + // 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); - static const int s_maxTransitionLength = 64; + unsigned outOfLineCapacity() const + { + ASSERT(checkOffsetConsistency()); + + unsigned outOfLineSize = this->outOfLineSize(); - static const int noOffset = -1; + if (!outOfLineSize) + return 0; - static const unsigned maxSpecificFunctionThrashCount = 3; + if (outOfLineSize <= initialOutOfLineCapacity) + return initialOutOfLineCapacity; - TypeInfo m_typeInfo; - - WriteBarrier m_globalObject; - WriteBarrier m_prototype; - mutable WriteBarrier m_cachedPrototypeChain; + 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(m_offset + 1, m_inlineCapacity); + } + unsigned totalStorageSize() const + { + return numberOfSlotsForLastOffset(m_offset, m_inlineCapacity); + } + unsigned totalStorageCapacity() const + { + ASSERT(structure()->classInfo() == info()); + return outOfLineCapacity() + inlineCapacity(); + } - WriteBarrier m_previous; - RefPtr m_nameInPrevious; - WriteBarrier m_specificValueInPrevious; + 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); - const ClassInfo* m_classInfo; + PropertyOffset get(VM&, PropertyName); + PropertyOffset get(VM&, PropertyName, unsigned& attributes); - StructureTransitionTable m_transitionTable; + // 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 + void forEachPropertyConcurrently(const Functor&); + + PropertyOffset getConcurrently(UniquedStringImpl* uid); + PropertyOffset getConcurrently(UniquedStringImpl* uid, unsigned& attributes); + + Vector 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 m_enumerationCache; + void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*); + JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const; + bool canCachePropertyNameEnumerator() const; + bool canAccessPropertiesQuickly() const; - OwnPtr m_propertyTable; + void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode); - uint32_t m_propertyStorageCapacity; + JSString* objectToStringValue() + { + if (!hasRareData()) + return 0; + return rareData()->objectToStringValue(); + } - WriteBarrier m_objectToStringValue; + void setObjectToStringValue(VM& vm, JSString* value) + { + if (!hasRareData()) + allocateRareData(vm); + rareData()->setObjectToStringValue(vm, value); + } - // m_offset does not account for anonymous slots - int m_offset; + const ClassInfo* classInfo() const { return m_classInfo; } - unsigned m_dictionaryKind : 2; - bool m_isPinnedPropertyTable : 1; - bool m_hasGetterSetterProperties : 1; - bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1; - bool m_hasNonEnumerableProperties : 1; - unsigned m_attributesInPrevious : 7; - unsigned m_specificFunctionThrashCount : 2; - unsigned m_preventExtensions : 1; - unsigned m_didTransition : 1; - unsigned m_staticFunctionReified; - }; + static ptrdiff_t structureIDOffset() + { + return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset(); + } - inline size_t Structure::get(JSGlobalData& globalData, const Identifier& propertyName) + static ptrdiff_t prototypeOffset() { - ASSERT(structure()->classInfo() == &s_info); - materializePropertyMapIfNecessary(globalData); - if (!m_propertyTable) - return notFound; + return OBJECT_OFFSETOF(Structure, m_prototype); + } - PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first; - return entry ? entry->offset : notFound; + static ptrdiff_t globalObjectOffset() + { + return OBJECT_OFFSETOF(Structure, m_globalObject); } - inline size_t Structure::get(JSGlobalData& globalData, const UString& name) + static ptrdiff_t classInfoOffset() { - ASSERT(structure()->classInfo() == &s_info); - materializePropertyMapIfNecessary(globalData); - if (!m_propertyTable) - return notFound; + return OBJECT_OFFSETOF(Structure, m_classInfo); + } + + static ptrdiff_t indexingTypeOffset() + { + return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset(); + } - PropertyMapEntry* entry = m_propertyTable->findWithString(name.impl()).first; - return entry ? entry->offset : notFound; + static Structure* createStructure(VM&); + + bool transitionWatchpointSetHasBeenInvalidated() const + { + return m_transitionWatchpointSet.hasBeenInvalidated(); + } + + bool transitionWatchpointSetIsStillValid() const + { + return m_transitionWatchpointSet.isStillValid(); } - inline bool JSCell::isObject() const + bool dfgShouldWatchIfPossible() const { - return m_structure->isObject(); + // 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; } - - inline bool JSCell::isString() const + + bool dfgShouldWatch() const { - return m_structure->typeInfo().type() == StringType; + return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid(); } - - inline bool JSCell::isGetterSetter() const + + void addTransitionWatchpoint(Watchpoint* watchpoint) const { - return m_structure->typeInfo().type() == GetterSetterType; + ASSERT(transitionWatchpointSetIsStillValid()); + m_transitionWatchpointSet.add(watchpoint); } - - inline bool JSCell::isAPIValueWrapper() const + + void didTransitionFromThisStructure() const; + + InlineWatchpointSet& transitionWatchpointSet() const { - return m_structure->typeInfo().type() == APIValueWrapperType; + return m_transitionWatchpointSet; } - - inline void JSCell::setStructure(JSGlobalData& globalData, Structure* structure) + + 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) { - ASSERT(structure->typeInfo().overridesVisitChildren() == this->structure()->typeInfo().overridesVisitChildren()); - ASSERT(structure->classInfo() == m_structure->classInfo()); - m_structure.set(globalData, this, structure); + for (Structure* structure = this; structure; structure = structure->storedPrototypeStructure()) + structure->startWatchingInternalPropertiesIfNecessary(vm); } - inline const ClassInfo* JSCell::validatedClassInfo() const + PassRefPtr 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((m_bitField >> offset) & s_##lowerName##Mask); }\ + void set##upperName(type newValue) \ + {\ + m_bitField &= ~(s_##lowerName##Mask << offset);\ + m_bitField |= (newValue & s_##lowerName##Mask) << offset;\ + } + + 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& 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* 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&) { -#if ENABLE(GC_VALIDATION) - ASSERT(m_structure.unvalidatedGet()->classInfo() == m_classInfo); -#else - ASSERT(m_structure->classInfo() == m_classInfo); -#endif - return m_classInfo; + ASSERT(structure()->classInfo() == info()); + checkOffsetConsistency(); + if (!propertyTable()) + materializePropertyMap(vm); } - ALWAYS_INLINE void MarkStack::internalAppend(JSCell* cell) + void setPreviousID(VM& vm, Structure* structure) { - ASSERT(!m_isCheckingForDefaultMarkViolation); -#if ENABLE(GC_VALIDATION) - validate(cell); -#endif - m_visitCount++; - if (Heap::testAndSetMarked(cell) || !cell->structure()) - return; - - // Should never attempt to mark something that is zapped. - ASSERT(!cell->isZapped()); - - m_stack.append(cell); + if (hasRareData()) + rareData()->setPreviousID(vm, structure); + else + m_previousOrRareData.set(vm, this, structure); } - inline StructureTransitionTable::Hash::Key StructureTransitionTable::keyForWeakGCMapFinalizer(void*, Structure* structure) + void clearPreviousID() { - // 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); + if (hasRareData()) + rareData()->clearPreviousID(); + else + m_previousOrRareData.clear(); } - inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind) + int transitionCount() const { - for (Structure* current = this; current; current = current->previousID()) { - if (current == structureToFind) - return true; - } - return false; + // 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); } - inline JSCell::JSCell(JSGlobalData& globalData, Structure* structure) - : m_classInfo(structure->classInfo()) - , m_structure(globalData, this, structure) + bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const; + bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; + + void pin(); + + Structure* previous() const { + ASSERT(!hasRareData()); + return static_cast(m_previousOrRareData.get()); } - inline void JSCell::finishCreation(JSGlobalData& globalData, Structure* structure, CreatingEarlyCellTag) + StructureRareData* rareData() const { -#if ENABLE(GC_VALIDATION) - ASSERT(globalData.isInitializingObject()); - globalData.setInitializingObjectClass(0); - if (structure) -#endif - m_structure.setEarlyValue(globalData, this, structure); - m_classInfo = structure->classInfo(); - // Very first set of allocations won't have a real structure. - ASSERT(m_structure || !globalData.structureStructure); + ASSERT(hasRareData()); + return static_cast(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 m_globalObject; + WriteBarrier m_prototype; + mutable WriteBarrier m_cachedPrototypeChain; + + WriteBarrier m_previousOrRareData; + + RefPtr 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 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