Fill out some Poisoned APIs, fix some bugs, and add some tests.
[WebKit-https.git] / Source / JavaScriptCore / runtime / StructureTransitionTable.h
1 /*
2  * Copyright (C) 2008-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #pragma once
27
28 #include "IndexingType.h"
29 #include "JSCPoison.h"
30 #include "WeakGCMap.h"
31 #include <wtf/HashFunctions.h>
32 #include <wtf/text/UniquedStringImpl.h>
33
34 namespace JSC {
35
36 class JSCell;
37 class Structure;
38
39 static const unsigned FirstInternalAttribute = 1 << 6; // Use for transitions that don't have to do with property additions.
40
41 // Support for attributes used to indicate transitions not related to properties.
42 // If any of these are used, the string portion of the key should be 0.
43 enum class NonPropertyTransition : unsigned {
44     AllocateUndecided,
45     AllocateInt32,
46     AllocateDouble,
47     AllocateContiguous,
48     AllocateArrayStorage,
49     AllocateSlowPutArrayStorage,
50     SwitchToSlowPutArrayStorage,
51     AddIndexedAccessors,
52     PreventExtensions,
53     Seal,
54     Freeze
55 };
56
57 inline unsigned toAttributes(NonPropertyTransition transition)
58 {
59     return static_cast<unsigned>(transition) + FirstInternalAttribute;
60 }
61
62 inline bool changesIndexingType(NonPropertyTransition transition)
63 {
64     switch (transition) {
65     case NonPropertyTransition::AllocateUndecided:
66     case NonPropertyTransition::AllocateInt32:
67     case NonPropertyTransition::AllocateDouble:
68     case NonPropertyTransition::AllocateContiguous:
69     case NonPropertyTransition::AllocateArrayStorage:
70     case NonPropertyTransition::AllocateSlowPutArrayStorage:
71     case NonPropertyTransition::SwitchToSlowPutArrayStorage:
72     case NonPropertyTransition::AddIndexedAccessors:
73         return true;
74     default:
75         return false;
76     }
77 }
78
79 inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition transition)
80 {
81     switch (transition) {
82     case NonPropertyTransition::AllocateUndecided:
83         ASSERT(!hasIndexedProperties(oldType));
84         return oldType | UndecidedShape;
85     case NonPropertyTransition::AllocateInt32:
86         ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType));
87         return (oldType & ~IndexingShapeMask) | Int32Shape;
88     case NonPropertyTransition::AllocateDouble:
89         ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType));
90         return (oldType & ~IndexingShapeMask) | DoubleShape;
91     case NonPropertyTransition::AllocateContiguous:
92         ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType));
93         return (oldType & ~IndexingShapeMask) | ContiguousShape;
94     case NonPropertyTransition::AllocateArrayStorage:
95         ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType));
96         return (oldType & ~IndexingShapeMask) | ArrayStorageShape;
97     case NonPropertyTransition::AllocateSlowPutArrayStorage:
98         ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType));
99         return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape;
100     case NonPropertyTransition::SwitchToSlowPutArrayStorage:
101         ASSERT(hasArrayStorage(oldType));
102         return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape;
103     case NonPropertyTransition::AddIndexedAccessors:
104         return oldType | MayHaveIndexedAccessors;
105     default:
106         return oldType;
107     }
108 }
109
110 inline bool preventsExtensions(NonPropertyTransition transition)
111 {
112     switch (transition) {
113     case NonPropertyTransition::PreventExtensions:
114     case NonPropertyTransition::Seal:
115     case NonPropertyTransition::Freeze:
116         return true;
117     default:
118         return false;
119     }
120 }
121
122 inline bool setsDontDeleteOnAllProperties(NonPropertyTransition transition)
123 {
124     switch (transition) {
125     case NonPropertyTransition::Seal:
126     case NonPropertyTransition::Freeze:
127         return true;
128     default:
129         return false;
130     }
131 }
132
133 inline bool setsReadOnlyOnNonAccessorProperties(NonPropertyTransition transition)
134 {
135     switch (transition) {
136     case NonPropertyTransition::Freeze:
137         return true;
138     default:
139         return false;
140     }
141 }
142
143 class StructureTransitionTable {
144     static const intptr_t UsingSingleSlotFlag = 1;
145
146     
147     struct Hash {
148         typedef std::pair<UniquedStringImpl*, unsigned> Key;
149         
150         static unsigned hash(const Key& p)
151         {
152             return PtrHash<UniquedStringImpl*>::hash(p.first) + p.second;
153         }
154
155         static bool equal(const Key& a, const Key& b)
156         {
157             return a == b;
158         }
159
160         static const bool safeToCompareToEmptyOrDeleted = true;
161     };
162
163     typedef WeakGCMap<Hash::Key, Structure, Hash> TransitionMap;
164
165 public:
166     StructureTransitionTable()
167         : m_data(UsingSingleSlotFlag)
168     {
169     }
170
171     ~StructureTransitionTable()
172     {
173         if (!isUsingSingleSlot()) {
174             delete map();
175             return;
176         }
177
178         WeakImpl* impl = this->weakImpl();
179         if (!impl)
180             return;
181         WeakSet::deallocate(impl);
182     }
183
184     void add(VM&, Structure*);
185     bool contains(UniquedStringImpl*, unsigned attributes) const;
186     Structure* get(UniquedStringImpl*, unsigned attributes) const;
187
188 private:
189     friend class SingleSlotTransitionWeakOwner;
190     using PoisonedTransitionMapPtr = ConstExprPoisoned<TransitionMapPoison, TransitionMap*>;
191     using PoisonedWeakImplPtr = ConstExprPoisoned<WeakImplPoison, WeakImpl*>;
192
193     bool isUsingSingleSlot() const
194     {
195         return m_data & UsingSingleSlotFlag;
196     }
197
198     TransitionMap* map() const
199     {
200         ASSERT(!isUsingSingleSlot());
201         return PoisonedTransitionMapPtr(m_data).unpoisoned();
202     }
203
204     WeakImpl* weakImpl() const
205     {
206         ASSERT(isUsingSingleSlot());
207         return PoisonedWeakImplPtr(m_data & ~UsingSingleSlotFlag).unpoisoned();
208     }
209
210     void setMap(TransitionMap* map)
211     {
212         ASSERT(isUsingSingleSlot());
213         
214         if (WeakImpl* impl = this->weakImpl())
215             WeakSet::deallocate(impl);
216
217         // This implicitly clears the flag that indicates we're using a single transition
218         m_data = PoisonedTransitionMapPtr(map).bits();
219
220         ASSERT(!isUsingSingleSlot());
221     }
222
223     Structure* singleTransition() const;
224     void setSingleTransition(Structure*);
225
226     intptr_t m_data;
227 };
228
229 } // namespace JSC