1 | /* |
2 | * Copyright (C) 2015-2019 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 "Identifier.h" |
29 | #include <wtf/HashMap.h> |
30 | |
31 | namespace JSC { |
32 | |
33 | struct VariableEnvironmentEntry { |
34 | public: |
35 | ALWAYS_INLINE bool isCaptured() const { return m_bits & IsCaptured; } |
36 | ALWAYS_INLINE bool isConst() const { return m_bits & IsConst; } |
37 | ALWAYS_INLINE bool isVar() const { return m_bits & IsVar; } |
38 | ALWAYS_INLINE bool isLet() const { return m_bits & IsLet; } |
39 | ALWAYS_INLINE bool isExported() const { return m_bits & IsExported; } |
40 | ALWAYS_INLINE bool isImported() const { return m_bits & IsImported; } |
41 | ALWAYS_INLINE bool isImportedNamespace() const { return m_bits & IsImportedNamespace; } |
42 | ALWAYS_INLINE bool isFunction() const { return m_bits & IsFunction; } |
43 | ALWAYS_INLINE bool isParameter() const { return m_bits & IsParameter; } |
44 | ALWAYS_INLINE bool isSloppyModeHoistingCandidate() const { return m_bits & IsSloppyModeHoistingCandidate; } |
45 | |
46 | ALWAYS_INLINE void setIsCaptured() { m_bits |= IsCaptured; } |
47 | ALWAYS_INLINE void setIsConst() { m_bits |= IsConst; } |
48 | ALWAYS_INLINE void setIsVar() { m_bits |= IsVar; } |
49 | ALWAYS_INLINE void setIsLet() { m_bits |= IsLet; } |
50 | ALWAYS_INLINE void setIsExported() { m_bits |= IsExported; } |
51 | ALWAYS_INLINE void setIsImported() { m_bits |= IsImported; } |
52 | ALWAYS_INLINE void setIsImportedNamespace() { m_bits |= IsImportedNamespace; } |
53 | ALWAYS_INLINE void setIsFunction() { m_bits |= IsFunction; } |
54 | ALWAYS_INLINE void setIsParameter() { m_bits |= IsParameter; } |
55 | ALWAYS_INLINE void setIsSloppyModeHoistingCandidate() { m_bits |= IsSloppyModeHoistingCandidate; } |
56 | |
57 | ALWAYS_INLINE void clearIsVar() { m_bits &= ~IsVar; } |
58 | |
59 | uint16_t bits() const { return m_bits; } |
60 | |
61 | bool operator==(const VariableEnvironmentEntry& other) const |
62 | { |
63 | return m_bits == other.m_bits; |
64 | } |
65 | |
66 | private: |
67 | enum Traits : uint16_t { |
68 | IsCaptured = 1 << 0, |
69 | IsConst = 1 << 1, |
70 | IsVar = 1 << 2, |
71 | IsLet = 1 << 3, |
72 | IsExported = 1 << 4, |
73 | IsImported = 1 << 5, |
74 | IsImportedNamespace = 1 << 6, |
75 | IsFunction = 1 << 7, |
76 | IsParameter = 1 << 8, |
77 | IsSloppyModeHoistingCandidate = 1 << 9 |
78 | }; |
79 | uint16_t m_bits { 0 }; |
80 | }; |
81 | |
82 | struct VariableEnvironmentEntryHashTraits : HashTraits<VariableEnvironmentEntry> { |
83 | static constexpr bool needsDestruction = false; |
84 | }; |
85 | |
86 | class VariableEnvironment { |
87 | private: |
88 | typedef HashMap<RefPtr<UniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> Map; |
89 | public: |
90 | VariableEnvironment() = default; |
91 | VariableEnvironment(VariableEnvironment&& other) = default; |
92 | VariableEnvironment(const VariableEnvironment&) = default; |
93 | VariableEnvironment& operator=(const VariableEnvironment&) = default; |
94 | VariableEnvironment& operator=(VariableEnvironment&&) = default; |
95 | |
96 | ALWAYS_INLINE Map::iterator begin() { return m_map.begin(); } |
97 | ALWAYS_INLINE Map::iterator end() { return m_map.end(); } |
98 | ALWAYS_INLINE Map::const_iterator begin() const { return m_map.begin(); } |
99 | ALWAYS_INLINE Map::const_iterator end() const { return m_map.end(); } |
100 | ALWAYS_INLINE Map::AddResult add(const RefPtr<UniquedStringImpl>& identifier) { return m_map.add(identifier, VariableEnvironmentEntry()); } |
101 | ALWAYS_INLINE Map::AddResult add(const Identifier& identifier) { return add(identifier.impl()); } |
102 | ALWAYS_INLINE unsigned size() const { return m_map.size(); } |
103 | ALWAYS_INLINE bool contains(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.contains(identifier); } |
104 | ALWAYS_INLINE bool remove(const RefPtr<UniquedStringImpl>& identifier) { return m_map.remove(identifier); } |
105 | ALWAYS_INLINE Map::iterator find(const RefPtr<UniquedStringImpl>& identifier) { return m_map.find(identifier); } |
106 | ALWAYS_INLINE Map::const_iterator find(const RefPtr<UniquedStringImpl>& identifier) const { return m_map.find(identifier); } |
107 | void swap(VariableEnvironment& other); |
108 | void markVariableAsCapturedIfDefined(const RefPtr<UniquedStringImpl>& identifier); |
109 | void markVariableAsCaptured(const RefPtr<UniquedStringImpl>& identifier); |
110 | void markAllVariablesAsCaptured(); |
111 | bool hasCapturedVariables() const; |
112 | bool captures(UniquedStringImpl* identifier) const; |
113 | void markVariableAsImported(const RefPtr<UniquedStringImpl>& identifier); |
114 | void markVariableAsExported(const RefPtr<UniquedStringImpl>& identifier); |
115 | |
116 | bool isEverythingCaptured() const { return m_isEverythingCaptured; } |
117 | bool isEmpty() const { return !m_map.size(); } |
118 | |
119 | private: |
120 | friend class CachedVariableEnvironment; |
121 | |
122 | Map m_map; |
123 | bool m_isEverythingCaptured { false }; |
124 | }; |
125 | |
126 | class CompactVariableEnvironment { |
127 | WTF_MAKE_FAST_ALLOCATED; |
128 | WTF_MAKE_NONCOPYABLE(CompactVariableEnvironment); |
129 | |
130 | friend class CachedCompactVariableEnvironment; |
131 | |
132 | public: |
133 | CompactVariableEnvironment(const VariableEnvironment&); |
134 | VariableEnvironment toVariableEnvironment() const; |
135 | |
136 | bool operator==(const CompactVariableEnvironment&) const; |
137 | unsigned hash() const { return m_hash; } |
138 | |
139 | private: |
140 | CompactVariableEnvironment() = default; |
141 | |
142 | Vector<RefPtr<UniquedStringImpl>> m_variables; |
143 | Vector<VariableEnvironmentEntry> m_variableMetadata; |
144 | unsigned m_hash; |
145 | bool m_isEverythingCaptured; |
146 | }; |
147 | |
148 | struct CompactVariableMapKey { |
149 | CompactVariableMapKey() |
150 | : m_environment(nullptr) |
151 | { |
152 | ASSERT(isHashTableEmptyValue()); |
153 | } |
154 | |
155 | CompactVariableMapKey(CompactVariableEnvironment& environment) |
156 | : m_environment(&environment) |
157 | { } |
158 | |
159 | static unsigned hash(const CompactVariableMapKey& key) { return key.m_environment->hash(); } |
160 | static bool equal(const CompactVariableMapKey& a, const CompactVariableMapKey& b) { return *a.m_environment == *b.m_environment; } |
161 | static constexpr bool safeToCompareToEmptyOrDeleted = false; |
162 | static void makeDeletedValue(CompactVariableMapKey& key) |
163 | { |
164 | key.m_environment = reinterpret_cast<CompactVariableEnvironment*>(1); |
165 | } |
166 | bool isHashTableDeletedValue() const |
167 | { |
168 | return m_environment == reinterpret_cast<CompactVariableEnvironment*>(1); |
169 | } |
170 | bool isHashTableEmptyValue() const |
171 | { |
172 | return !m_environment; |
173 | } |
174 | |
175 | CompactVariableEnvironment& environment() |
176 | { |
177 | RELEASE_ASSERT(!isHashTableDeletedValue()); |
178 | RELEASE_ASSERT(!isHashTableEmptyValue()); |
179 | return *m_environment; |
180 | } |
181 | |
182 | private: |
183 | CompactVariableEnvironment* m_environment; |
184 | }; |
185 | |
186 | } // namespace JSC |
187 | |
188 | namespace WTF { |
189 | |
190 | template<typename T> struct DefaultHash; |
191 | template<> struct DefaultHash<JSC::CompactVariableMapKey> { |
192 | using Hash = JSC::CompactVariableMapKey; |
193 | }; |
194 | |
195 | template<> struct HashTraits<JSC::CompactVariableMapKey> : GenericHashTraits<JSC::CompactVariableMapKey> { |
196 | static constexpr bool emptyValueIsZero = true; |
197 | static JSC::CompactVariableMapKey emptyValue() { return JSC::CompactVariableMapKey(); } |
198 | |
199 | static constexpr bool hasIsEmptyValueFunction = true; |
200 | static bool isEmptyValue(JSC::CompactVariableMapKey key) { return key.isHashTableEmptyValue(); } |
201 | |
202 | static void constructDeletedValue(JSC::CompactVariableMapKey& key) { JSC::CompactVariableMapKey::makeDeletedValue(key); } |
203 | static bool isDeletedValue(JSC::CompactVariableMapKey key) { return key.isHashTableDeletedValue(); } |
204 | }; |
205 | |
206 | } // namespace WTF |
207 | |
208 | namespace JSC { |
209 | |
210 | class CompactVariableMap : public RefCounted<CompactVariableMap> { |
211 | public: |
212 | class Handle { |
213 | friend class CachedCompactVariableMapHandle; |
214 | |
215 | public: |
216 | Handle() = default; |
217 | |
218 | Handle(CompactVariableEnvironment&, CompactVariableMap&); |
219 | |
220 | Handle(Handle&& other) |
221 | { |
222 | swap(other); |
223 | } |
224 | Handle& operator=(Handle&& other) |
225 | { |
226 | Handle handle(WTFMove(other)); |
227 | swap(handle); |
228 | return *this; |
229 | } |
230 | |
231 | Handle(const Handle&); |
232 | Handle& operator=(const Handle& other) |
233 | { |
234 | Handle handle(other); |
235 | swap(handle); |
236 | return *this; |
237 | } |
238 | |
239 | ~Handle(); |
240 | |
241 | explicit operator bool() const { return !!m_map; } |
242 | |
243 | const CompactVariableEnvironment& environment() const |
244 | { |
245 | return *m_environment; |
246 | } |
247 | |
248 | private: |
249 | void swap(Handle& other) |
250 | { |
251 | std::swap(other.m_environment, m_environment); |
252 | std::swap(other.m_map, m_map); |
253 | } |
254 | |
255 | CompactVariableEnvironment* m_environment { nullptr }; |
256 | RefPtr<CompactVariableMap> m_map; |
257 | }; |
258 | |
259 | Handle get(const VariableEnvironment&); |
260 | |
261 | private: |
262 | friend class Handle; |
263 | friend class CachedCompactVariableMapHandle; |
264 | |
265 | Handle get(CompactVariableEnvironment*, bool& isNewEntry); |
266 | |
267 | HashMap<CompactVariableMapKey, unsigned> m_map; |
268 | }; |
269 | |
270 | } // namespace JSC |
271 | |