diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/Structure.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/Structure.h | 617 |
1 files changed, 444 insertions, 173 deletions
diff --git a/Source/JavaScriptCore/runtime/Structure.h b/Source/JavaScriptCore/runtime/Structure.h index c73e8cb96..b0f43b700 100644 --- a/Source/JavaScriptCore/runtime/Structure.h +++ b/Source/JavaScriptCore/runtime/Structure.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2012-2016 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 @@ -23,12 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Structure_h -#define Structure_h +#pragma once #include "ClassInfo.h" -#include "ConcurrentJITLock.h" +#include "ConcurrentJSLock.h" #include "IndexingType.h" +#include "InferredTypeTable.h" #include "JSCJSValue.h" #include "JSCell.h" #include "JSType.h" @@ -37,17 +37,22 @@ #include "PropertyOffset.h" #include "Protect.h" #include "PutPropertySlot.h" +#include "StructureIDBlob.h" #include "StructureRareData.h" +#include "StructureRareDataInlines.h" #include "StructureTransitionTable.h" #include "JSTypeInfo.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> +namespace WTF { + +class UniquedStringImpl; + +} // namespace WTF namespace JSC { @@ -57,6 +62,7 @@ class PropertyNameArray; class PropertyNameArrayData; class PropertyTable; class StructureChain; +class StructureShape; class SlotVisitor; class JSString; struct DumpContext; @@ -70,14 +76,66 @@ static const unsigned initialOutOfLineCapacity = 4; // initial allocation. static const unsigned outOfLineGrowthFactor = 2; -class Structure : public JSCell { +struct PropertyMapEntry { + UniquedStringImpl* key; + PropertyOffset offset; + uint8_t attributes; + bool hasInferredType; // This caches whether or not a property has an inferred type in the inferred type table, and is used for a fast check in JSObject::putDirectInternal(). + + PropertyMapEntry() + : key(nullptr) + , offset(invalidOffset) + , attributes(0) + , hasInferredType(false) + { + } + + PropertyMapEntry(UniquedStringImpl* key, PropertyOffset offset, unsigned attributes) + : key(key) + , offset(offset) + , attributes(attributes) + , hasInferredType(false) + { + ASSERT(this->attributes == attributes); + } +}; + +class StructureFireDetail : public FireDetail { +public: + StructureFireDetail(const Structure* structure) + : m_structure(structure) + { + } + + void dump(PrintStream& out) const override; + +private: + const Structure* m_structure; +}; + +class DeferredStructureTransitionWatchpointFire { + WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire); +public: + JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(); + JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire(); + + void add(const Structure*); + +private: + const Structure* m_structure; +}; + +class Structure final : public JSCell { public: friend class StructureTransitionTable; 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); + ~Structure(); + protected: void finishCreation(VM& vm) { @@ -95,71 +153,106 @@ protected: } public: + StructureID id() const { return m_blob.structureID(); } + int32_t objectInitializationBlob() const { return m_blob.blobExcludingStructureID(); } + int64_t idBlob() const { return m_blob.blob(); } + + bool isProxy() const + { + JSType type = m_blob.type(); + return type == ImpureProxyType || type == PureForwardingProxyType; + } + 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&); + JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&); + JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr); + 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* despecifyFunctionTransition(VM&, Structure*, PropertyName); - static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes); - static Structure* toCacheableDictionaryTransition(VM&, Structure*); + JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes); + JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr); static Structure* toUncacheableDictionaryTransition(VM&, Structure*); - static Structure* sealTransition(VM&, Structure*); - static Structure* freezeTransition(VM&, Structure*); + JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*); + JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*); static Structure* preventExtensionsTransition(VM&, Structure*); - static Structure* nonPropertyTransition(VM&, Structure*, NonPropertyTransition); + JS_EXPORT_PRIVATE 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(); - JS_EXPORT_PRIVATE size_t suggestedNewOutOfLineStorageCapacity(); + JS_EXPORT_PRIVATE bool isSealed(VM&); + JS_EXPORT_PRIVATE bool isFrozen(VM&); + bool isStructureExtensible() const { return !didPreventExtensions(); } - Structure* flattenDictionaryStructure(VM&, JSObject*); + 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); + // Versions that take a func will call it after making the change but while still holding + // the lock. The callback is not called if there is no change being made, like if you call + // removePropertyWithoutTransition() and the property is not found. + template<typename Func> + PropertyOffset addPropertyWithoutTransition(VM&, PropertyName, unsigned attributes, const Func&); + template<typename Func> + PropertyOffset removePropertyWithoutTransition(VM&, PropertyName, const Func&); 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 dictionaryKind() != NoneDictionaryKind; } + bool isUncacheableDictionary() const { return dictionaryKind() == UncachedDictionaryKind; } + + bool propertyAccessesAreCacheable() + { + return dictionaryKind() != UncachedDictionaryKind + && !typeInfo().prohibitsPropertyCaching() + && !(typeInfo().getOwnPropertySlotIsImpure() && !typeInfo().newImpurePropertyFiresWatchpoints()); + } + + bool propertyAccessesAreCacheableForAbsence() + { + return !typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence(); + } + + bool needImpurePropertyWatchpoint() + { + return propertyAccessesAreCacheable() + && typeInfo().getOwnPropertySlotIsImpure() + && typeInfo().newImpurePropertyFiresWatchpoints(); + } - bool propertyAccessesAreCacheable() { return m_dictionaryKind != UncachedDictionaryKind && !typeInfo().prohibitsPropertyCaching(); } + bool isImmutablePrototypeExoticObject() + { + return typeInfo().isImmutablePrototypeExoticObject(); + } // 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() { - ASSERT(!typeInfo().hasImpureGetOwnPropertySlot() || typeInfo().newImpurePropertyFiresWatchpoints()); - return typeInfo().hasImpureGetOwnPropertySlot(); + return typeInfo().getOwnPropertySlotIsImpure(); } - + // Type accessors. - const TypeInfo& typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_typeInfo; } + TypeInfo typeInfo() const { ASSERT(structure()->classInfo() == info()); return m_blob.typeInfo(m_outOfLineTypeFlags); } bool isObject() const { return typeInfo().isObject(); } - IndexingType indexingType() const { return m_indexingType & AllArrayTypes; } - IndexingType indexingTypeIncludingHistory() const { return m_indexingType; } + IndexingType indexingType() const { return m_blob.indexingTypeIncludingHistory() & AllArrayTypes; } + IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingTypeIncludingHistory(); } bool mayInterceptIndexedAccesses() const { return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors); } - bool anyObjectInChainMayInterceptIndexedAccesses() const; + JS_EXPORT_PRIVATE bool anyObjectInChainMayInterceptIndexedAccesses() const; + bool holesMustForwardToPrototype(VM&) const; bool needsSlowPutIndexing() const; NonPropertyTransition suggestedArrayStorageTransition() const; JSGlobalObject* globalObject() const { return m_globalObject.get(); } + + // NOTE: This method should only be called during the creation of structures, since the global + // object of a structure is presumed to be immutable in a bunch of places. void setGlobalObject(VM& vm, JSGlobalObject* globalObject) { m_globalObject.set(vm, this, globalObject); } JSValue storedPrototype() const { return m_prototype.get(); } @@ -171,27 +264,51 @@ public: StructureChain* prototypeChain(VM&, JSGlobalObject*) const; StructureChain* prototypeChain(ExecState*) const; static void visitChildren(JSCell*, SlotVisitor&); + + // A Structure is cheap to mark during GC if doing so would only add a small and bounded amount + // to our heap footprint. For example, if the structure refers to a global object that is not + // yet marked, then as far as we know, the decision to mark this Structure would lead to a large + // increase in footprint because no other object refers to that global object. This method + // returns true if all user-controlled (and hence unbounded in size) objects referenced from the + // Structure are already marked. + bool isCheapDuringGC(); + + // Returns true if this structure is now marked. + bool markIfCheap(SlotVisitor&); // Will just the prototype chain intercept this property access? - bool prototypeChainMayInterceptStoreTo(VM&, PropertyName); - - bool transitionDidInvolveSpecificValue() const { return !!m_specificValueInPrevious; } - + JS_EXPORT_PRIVATE bool prototypeChainMayInterceptStoreTo(VM&, PropertyName); + + bool hasRareData() const + { + return isRareData(m_previousOrRareData.get()); + } + Structure* previousID() const { ASSERT(structure()->classInfo() == info()); - if (typeInfo().structureHasRareData()) - return rareData()->previousID(); - return previous(); + // This is so written because it's used concurrently. We only load from m_previousOrRareData + // once, and this load is guaranteed atomic. + JSCell* cell = m_previousOrRareData.get(); + if (isRareData(cell)) + return static_cast<StructureRareData*>(cell)->previousID(); + return static_cast<Structure*>(cell); } bool transitivelyTransitionedFrom(Structure* structureToFind); + + PropertyOffset lastOffset() const { return m_offset; } + + void setLastOffset(PropertyOffset offset) { m_offset = offset; } - unsigned outOfLineCapacity() const + static unsigned outOfLineCapacity(PropertyOffset lastOffset) { - ASSERT(checkOffsetConsistency()); - - unsigned outOfLineSize = this->outOfLineSize(); + unsigned outOfLineSize = Structure::outOfLineSize(lastOffset); + // This algorithm completely determines the out-of-line property storage growth algorithm. + // The JSObject code will only trigger a resize if the value returned by this algorithm + // changed between the new and old structure. So, it's important to keep this simple because + // it's on a fast path. + if (!outOfLineSize) return 0; @@ -202,12 +319,19 @@ public: COMPILE_ASSERT(outOfLineGrowthFactor == 2, outOfLineGrowthFactor_is_two); return WTF::roundUpToPowerOfTwo(outOfLineSize); } + + static unsigned outOfLineSize(PropertyOffset lastOffset) + { + return numberOfOutOfLineSlotsForLastOffset(lastOffset); + } + + unsigned outOfLineCapacity() const + { + return outOfLineCapacity(m_offset); + } unsigned outOfLineSize() const { - ASSERT(checkOffsetConsistency()); - ASSERT(structure()->classInfo() == info()); - - return numberOfOutOfLineSlotsForLastOffset(m_offset); + return outOfLineSize(m_offset); } bool hasInlineStorage() const { @@ -237,11 +361,16 @@ public: && offset <= m_offset && (offset < m_inlineCapacity || offset >= firstOutOfLineOffset); } + + bool hijacksIndexingHeader() const + { + return isTypedView(m_classInfo->typedArrayStorageType); + } bool couldHaveIndexingHeader() const { return hasIndexedProperties(indexingType()) - || isTypedView(m_classInfo->typedArrayStorageType); + || hijacksIndexingHeader(); } bool hasIndexingHeader(const JSCell*) const; @@ -249,66 +378,66 @@ public: bool masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject); PropertyOffset get(VM&, PropertyName); - PropertyOffset get(VM&, const WTF::String& name); - JS_EXPORT_PRIVATE PropertyOffset get(VM&, PropertyName, unsigned& attributes, JSCell*& specificValue); - - PropertyOffset getConcurrently(VM&, StringImpl* uid); - PropertyOffset getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue); - - bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; } - bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; } - void setHasGetterSetterProperties(bool is__proto__) + PropertyOffset get(VM&, PropertyName, unsigned& attributes); + PropertyOffset get(VM&, PropertyName, unsigned& attributes, bool& hasInferredType); + + // 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__) { - m_hasGetterSetterProperties = true; + setHasGetterSetterProperties(true); if (!is__proto__) - m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); } - void setContainsReadOnlyProperties() + + void setContainsReadOnlyProperties() { setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); } + + void setHasCustomGetterSetterPropertiesWithProtoCheck(bool is__proto__) { - m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; + setHasCustomGetterSetterProperties(true); + if (!is__proto__) + setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); } - - bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; } - + bool isEmpty() const { ASSERT(checkOffsetConsistency()); return !JSC::isValidOffset(m_offset); } - JS_EXPORT_PRIVATE void despecifyDictionaryFunction(VM&, PropertyName); - void disableSpecificFunctionTracking() { m_specificFunctionThrashCount = maxSpecificFunctionThrashCount; } + void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*); + JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const; + bool canCachePropertyNameEnumerator() const; + bool canAccessPropertiesQuicklyForEnumeration() const; - void setEnumerationCache(VM&, JSPropertyNameIterator* enumerationCache); // Defined in JSPropertyNameIterator.h. - JSPropertyNameIterator* enumerationCache(); // Defined in JSPropertyNameIterator.h. void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode); JSString* objectToStringValue() { - if (!typeInfo().structureHasRareData()) + if (!hasRareData()) return 0; return rareData()->objectToStringValue(); } - void setObjectToStringValue(VM& vm, const JSCell* owner, JSString* value) - { - if (!typeInfo().structureHasRareData()) - allocateRareData(vm); - rareData()->setObjectToStringValue(vm, owner, value); - } + void setObjectToStringValue(ExecState*, VM&, JSString* value, PropertySlot toStringTagSymbolSlot); - bool staticFunctionsReified() - { - return m_staticFunctionReified; - } + const ClassInfo* classInfo() const { return m_classInfo; } - void setStaticFunctionsReified() + static ptrdiff_t structureIDOffset() { - m_staticFunctionReified = true; + return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::structureIDOffset(); } - const ClassInfo* classInfo() const { return m_classInfo; } - static ptrdiff_t prototypeOffset() { return OBJECT_OFFSETOF(Structure, m_prototype); @@ -319,24 +448,24 @@ public: return OBJECT_OFFSETOF(Structure, m_globalObject); } - static ptrdiff_t typeInfoFlagsOffset() + static ptrdiff_t classInfoOffset() { - return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::flagsOffset(); + return OBJECT_OFFSETOF(Structure, m_classInfo); } - - static ptrdiff_t typeInfoTypeOffset() + + static ptrdiff_t indexingTypeIncludingHistoryOffset() { - return OBJECT_OFFSETOF(Structure, m_typeInfo) + TypeInfo::typeOffset(); + return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeIncludingHistoryOffset(); } - - static ptrdiff_t classInfoOffset() + + static ptrdiff_t propertyTableUnsafeOffset() { - return OBJECT_OFFSETOF(Structure, m_classInfo); + return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe); } - - static ptrdiff_t indexingTypeOffset() + + static ptrdiff_t inlineCapacityOffset() { - return OBJECT_OFFSETOF(Structure, m_indexingType); + return OBJECT_OFFSETOF(Structure, m_inlineCapacity); } static Structure* createStructure(VM&); @@ -350,41 +479,178 @@ public: { 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 - void addTransitionWatchpoint(Watchpoint* watchpoint) const + // - We don't watch Structures that either decided not to be watched, or whose predecessors + // decided not to be watched. This happens when a transition is fired while being watched. + if (transitionWatchpointIsLikelyToBeFired()) + return false; + + // - Don't watch Structures that had been dictionaries. + if (hasBeenDictionary()) + return false; + + return true; + } + + bool dfgShouldWatch() const { - ASSERT(transitionWatchpointSetIsStillValid()); - m_transitionWatchpointSet.add(watchpoint); + return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid(); } - void notifyTransitionFromThisStructure() const + void addTransitionWatchpoint(Watchpoint* watchpoint) const { - m_transitionWatchpointSet.fireAll(); + ASSERT(transitionWatchpointSetIsStillValid()); + m_transitionWatchpointSet.add(watchpoint); } + void didTransitionFromThisStructure(DeferredStructureTransitionWatchpointFire* = nullptr) const; + InlineWatchpointSet& transitionWatchpointSet() const { 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); + } + + bool hasInferredTypes() const + { + return !!m_inferredTypeTable; + } + + InferredType* inferredTypeFor(UniquedStringImpl* uid) + { + if (InferredTypeTable* table = m_inferredTypeTable.get()) + return table->get(uid); + return nullptr; + } + + InferredType::Descriptor inferredTypeDescriptorFor(UniquedStringImpl* uid) + { + if (InferredType* result = inferredTypeFor(uid)) + return result->descriptor(); + return InferredType::Top; + } + + // Call this when we know that this is a brand new property. Note that it's not enough for the + // property to be brand new to some object. It has to be brand new to the Structure. + ALWAYS_INLINE void willStoreValueForNewTransition( + VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize) + { + if (hasBeenDictionary() || (!shouldOptimize && !m_inferredTypeTable)) + return; + willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::NewProperty); + } + + // Call this when we know that this is a new property for the object, but not new for the + // structure. Therefore, under the InferredTypeTable's rules, absence of the property from the + // table means Top rather than Bottom. + ALWAYS_INLINE void willStoreValueForExistingTransition( + VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize) + { + if (hasBeenDictionary() || !m_inferredTypeTable) + return; + willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::NewProperty); + } + + // Call this when we know that the inferred type table exists and has an entry for this property. + ALWAYS_INLINE void willStoreValueForReplace( + VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize) + { + if (hasBeenDictionary()) + return; + willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::OldProperty); + } + + Ref<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&); + ConcurrentJSLock& lock() { return m_lock; } + 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;\ + } + + 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, isQuickPropertyAccessAllowedForEnumeration, IsQuickPropertyAccessAllowedForEnumeration, 1, 5); + DEFINE_BITFIELD(unsigned, attributesInPrevious, AttributesInPrevious, 14, 6); + DEFINE_BITFIELD(bool, didPreventExtensions, DidPreventExtensions, 1, 20); + DEFINE_BITFIELD(bool, didTransition, DidTransition, 1, 21); + DEFINE_BITFIELD(bool, staticPropertiesReified, StaticPropertiesReified, 1, 22); + DEFINE_BITFIELD(bool, hasBeenFlattenedBefore, HasBeenFlattenedBefore, 1, 23); + DEFINE_BITFIELD(bool, hasCustomGetterSetterProperties, HasCustomGetterSetterProperties, 1, 24); + DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 25); + DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 26); + DEFINE_BITFIELD(bool, hasBeenDictionary, HasBeenDictionary, 1, 27); + DEFINE_BITFIELD(bool, isAddingPropertyForTransition, IsAddingPropertyForTransition, 1, 28); + +private: friend class LLIntOffsetsExtractor; JS_EXPORT_PRIVATE Structure(VM&, JSGlobalObject*, JSValue prototype, const TypeInfo&, const ClassInfo*, IndexingType, unsigned inlineCapacity); Structure(VM&); - Structure(VM&, const Structure*); + Structure(VM&, Structure*, DeferredStructureTransitionWatchpointFire*); - static Structure* create(VM&, const Structure*); + static Structure* create(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr); - static Structure* addPropertyTransitionToExistingStructureImpl(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&); + 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 @@ -392,54 +658,60 @@ private: // 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); + static Structure* toDictionaryTransition(VM&, Structure*, DictionaryKind, DeferredStructureTransitionWatchpointFire* = nullptr); + + enum class ShouldPin { No, Yes }; + template<ShouldPin, typename Func> + PropertyOffset add(VM&, PropertyName, unsigned attributes, const Func&); + PropertyOffset add(VM&, PropertyName, unsigned attributes); + template<typename Func> + PropertyOffset remove(PropertyName, const Func&); 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&, Structure* owner); - PropertyTable* copyPropertyTable(VM&, Structure* owner); - PropertyTable* copyPropertyTableForPinning(VM&, Structure* owner); - JS_EXPORT_PRIVATE void materializePropertyMap(VM&); - void materializePropertyMapIfNecessary(VM& vm, DeferGC&) + // This may grab the lock, or not. Do not call when holding the Structure's lock. + PropertyTable* ensurePropertyTableIfNotEmpty(VM& vm) { - ASSERT(!isCompilationThread()); - ASSERT(structure()->classInfo() == info()); - ASSERT(checkOffsetConsistency()); - if (!propertyTable() && previousID()) - materializePropertyMap(vm); + if (PropertyTable* result = m_propertyTableUnsafe.get()) + return result; + if (!previousID()) + return nullptr; + return materializePropertyTable(vm); } - void materializePropertyMapIfNecessaryForPinning(VM& vm, DeferGC&) + + // This may grab the lock, or not. Do not call when holding the Structure's lock. + PropertyTable* ensurePropertyTable(VM& vm) { - ASSERT(structure()->classInfo() == info()); - checkOffsetConsistency(); - if (!propertyTable()) - materializePropertyMap(vm); + if (PropertyTable* result = m_propertyTableUnsafe.get()) + return result; + return materializePropertyTable(vm); } + + PropertyTable* propertyTableOrNull() const + { + return m_propertyTableUnsafe.get(); + } + + // This will grab the lock. Do not call when holding the Structure's lock. + JS_EXPORT_PRIVATE PropertyTable* materializePropertyTable(VM&, bool setPropertyTable = true); + + void setPropertyTable(VM& vm, PropertyTable* table); + + PropertyTable* takePropertyTableOrCloneIfPinned(VM&); + PropertyTable* copyPropertyTableForPinning(VM&); - void setPreviousID(VM& vm, Structure* transition, Structure* structure) + void setPreviousID(VM& vm, Structure* structure) { - if (typeInfo().structureHasRareData()) - rareData()->setPreviousID(vm, transition, structure); + if (hasRareData()) + rareData()->setPreviousID(vm, structure); else - m_previousOrRareData.set(vm, transition, structure); + m_previousOrRareData.set(vm, this, structure); } void clearPreviousID() { - if (typeInfo().structureHasRareData()) + if (hasRareData()) rareData()->clearPreviousID(); else m_previousOrRareData.clear(); @@ -453,47 +725,59 @@ private: bool isValid(JSGlobalObject*, StructureChain* cachedPrototypeChain) const; bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const; - - void pin(); - Structure* previous() const + // You have to hold the structure lock to do these. + JS_EXPORT_PRIVATE void pin(const AbstractLocker&, VM&, PropertyTable*); + void pinForCaching(const AbstractLocker&, VM&, PropertyTable*); + + bool isRareData(JSCell* cell) const { - ASSERT(!typeInfo().structureHasRareData()); - return static_cast<Structure*>(m_previousOrRareData.get()); + return cell && cell->structureID() != structureID(); } StructureRareData* rareData() const { - ASSERT(typeInfo().structureHasRareData()); + ASSERT(hasRareData()); return static_cast<StructureRareData*>(m_previousOrRareData.get()); } - + + template<typename DetailsFunc> + bool checkOffsetConsistency(PropertyTable*, const DetailsFunc&) const; bool checkOffsetConsistency() const; - void allocateRareData(VM&); - void cloneRareDataFrom(VM&, const Structure*); + JS_EXPORT_PRIVATE void allocateRareData(VM&); + + void startWatchingInternalProperties(VM&); + + JS_EXPORT_PRIVATE void willStoreValueSlow( + VM&, PropertyName, JSValue, bool, InferredTypeTable::StoredPropertyAge); static const int s_maxTransitionLength = 64; static const int s_maxTransitionLengthForNonEvalPutById = 512; - 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; + WriteBarrier<JSGlobalObject> m_globalObject; WriteBarrier<Unknown> m_prototype; mutable WriteBarrier<StructureChain> m_cachedPrototypeChain; WriteBarrier<JSCell> m_previousOrRareData; - RefPtr<StringImpl> m_nameInPrevious; - WriteBarrier<JSCell> m_specificValueInPrevious; + 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. + // Should be accessed through ensurePropertyTable(). During GC, it may be set to 0 by another thread. + // During a Heap Snapshot GC we avoid clearing the table so it is safe to use. WriteBarrier<PropertyTable> m_propertyTableUnsafe; + WriteBarrier<InferredTypeTable> m_inferredTypeTable; + mutable InlineWatchpointSet m_transitionWatchpointSet; COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits); @@ -501,24 +785,11 @@ private: // m_offset does not account for anonymous slots PropertyOffset m_offset; - TypeInfo m_typeInfo; - IndexingType m_indexingType; uint8_t m_inlineCapacity; - ConcurrentJITLock m_lock; - - unsigned m_dictionaryKind : 2; - bool m_isPinnedPropertyTable : 1; - bool m_hasGetterSetterProperties : 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; + ConcurrentJSLock m_lock; + + uint32_t m_bitField; }; } // namespace JSC - -#endif // Structure_h |