Coverage Report

Created: 2022-05-20 06:19

/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
}