/* * Copyright (C) 2008-2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * 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 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 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 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "IndexingType.h" #include "WeakGCMap.h" #include #include namespace JSC { class JSCell; class Structure; using TransitionPropertyAttributes = uint8_t; enum class TransitionKind : uint8_t { Unknown, PropertyAddition, PropertyDeletion, PropertyAttributeChange, // Support for transitions not related to properties. // If any of these are used, the string portion of the key should be 0. AllocateUndecided, AllocateInt32, AllocateDouble, AllocateContiguous, AllocateArrayStorage, AllocateSlowPutArrayStorage, SwitchToSlowPutArrayStorage, AddIndexedAccessors, PreventExtensions, Seal, Freeze }; static constexpr auto FirstNonPropertyTransitionKind = TransitionKind::AllocateUndecided; inline bool changesIndexingType(TransitionKind transition) { switch (transition) { case TransitionKind::AllocateUndecided: case TransitionKind::AllocateInt32: case TransitionKind::AllocateDouble: case TransitionKind::AllocateContiguous: case TransitionKind::AllocateArrayStorage: case TransitionKind::AllocateSlowPutArrayStorage: case TransitionKind::SwitchToSlowPutArrayStorage: case TransitionKind::AddIndexedAccessors: return true; default: return false; } } inline IndexingType newIndexingType(IndexingType oldType, TransitionKind transition) { switch (transition) { case TransitionKind::AllocateUndecided: ASSERT(!hasIndexedProperties(oldType)); return oldType | UndecidedShape; case TransitionKind::AllocateInt32: ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || oldType == CopyOnWriteArrayWithInt32); return (oldType & ~IndexingShapeAndWritabilityMask) | Int32Shape; case TransitionKind::AllocateDouble: ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || oldType == CopyOnWriteArrayWithDouble); return (oldType & ~IndexingShapeAndWritabilityMask) | DoubleShape; case TransitionKind::AllocateContiguous: ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || oldType == CopyOnWriteArrayWithContiguous); return (oldType & ~IndexingShapeAndWritabilityMask) | ContiguousShape; case TransitionKind::AllocateArrayStorage: ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType)); return (oldType & ~IndexingShapeAndWritabilityMask) | ArrayStorageShape; case TransitionKind::AllocateSlowPutArrayStorage: ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType)); return (oldType & ~IndexingShapeAndWritabilityMask) | SlowPutArrayStorageShape; case TransitionKind::SwitchToSlowPutArrayStorage: ASSERT(hasArrayStorage(oldType)); return (oldType & ~IndexingShapeAndWritabilityMask) | SlowPutArrayStorageShape; case TransitionKind::AddIndexedAccessors: return oldType | MayHaveIndexedAccessors; default: return oldType; } } inline bool preventsExtensions(TransitionKind transition) { switch (transition) { case TransitionKind::PreventExtensions: case TransitionKind::Seal: case TransitionKind::Freeze: return true; default: return false; } } inline bool setsDontDeleteOnAllProperties(TransitionKind transition) { switch (transition) { case TransitionKind::Seal: case TransitionKind::Freeze: return true; default: return false; } } inline bool setsReadOnlyOnNonAccessorProperties(TransitionKind transition) { switch (transition) { case TransitionKind::Freeze: return true; default: return false; } } class StructureTransitionTable { static constexpr intptr_t UsingSingleSlotFlag = 1; #if CPU(ADDRESS64) struct Hash { // Logically, Key is a tuple of (1) UniquedStringImpl*, (2) unsigned attributes, and (3) transitionKind. struct Key { friend struct Hash; static_assert(WTF_OS_CONSTANT_EFFECTIVE_ADDRESS_WIDTH <= 48); static constexpr unsigned attributesShift = 48; static constexpr unsigned transitionKindShift = 56; static constexpr uintptr_t stringMask = (1ULL << attributesShift) - 1; static constexpr uintptr_t hashTableDeletedValue = 0x2; static_assert(sizeof(TransitionPropertyAttributes) * 8 <= 8); static_assert(sizeof(TransitionKind) * 8 <= 8); static_assert(hashTableDeletedValue < alignof(UniquedStringImpl)); // Highest 8 bits are for TransitionKind; next 8 belong to TransitionPropertyAttributes. // Remaining bits are for UniquedStringImpl*. Key(UniquedStringImpl* impl, unsigned attributes, TransitionKind transitionKind) : m_encodedData(bitwise_cast(impl) | (static_cast(attributes) << attributesShift) | (static_cast(transitionKind) << transitionKindShift)) { ASSERT(impl == this->impl()); ASSERT(attributes <= UINT8_MAX); ASSERT(attributes == this->attributes()); ASSERT(transitionKind != TransitionKind::Unknown); ASSERT(transitionKind == this->transitionKind()); } Key() = default; Key(WTF::HashTableDeletedValueType) : m_encodedData(hashTableDeletedValue) { } bool isHashTableDeletedValue() const { return m_encodedData == hashTableDeletedValue; } UniquedStringImpl* impl() const { return bitwise_cast(m_encodedData & stringMask); } TransitionPropertyAttributes attributes() const { return (m_encodedData >> attributesShift) & UINT8_MAX; } TransitionKind transitionKind() const { return static_cast(m_encodedData >> transitionKindShift); } friend bool operator==(const Key& a, const Key& b) { return a.m_encodedData == b.m_encodedData; } friend bool operator!=(const Key& a, const Key& b) { return a.m_encodedData != b.m_encodedData; } private: uintptr_t m_encodedData { 0 }; }; using KeyTraits = SimpleClassHashTraits; static unsigned hash(const Key& p) { return IntHash::hash(p.m_encodedData); } static bool equal(const Key& a, const Key& b) { return a == b; } static constexpr bool safeToCompareToEmptyOrDeleted = true; }; #else struct Hash { using Key = std::tuple; using KeyTraits = HashTraits; static unsigned hash(const Key& p) { return PtrHash::hash(std::get<0>(p)) + std::get<1>(p) + static_cast(std::get<2>(p)); } static bool equal(const Key& a, const Key& b) { return a == b; } static constexpr bool safeToCompareToEmptyOrDeleted = true; }; #endif typedef WeakGCMap TransitionMap; public: StructureTransitionTable() : m_data(UsingSingleSlotFlag) { } ~StructureTransitionTable() { if (!isUsingSingleSlot()) { delete map(); return; } WeakImpl* impl = this->weakImpl(); if (!impl) return; WeakSet::deallocate(impl); } void add(VM&, Structure*); bool contains(UniquedStringImpl*, unsigned attributes, TransitionKind) const; Structure* get(UniquedStringImpl*, unsigned attributes, TransitionKind) const; private: friend class SingleSlotTransitionWeakOwner; bool isUsingSingleSlot() const { return m_data & UsingSingleSlotFlag; } TransitionMap* map() const { ASSERT(!isUsingSingleSlot()); return bitwise_cast(m_data); } WeakImpl* weakImpl() const { ASSERT(isUsingSingleSlot()); return bitwise_cast(m_data & ~UsingSingleSlotFlag); } void setMap(TransitionMap* map) { ASSERT(isUsingSingleSlot()); if (WeakImpl* impl = this->weakImpl()) WeakSet::deallocate(impl); // This implicitly clears the flag that indicates we're using a single transition m_data = bitwise_cast(map); ASSERT(!isUsingSingleSlot()); } Structure* singleTransition() const; void setSingleTransition(Structure*); intptr_t m_data; }; } // namespace JSC