/src/serenity/Userland/Libraries/LibJS/Runtime/Shape.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Heap/DeferGC.h> |
8 | | #include <LibJS/Runtime/GlobalObject.h> |
9 | | #include <LibJS/Runtime/Shape.h> |
10 | | |
11 | | namespace JS { |
12 | | |
13 | | Shape* Shape::create_unique_clone() const |
14 | 76 | { |
15 | 76 | VERIFY(m_global_object); |
16 | 0 | auto* new_shape = heap().allocate_without_global_object<Shape>(*m_global_object); |
17 | 76 | new_shape->m_unique = true; |
18 | 76 | new_shape->m_prototype = m_prototype; |
19 | 76 | ensure_property_table(); |
20 | 76 | new_shape->ensure_property_table(); |
21 | 76 | (*new_shape->m_property_table) = *m_property_table; |
22 | 76 | new_shape->m_property_count = new_shape->m_property_table->size(); |
23 | 76 | return new_shape; |
24 | 76 | } |
25 | | |
26 | | Shape* Shape::get_or_prune_cached_forward_transition(TransitionKey const& key) |
27 | 890k | { |
28 | 890k | if (!m_forward_transitions) |
29 | 63.3k | return nullptr; |
30 | 826k | auto it = m_forward_transitions->find(key); |
31 | 826k | if (it == m_forward_transitions->end()) |
32 | 5.77k | return nullptr; |
33 | 821k | if (!it->value) { |
34 | | // The cached forward transition has gone stale (from garbage collection). Prune it. |
35 | 0 | m_forward_transitions->remove(it); |
36 | 0 | return nullptr; |
37 | 0 | } |
38 | 821k | return it->value; |
39 | 821k | } |
40 | | |
41 | | Shape* Shape::get_or_prune_cached_prototype_transition(Object* prototype) |
42 | 466k | { |
43 | 466k | if (!m_prototype_transitions) |
44 | 76 | return nullptr; |
45 | 466k | auto it = m_prototype_transitions->find(prototype); |
46 | 466k | if (it == m_prototype_transitions->end()) |
47 | 627 | return nullptr; |
48 | 466k | if (!it->value) { |
49 | | // The cached prototype transition has gone stale (from garbage collection). Prune it. |
50 | 0 | m_prototype_transitions->remove(it); |
51 | 0 | return nullptr; |
52 | 0 | } |
53 | 466k | return it->value; |
54 | 466k | } |
55 | | |
56 | | Shape* Shape::create_put_transition(StringOrSymbol const& property_key, PropertyAttributes attributes) |
57 | 890k | { |
58 | 890k | TransitionKey key { property_key, attributes }; |
59 | 890k | if (auto* existing_shape = get_or_prune_cached_forward_transition(key)) |
60 | 821k | return existing_shape; |
61 | 69.1k | auto* new_shape = heap().allocate_without_global_object<Shape>(*this, property_key, attributes, TransitionType::Put); |
62 | 69.1k | if (!m_forward_transitions) |
63 | 63.3k | m_forward_transitions = make<HashMap<TransitionKey, WeakPtr<Shape>>>(); |
64 | 69.1k | m_forward_transitions->set(key, new_shape); |
65 | 69.1k | return new_shape; |
66 | 890k | } |
67 | | |
68 | | Shape* Shape::create_configure_transition(StringOrSymbol const& property_key, PropertyAttributes attributes) |
69 | 0 | { |
70 | 0 | TransitionKey key { property_key, attributes }; |
71 | 0 | if (auto* existing_shape = get_or_prune_cached_forward_transition(key)) |
72 | 0 | return existing_shape; |
73 | 0 | auto* new_shape = heap().allocate_without_global_object<Shape>(*this, property_key, attributes, TransitionType::Configure); |
74 | 0 | if (!m_forward_transitions) |
75 | 0 | m_forward_transitions = make<HashMap<TransitionKey, WeakPtr<Shape>>>(); |
76 | 0 | m_forward_transitions->set(key, new_shape); |
77 | 0 | return new_shape; |
78 | 0 | } |
79 | | |
80 | | Shape* Shape::create_prototype_transition(Object* new_prototype) |
81 | 466k | { |
82 | 466k | if (auto* existing_shape = get_or_prune_cached_prototype_transition(new_prototype)) |
83 | 466k | return existing_shape; |
84 | 703 | auto* new_shape = heap().allocate_without_global_object<Shape>(*this, new_prototype); |
85 | 703 | if (!m_prototype_transitions) |
86 | 76 | m_prototype_transitions = make<HashMap<Object*, WeakPtr<Shape>>>(); |
87 | 703 | m_prototype_transitions->set(new_prototype, new_shape); |
88 | 703 | return new_shape; |
89 | 466k | } |
90 | | |
91 | | Shape::Shape(Object& global_object) |
92 | | : m_global_object(&global_object) |
93 | 456 | { |
94 | 456 | } |
95 | | |
96 | | Shape::Shape(Shape& previous_shape, StringOrSymbol const& property_key, PropertyAttributes attributes, TransitionType transition_type) |
97 | | : m_global_object(previous_shape.m_global_object) |
98 | | , m_previous(&previous_shape) |
99 | | , m_property_key(property_key) |
100 | | , m_prototype(previous_shape.m_prototype) |
101 | | , m_property_count(transition_type == TransitionType::Put ? previous_shape.m_property_count + 1 : previous_shape.m_property_count) |
102 | | , m_attributes(attributes) |
103 | | , m_transition_type(transition_type) |
104 | 69.1k | { |
105 | 69.1k | } |
106 | | |
107 | | Shape::Shape(Shape& previous_shape, Object* new_prototype) |
108 | | : m_global_object(previous_shape.m_global_object) |
109 | | , m_previous(&previous_shape) |
110 | | , m_prototype(new_prototype) |
111 | | , m_property_count(previous_shape.m_property_count) |
112 | | , m_transition_type(TransitionType::Prototype) |
113 | 703 | { |
114 | 703 | } |
115 | | |
116 | | void Shape::visit_edges(Cell::Visitor& visitor) |
117 | 57.4k | { |
118 | 57.4k | Cell::visit_edges(visitor); |
119 | 57.4k | visitor.visit(m_global_object); |
120 | 57.4k | visitor.visit(m_prototype); |
121 | 57.4k | visitor.visit(m_previous); |
122 | 57.4k | m_property_key.visit_edges(visitor); |
123 | 57.4k | if (m_property_table) { |
124 | 51.3k | for (auto& it : *m_property_table) |
125 | 710k | it.key.visit_edges(visitor); |
126 | 51.3k | } |
127 | 57.4k | } |
128 | | |
129 | | Optional<PropertyMetadata> Shape::lookup(StringOrSymbol const& property_key) const |
130 | 2.26M | { |
131 | 2.26M | if (m_property_count == 0) |
132 | 1.05M | return {}; |
133 | 1.21M | auto property = property_table().get(property_key); |
134 | 1.21M | if (!property.has_value()) |
135 | 1.07M | return {}; |
136 | 135k | return property; |
137 | 1.21M | } |
138 | | |
139 | | FLATTEN HashMap<StringOrSymbol, PropertyMetadata> const& Shape::property_table() const |
140 | 1.21M | { |
141 | 1.21M | ensure_property_table(); |
142 | 1.21M | return *m_property_table; |
143 | 1.21M | } |
144 | | |
145 | | Vector<Shape::Property> Shape::property_table_ordered() const |
146 | 0 | { |
147 | 0 | auto vec = Vector<Shape::Property>(); |
148 | 0 | vec.resize(property_count()); |
149 | |
|
150 | 0 | for (auto& it : property_table()) { |
151 | 0 | vec[it.value.offset] = { it.key, it.value }; |
152 | 0 | } |
153 | |
|
154 | 0 | return vec; |
155 | 0 | } |
156 | | |
157 | | void Shape::ensure_property_table() const |
158 | 1.21M | { |
159 | 1.21M | if (m_property_table) |
160 | 1.15M | return; |
161 | 63.0k | m_property_table = make<HashMap<StringOrSymbol, PropertyMetadata>>(); |
162 | | |
163 | 63.0k | u32 next_offset = 0; |
164 | | |
165 | 63.0k | Vector<Shape const&, 64> transition_chain; |
166 | 69.3k | for (auto* shape = m_previous; shape; shape = shape->m_previous) { |
167 | 65.8k | if (shape->m_property_table) { |
168 | 59.6k | *m_property_table = *shape->m_property_table; |
169 | 59.6k | next_offset = shape->m_property_count; |
170 | 59.6k | break; |
171 | 59.6k | } |
172 | 6.23k | transition_chain.append(*shape); |
173 | 6.23k | } |
174 | 63.0k | transition_chain.append(*this); |
175 | | |
176 | 69.3k | for (auto const& shape : transition_chain.in_reverse()) { |
177 | 69.3k | if (!shape.m_property_key.is_valid()) { |
178 | | // Ignore prototype transitions as they don't affect the key map. |
179 | 6.46k | continue; |
180 | 6.46k | } |
181 | 62.8k | if (shape.m_transition_type == TransitionType::Put) { |
182 | 62.8k | m_property_table->set(shape.m_property_key, { next_offset++, shape.m_attributes }); |
183 | 62.8k | } else if (shape.m_transition_type == TransitionType::Configure) { |
184 | 0 | auto it = m_property_table->find(shape.m_property_key); |
185 | 0 | VERIFY(it != m_property_table->end()); |
186 | 0 | it->value.attributes = shape.m_attributes; |
187 | 0 | } |
188 | 62.8k | } |
189 | 63.0k | } |
190 | | |
191 | | void Shape::add_property_to_unique_shape(StringOrSymbol const& property_key, PropertyAttributes attributes) |
192 | 4.94k | { |
193 | 4.94k | VERIFY(is_unique()); |
194 | 4.94k | VERIFY(m_property_table); |
195 | 4.94k | VERIFY(!m_property_table->contains(property_key)); |
196 | 0 | m_property_table->set(property_key, { static_cast<u32>(m_property_table->size()), attributes }); |
197 | | |
198 | 4.94k | VERIFY(m_property_count < NumericLimits<u32>::max()); |
199 | 0 | ++m_property_count; |
200 | 4.94k | } |
201 | | |
202 | | void Shape::reconfigure_property_in_unique_shape(StringOrSymbol const& property_key, PropertyAttributes attributes) |
203 | 0 | { |
204 | 0 | VERIFY(is_unique()); |
205 | 0 | VERIFY(m_property_table); |
206 | 0 | auto it = m_property_table->find(property_key); |
207 | 0 | VERIFY(it != m_property_table->end()); |
208 | 0 | it->value.attributes = attributes; |
209 | 0 | m_property_table->set(property_key, it->value); |
210 | 0 | } |
211 | | |
212 | | void Shape::remove_property_from_unique_shape(StringOrSymbol const& property_key, size_t offset) |
213 | 0 | { |
214 | 0 | VERIFY(is_unique()); |
215 | 0 | VERIFY(m_property_table); |
216 | 0 | if (m_property_table->remove(property_key)) |
217 | 0 | --m_property_count; |
218 | 0 | for (auto& it : *m_property_table) { |
219 | 0 | VERIFY(it.value.offset != offset); |
220 | 0 | if (it.value.offset > offset) |
221 | 0 | --it.value.offset; |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | void Shape::add_property_without_transition(StringOrSymbol const& property_key, PropertyAttributes attributes) |
226 | 76 | { |
227 | 76 | VERIFY(property_key.is_valid()); |
228 | 0 | ensure_property_table(); |
229 | 76 | if (m_property_table->set(property_key, { m_property_count, attributes }) == AK::HashSetResult::InsertedNewEntry) { |
230 | 76 | VERIFY(m_property_count < NumericLimits<u32>::max()); |
231 | 0 | ++m_property_count; |
232 | 76 | } |
233 | 76 | } |
234 | | |
235 | | FLATTEN void Shape::add_property_without_transition(PropertyKey const& property_key, PropertyAttributes attributes) |
236 | 76 | { |
237 | 76 | VERIFY(property_key.is_valid()); |
238 | 0 | add_property_without_transition(property_key.to_string_or_symbol(), attributes); |
239 | 76 | } |
240 | | |
241 | | } |