Coverage Report

Created: 2022-05-20 06:19

/src/serenity/Userland/Libraries/LibJS/Runtime/DeclarativeEnvironment.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibJS/Interpreter.h>
8
#include <LibJS/Runtime/DeclarativeEnvironment.h>
9
#include <LibJS/Runtime/Error.h>
10
#include <LibJS/Runtime/FunctionObject.h>
11
#include <LibJS/Runtime/GlobalObject.h>
12
#include <LibJS/Runtime/Value.h>
13
14
namespace JS {
15
16
DeclarativeEnvironment* DeclarativeEnvironment::create_for_per_iteration_bindings(Badge<ForStatement>, DeclarativeEnvironment& other, size_t bindings_size)
17
0
{
18
0
    auto bindings = other.m_bindings.span().slice(0, bindings_size);
19
0
    auto* parent_environment = other.outer_environment();
20
21
0
    return parent_environment->heap().allocate_without_global_object<DeclarativeEnvironment>(parent_environment, bindings);
22
0
}
23
24
DeclarativeEnvironment::DeclarativeEnvironment()
25
    : Environment(nullptr)
26
76
{
27
76
}
28
29
DeclarativeEnvironment::DeclarativeEnvironment(Environment* parent_environment)
30
    : Environment(parent_environment)
31
0
{
32
0
}
33
34
DeclarativeEnvironment::DeclarativeEnvironment(Environment* parent_environment, Span<Binding const> bindings)
35
    : Environment(parent_environment)
36
    , m_bindings(bindings)
37
0
{
38
0
}
39
40
void DeclarativeEnvironment::visit_edges(Visitor& visitor)
41
62
{
42
62
    Base::visit_edges(visitor);
43
62
    for (auto& binding : m_bindings)
44
0
        visitor.visit(binding.value);
45
62
}
46
47
// 9.1.1.1.1 HasBinding ( N ), https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n
48
ThrowCompletionOr<bool> DeclarativeEnvironment::has_binding(FlyString const& name, Optional<size_t>* out_index) const
49
8.32k
{
50
8.32k
    auto index = find_binding_index(name);
51
8.32k
    if (!index.has_value())
52
8.32k
        return false;
53
0
    if (!is_permanently_screwed_by_eval() && out_index)
54
0
        *out_index = *index;
55
0
    return true;
56
8.32k
}
57
58
// 9.1.1.1.2 CreateMutableBinding ( N, D ), https://tc39.es/ecma262/#sec-declarative-environment-records-createmutablebinding-n-d
59
ThrowCompletionOr<void> DeclarativeEnvironment::create_mutable_binding(GlobalObject&, FlyString const& name, bool can_be_deleted)
60
0
{
61
    // 1. Assert: envRec does not already have a binding for N.
62
    // NOTE: We skip this to avoid O(n) traversal of m_bindings.
63
64
    // 2. Create a mutable binding in envRec for N and record that it is uninitialized. If D is true, record that the newly created binding may be deleted by a subsequent DeleteBinding call.
65
0
    m_bindings.append(Binding {
66
0
        .name = name,
67
0
        .value = {},
68
0
        .strict = false,
69
0
        .mutable_ = true,
70
0
        .can_be_deleted = can_be_deleted,
71
0
        .initialized = false,
72
0
    });
73
74
    // 3. Return unused.
75
0
    return {};
76
0
}
77
78
// 9.1.1.1.3 CreateImmutableBinding ( N, S ), https://tc39.es/ecma262/#sec-declarative-environment-records-createimmutablebinding-n-s
79
ThrowCompletionOr<void> DeclarativeEnvironment::create_immutable_binding(GlobalObject&, FlyString const& name, bool strict)
80
0
{
81
    // 1. Assert: envRec does not already have a binding for N.
82
    // NOTE: We skip this to avoid O(n) traversal of m_bindings.
83
84
    // 2. Create an immutable binding in envRec for N and record that it is uninitialized. If S is true, record that the newly created binding is a strict binding.
85
0
    m_bindings.append(Binding {
86
0
        .name = name,
87
0
        .value = {},
88
0
        .strict = strict,
89
0
        .mutable_ = false,
90
0
        .can_be_deleted = false,
91
0
        .initialized = false,
92
0
    });
93
94
    // 3. Return unused.
95
0
    return {};
96
0
}
97
98
// 9.1.1.1.4 InitializeBinding ( N, V ), https://tc39.es/ecma262/#sec-declarative-environment-records-initializebinding-n-v
99
ThrowCompletionOr<void> DeclarativeEnvironment::initialize_binding(GlobalObject& global_object, FlyString const& name, Value value)
100
0
{
101
0
    auto index = find_binding_index(name);
102
0
    VERIFY(index.has_value());
103
104
0
    return initialize_binding_direct(global_object, *index, value);
105
0
}
106
107
ThrowCompletionOr<void> DeclarativeEnvironment::initialize_binding_direct(GlobalObject&, size_t index, Value value)
108
0
{
109
0
    auto& binding = m_bindings[index];
110
111
    // 1. Assert: envRec must have an uninitialized binding for N.
112
0
    VERIFY(binding.initialized == false);
113
114
    // 2. Set the bound value for N in envRec to V.
115
0
    binding.value = value;
116
117
    // 3. Record that the binding for N in envRec has been initialized.
118
0
    binding.initialized = true;
119
120
    // 4. Return unused.
121
0
    return {};
122
0
}
123
124
// 9.1.1.1.5 SetMutableBinding ( N, V, S ), https://tc39.es/ecma262/#sec-declarative-environment-records-setmutablebinding-n-v-s
125
ThrowCompletionOr<void> DeclarativeEnvironment::set_mutable_binding(GlobalObject& global_object, FlyString const& name, Value value, bool strict)
126
0
{
127
    // 1. If envRec does not have a binding for N, then
128
0
    auto index = find_binding_index(name);
129
0
    if (!index.has_value()) {
130
        // a. If S is true, throw a ReferenceError exception.
131
0
        if (strict)
132
0
            return vm().throw_completion<ReferenceError>(global_object, ErrorType::UnknownIdentifier, name);
133
134
        // FIXME: Should be `! envRec.CreateMutableBinding(N, true)` (see https://github.com/tc39/ecma262/pull/2764)
135
        // b. Perform envRec.CreateMutableBinding(N, true).
136
0
        MUST(create_mutable_binding(global_object, name, true));
137
138
        // c. Perform ! envRec.InitializeBinding(N, V).
139
0
        MUST(initialize_binding(global_object, name, value));
140
141
        // d. Return unused.
142
0
        return {};
143
0
    }
144
145
    // 2-5. (extracted into a non-standard function below)
146
0
    TRY(set_mutable_binding_direct(global_object, *index, value, strict));
147
148
    // 6. Return unused.
149
0
    return {};
150
0
}
151
152
ThrowCompletionOr<void> DeclarativeEnvironment::set_mutable_binding_direct(GlobalObject& global_object, size_t index, Value value, bool strict)
153
0
{
154
0
    auto& binding = m_bindings[index];
155
0
    if (binding.strict)
156
0
        strict = true;
157
158
0
    if (!binding.initialized)
159
0
        return vm().throw_completion<ReferenceError>(global_object, ErrorType::BindingNotInitialized, binding.name);
160
161
0
    if (binding.mutable_) {
162
0
        binding.value = value;
163
0
    } else {
164
0
        if (strict)
165
0
            return vm().throw_completion<TypeError>(global_object, ErrorType::InvalidAssignToConst);
166
0
    }
167
168
0
    return {};
169
0
}
170
171
// 9.1.1.1.6 GetBindingValue ( N, S ), https://tc39.es/ecma262/#sec-declarative-environment-records-getbindingvalue-n-s
172
ThrowCompletionOr<Value> DeclarativeEnvironment::get_binding_value(GlobalObject& global_object, FlyString const& name, bool strict)
173
0
{
174
    // 1. Assert: envRec has a binding for N.
175
0
    auto index = find_binding_index(name);
176
0
    VERIFY(index.has_value());
177
178
    // 2-3. (extracted into a non-standard function below)
179
0
    return get_binding_value_direct(global_object, *index, strict);
180
0
}
181
182
ThrowCompletionOr<Value> DeclarativeEnvironment::get_binding_value_direct(GlobalObject& global_object, size_t index, bool)
183
0
{
184
0
    auto& binding = m_bindings[index];
185
186
    // 2. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
187
0
    if (!binding.initialized)
188
0
        return vm().throw_completion<ReferenceError>(global_object, ErrorType::BindingNotInitialized, binding.name);
189
190
    // 3. Return the value currently bound to N in envRec.
191
0
    return binding.value;
192
0
}
193
194
// 9.1.1.1.7 DeleteBinding ( N ), https://tc39.es/ecma262/#sec-declarative-environment-records-deletebinding-n
195
ThrowCompletionOr<bool> DeclarativeEnvironment::delete_binding(GlobalObject&, FlyString const& name)
196
0
{
197
    // 1. Assert: envRec has a binding for the name that is the value of N.
198
0
    auto index = find_binding_index(name);
199
0
    VERIFY(index.has_value());
200
201
0
    auto& binding = m_bindings[*index];
202
203
    // 2. If the binding for N in envRec cannot be deleted, return false.
204
0
    if (!binding.can_be_deleted)
205
0
        return false;
206
207
    // 3. Remove the binding for N from envRec.
208
    // NOTE: We keep the entries in m_bindings to avoid disturbing indices.
209
0
    binding = {};
210
211
    // 4. Return true.
212
0
    return true;
213
0
}
214
215
ThrowCompletionOr<void> DeclarativeEnvironment::initialize_or_set_mutable_binding(GlobalObject& global_object, FlyString const& name, Value value)
216
0
{
217
0
    auto index = find_binding_index(name);
218
0
    VERIFY(index.has_value());
219
0
    auto& binding = m_bindings[*index];
220
0
    if (!binding.initialized)
221
0
        TRY(initialize_binding(global_object, name, value));
222
0
    else
223
0
        TRY(set_mutable_binding(global_object, name, value, false));
224
0
    return {};
225
0
}
226
227
void DeclarativeEnvironment::initialize_or_set_mutable_binding(Badge<ScopeNode>, GlobalObject& global_object, FlyString const& name, Value value)
228
0
{
229
0
    MUST(initialize_or_set_mutable_binding(global_object, name, value));
230
0
}
231
232
}