Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
3
 * Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <AK/CharacterTypes.h>
9
#include <AK/FloatingPointStringConversions.h>
10
#include <AK/Function.h>
11
#include <AK/Optional.h>
12
#include <AK/Utf16View.h>
13
#include <LibJS/Bytecode/Interpreter.h>
14
#include <LibJS/ModuleLoading.h>
15
#include <LibJS/Parser.h>
16
#include <LibJS/Runtime/AbstractOperations.h>
17
#include <LibJS/Runtime/Accessor.h>
18
#include <LibJS/Runtime/ArgumentsObject.h>
19
#include <LibJS/Runtime/Array.h>
20
#include <LibJS/Runtime/BoundFunction.h>
21
#include <LibJS/Runtime/Completion.h>
22
#include <LibJS/Runtime/DeclarativeEnvironment.h>
23
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
24
#include <LibJS/Runtime/ErrorTypes.h>
25
#include <LibJS/Runtime/FunctionEnvironment.h>
26
#include <LibJS/Runtime/FunctionObject.h>
27
#include <LibJS/Runtime/GlobalEnvironment.h>
28
#include <LibJS/Runtime/GlobalObject.h>
29
#include <LibJS/Runtime/Object.h>
30
#include <LibJS/Runtime/ObjectEnvironment.h>
31
#include <LibJS/Runtime/PromiseCapability.h>
32
#include <LibJS/Runtime/PromiseConstructor.h>
33
#include <LibJS/Runtime/PropertyDescriptor.h>
34
#include <LibJS/Runtime/PropertyKey.h>
35
#include <LibJS/Runtime/ProxyObject.h>
36
#include <LibJS/Runtime/Reference.h>
37
#include <LibJS/Runtime/StringPrototype.h>
38
#include <LibJS/Runtime/SuppressedError.h>
39
#include <LibJS/Runtime/ValueInlines.h>
40
41
namespace JS {
42
43
// 7.2.1 RequireObjectCoercible ( argument ), https://tc39.es/ecma262/#sec-requireobjectcoercible
44
ThrowCompletionOr<Value> require_object_coercible(VM& vm, Value value)
45
0
{
46
0
    if (value.is_nullish())
47
0
        return vm.throw_completion<TypeError>(ErrorType::NotObjectCoercible, value.to_string_without_side_effects());
48
0
    return value;
49
0
}
50
51
// 7.3.14 Call ( F, V [ , argumentsList ] ), https://tc39.es/ecma262/#sec-call
52
ThrowCompletionOr<Value> call_impl(VM& vm, Value function, Value this_value, ReadonlySpan<Value> arguments_list)
53
0
{
54
    // 1. If argumentsList is not present, set argumentsList to a new empty List.
55
56
    // 2. If IsCallable(F) is false, throw a TypeError exception.
57
0
    if (!function.is_function())
58
0
        return vm.throw_completion<TypeError>(ErrorType::NotAFunction, function.to_string_without_side_effects());
59
60
    // 3. Return ? F.[[Call]](V, argumentsList).
61
0
    return function.as_function().internal_call(this_value, arguments_list);
62
0
}
63
64
ThrowCompletionOr<Value> call_impl(VM&, FunctionObject& function, Value this_value, ReadonlySpan<Value> arguments_list)
65
0
{
66
    // 1. If argumentsList is not present, set argumentsList to a new empty List.
67
68
    // 2. If IsCallable(F) is false, throw a TypeError exception.
69
    // Note: Called with a FunctionObject ref
70
71
    // 3. Return ? F.[[Call]](V, argumentsList).
72
0
    return function.internal_call(this_value, arguments_list);
73
0
}
74
75
// 7.3.15 Construct ( F [ , argumentsList [ , newTarget ] ] ), https://tc39.es/ecma262/#sec-construct
76
ThrowCompletionOr<NonnullGCPtr<Object>> construct_impl(VM&, FunctionObject& function, ReadonlySpan<Value> arguments_list, FunctionObject* new_target)
77
0
{
78
    // 1. If newTarget is not present, set newTarget to F.
79
0
    if (!new_target)
80
0
        new_target = &function;
81
82
    // 2. If argumentsList is not present, set argumentsList to a new empty List.
83
84
    // 3. Return ? F.[[Construct]](argumentsList, newTarget).
85
0
    return function.internal_construct(arguments_list, *new_target);
86
0
}
87
88
// 7.3.19 LengthOfArrayLike ( obj ), https://tc39.es/ecma262/#sec-lengthofarraylike
89
ThrowCompletionOr<size_t> length_of_array_like(VM& vm, Object const& object)
90
0
{
91
    // OPTIMIZATION: For Array objects with a magical "length" property, it should always reflect the size of indexed property storage.
92
0
    if (object.has_magical_length_property())
93
0
        return object.indexed_properties().array_like_size();
94
95
    // 1. Return ℝ(? ToLength(? Get(obj, "length"))).
96
0
    return TRY(object.get(vm.names.length)).to_length(vm);
97
0
}
98
99
// 7.3.20 CreateListFromArrayLike ( obj [ , elementTypes ] ), https://tc39.es/ecma262/#sec-createlistfromarraylike
100
ThrowCompletionOr<MarkedVector<Value>> create_list_from_array_like(VM& vm, Value value, Function<ThrowCompletionOr<void>(Value)> check_value)
101
0
{
102
    // 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».
103
104
    // 2. If Type(obj) is not Object, throw a TypeError exception.
105
0
    if (!value.is_object())
106
0
        return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
107
108
0
    auto& array_like = value.as_object();
109
110
    // 3. Let len be ? LengthOfArrayLike(obj).
111
0
    auto length = TRY(length_of_array_like(vm, array_like));
112
113
    // 4. Let list be a new empty List.
114
0
    auto list = MarkedVector<Value> { vm.heap() };
115
0
    list.ensure_capacity(length);
116
117
    // 5. Let index be 0.
118
    // 6. Repeat, while index < len,
119
0
    for (size_t i = 0; i < length; ++i) {
120
        // a. Let indexName be ! ToString(𝔽(index)).
121
0
        auto index_name = PropertyKey { i };
122
123
        // b. Let next be ? Get(obj, indexName).
124
0
        auto next = TRY(array_like.get(index_name));
125
126
        // c. If Type(next) is not an element of elementTypes, throw a TypeError exception.
127
0
        if (check_value)
128
0
            TRY(check_value(next));
129
130
        // d. Append next as the last element of list.
131
0
        list.unchecked_append(next);
132
0
    }
133
134
    // 7. Return list.
135
0
    return ThrowCompletionOr(move(list));
136
0
}
137
138
// 7.3.23 SpeciesConstructor ( O, defaultConstructor ), https://tc39.es/ecma262/#sec-speciesconstructor
139
ThrowCompletionOr<FunctionObject*> species_constructor(VM& vm, Object const& object, FunctionObject& default_constructor)
140
0
{
141
    // 1. Let C be ? Get(O, "constructor").
142
0
    auto constructor = TRY(object.get(vm.names.constructor));
143
144
    // 2. If C is undefined, return defaultConstructor.
145
0
    if (constructor.is_undefined())
146
0
        return &default_constructor;
147
148
    // 3. If Type(C) is not Object, throw a TypeError exception.
149
0
    if (!constructor.is_object())
150
0
        return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, constructor.to_string_without_side_effects());
151
152
    // 4. Let S be ? Get(C, @@species).
153
0
    auto species = TRY(constructor.as_object().get(vm.well_known_symbol_species()));
154
155
    // 5. If S is either undefined or null, return defaultConstructor.
156
0
    if (species.is_nullish())
157
0
        return &default_constructor;
158
159
    // 6. If IsConstructor(S) is true, return S.
160
0
    if (species.is_constructor())
161
0
        return &species.as_function();
162
163
    // 7. Throw a TypeError exception.
164
0
    return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, species.to_string_without_side_effects());
165
0
}
166
167
// 7.3.25 GetFunctionRealm ( obj ), https://tc39.es/ecma262/#sec-getfunctionrealm
168
ThrowCompletionOr<Realm*> get_function_realm(VM& vm, FunctionObject const& function)
169
0
{
170
    // 1. If obj has a [[Realm]] internal slot, then
171
0
    if (function.realm()) {
172
        // a. Return obj.[[Realm]].
173
0
        return function.realm();
174
0
    }
175
176
    // 2. If obj is a bound function exotic object, then
177
0
    if (is<BoundFunction>(function)) {
178
0
        auto& bound_function = static_cast<BoundFunction const&>(function);
179
180
        // a. Let target be obj.[[BoundTargetFunction]].
181
0
        auto& target = bound_function.bound_target_function();
182
183
        // b. Return ? GetFunctionRealm(target).
184
0
        return get_function_realm(vm, target);
185
0
    }
186
187
    // 3. If obj is a Proxy exotic object, then
188
0
    if (is<ProxyObject>(function)) {
189
0
        auto& proxy = static_cast<ProxyObject const&>(function);
190
191
        // a. If obj.[[ProxyHandler]] is null, throw a TypeError exception.
192
0
        if (proxy.is_revoked())
193
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
194
195
        // b. Let proxyTarget be obj.[[ProxyTarget]].
196
0
        auto& proxy_target = proxy.target();
197
198
        // c. Return ? GetFunctionRealm(proxyTarget).
199
0
        VERIFY(proxy_target.is_function());
200
0
        return get_function_realm(vm, static_cast<FunctionObject const&>(proxy_target));
201
0
    }
202
203
    // 4. Return the current Realm Record.
204
0
    return vm.current_realm();
205
0
}
206
207
// 8.5.2.1 InitializeBoundName ( name, value, environment ), https://tc39.es/ecma262/#sec-initializeboundname
208
ThrowCompletionOr<void> initialize_bound_name(VM& vm, DeprecatedFlyString const& name, Value value, Environment* environment)
209
0
{
210
    // 1. If environment is not undefined, then
211
0
    if (environment) {
212
        // FIXME: The normal is not included in the explicit resource management spec yet, so there is no spec link for it.
213
        // a. Perform ! environment.InitializeBinding(name, value, normal).
214
0
        MUST(environment->initialize_binding(vm, name, value, Environment::InitializeBindingHint::Normal));
215
216
        // b. Return unused.
217
0
        return {};
218
0
    }
219
    // 2. Else,
220
0
    else {
221
        // a. Let lhs be ? ResolveBinding(name).
222
0
        auto lhs = TRY(vm.resolve_binding(name));
223
224
        // b. Return ? PutValue(lhs, value).
225
0
        return TRY(lhs.put_value(vm, value));
226
0
    }
227
228
0
    VERIFY_NOT_REACHED();
229
0
}
230
231
// 10.1.6.2 IsCompatiblePropertyDescriptor ( Extensible, Desc, Current ), https://tc39.es/ecma262/#sec-iscompatiblepropertydescriptor
232
bool is_compatible_property_descriptor(bool extensible, PropertyDescriptor const& descriptor, Optional<PropertyDescriptor> const& current)
233
0
{
234
    // 1. Return ValidateAndApplyPropertyDescriptor(undefined, "", Extensible, Desc, Current).
235
0
    return validate_and_apply_property_descriptor(nullptr, "", extensible, descriptor, current);
236
0
}
237
238
// 10.1.6.3 ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ), https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
239
bool validate_and_apply_property_descriptor(Object* object, PropertyKey const& property_key, bool extensible, PropertyDescriptor const& descriptor, Optional<PropertyDescriptor> const& current)
240
0
{
241
    // 1. Assert: IsPropertyKey(P) is true.
242
0
    VERIFY(property_key.is_valid());
243
244
    // 2. If current is undefined, then
245
0
    if (!current.has_value()) {
246
        // a. If extensible is false, return false.
247
0
        if (!extensible)
248
0
            return false;
249
250
        // b. If O is undefined, return true.
251
0
        if (object == nullptr)
252
0
            return true;
253
254
        // c. If IsAccessorDescriptor(Desc) is true, then
255
0
        if (descriptor.is_accessor_descriptor()) {
256
            // i. Create an own accessor property named P of object O whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]] attributes are set to the value of the corresponding field in Desc if Desc has that field, or to the attribute's default value otherwise.
257
0
            auto accessor = Accessor::create(object->vm(), descriptor.get.value_or(nullptr), descriptor.set.value_or(nullptr));
258
0
            object->storage_set(property_key, { accessor, descriptor.attributes() });
259
0
        }
260
        // d. Else,
261
0
        else {
262
            // i. Create an own data property named P of object O whose [[Value]], [[Writable]], [[Enumerable]], and [[Configurable]] attributes are set to the value of the corresponding field in Desc if Desc has that field, or to the attribute's default value otherwise.
263
0
            auto value = descriptor.value.value_or(js_undefined());
264
0
            object->storage_set(property_key, { value, descriptor.attributes() });
265
0
        }
266
267
        // e. Return true.
268
0
        return true;
269
0
    }
270
271
    // 3. Assert: current is a fully populated Property Descriptor.
272
273
    // 4. If Desc does not have any fields, return true.
274
0
    if (descriptor.is_empty())
275
0
        return true;
276
277
    // 5. If current.[[Configurable]] is false, then
278
0
    if (!*current->configurable) {
279
        // a. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.
280
0
        if (descriptor.configurable.has_value() && *descriptor.configurable)
281
0
            return false;
282
283
        // b. If Desc has an [[Enumerable]] field and SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) is false, return false.
284
0
        if (descriptor.enumerable.has_value() && *descriptor.enumerable != *current->enumerable)
285
0
            return false;
286
287
        // c. If IsGenericDescriptor(Desc) is false and SameValue(IsAccessorDescriptor(Desc), IsAccessorDescriptor(current)) is false, return false.
288
0
        if (!descriptor.is_generic_descriptor() && (descriptor.is_accessor_descriptor() != current->is_accessor_descriptor()))
289
0
            return false;
290
291
        // d. If IsAccessorDescriptor(current) is true, then
292
0
        if (current->is_accessor_descriptor()) {
293
            // i. If Desc has a [[Get]] field and SameValue(Desc.[[Get]], current.[[Get]]) is false, return false.
294
0
            if (descriptor.get.has_value() && *descriptor.get != *current->get)
295
0
                return false;
296
297
            // ii. If Desc has a [[Set]] field and SameValue(Desc.[[Set]], current.[[Set]]) is false, return false.
298
0
            if (descriptor.set.has_value() && *descriptor.set != *current->set)
299
0
                return false;
300
0
        }
301
        // e. Else if current.[[Writable]] is false, then
302
0
        else if (!*current->writable) {
303
            // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is true, return false.
304
0
            if (descriptor.writable.has_value() && *descriptor.writable)
305
0
                return false;
306
307
            // ii. If Desc has a [[Value]] field and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false.
308
0
            if (descriptor.value.has_value() && (*descriptor.value != *current->value))
309
0
                return false;
310
0
        }
311
0
    }
312
313
    // 6. If O is not undefined, then
314
0
    if (object != nullptr) {
315
        // a. If IsDataDescriptor(current) is true and IsAccessorDescriptor(Desc) is true, then
316
0
        if (current->is_data_descriptor() && descriptor.is_accessor_descriptor()) {
317
            // i. If Desc has a [[Configurable]] field, let configurable be Desc.[[Configurable]], else let configurable be current.[[Configurable]].
318
0
            auto configurable = descriptor.configurable.value_or(*current->configurable);
319
320
            // ii. If Desc has a [[Enumerable]] field, let enumerable be Desc.[[Enumerable]], else let enumerable be current.[[Enumerable]].
321
0
            auto enumerable = descriptor.enumerable.value_or(*current->enumerable);
322
323
            // iii. Replace the property named P of object O with an accessor property having [[Configurable]] and [[Enumerable]] attributes set to configurable and enumerable, respectively, and each other attribute set to its corresponding value in Desc if present, otherwise to its default value.
324
0
            auto accessor = Accessor::create(object->vm(), descriptor.get.value_or(nullptr), descriptor.set.value_or(nullptr));
325
0
            PropertyAttributes attributes;
326
0
            attributes.set_enumerable(enumerable);
327
0
            attributes.set_configurable(configurable);
328
0
            object->storage_set(property_key, { accessor, attributes });
329
0
        }
330
        // b. Else if IsAccessorDescriptor(current) is true and IsDataDescriptor(Desc) is true, then
331
0
        else if (current->is_accessor_descriptor() && descriptor.is_data_descriptor()) {
332
            // i. If Desc has a [[Configurable]] field, let configurable be Desc.[[Configurable]], else let configurable be current.[[Configurable]].
333
0
            auto configurable = descriptor.configurable.value_or(*current->configurable);
334
335
            // ii. If Desc has a [[Enumerable]] field, let enumerable be Desc.[[Enumerable]], else let enumerable be current.[[Enumerable]].
336
0
            auto enumerable = descriptor.enumerable.value_or(*current->enumerable);
337
338
            // iii. Replace the property named P of object O with a data property having [[Configurable]] and [[Enumerable]] attributes set to configurable and enumerable, respectively, and each other attribute set to its corresponding value in Desc if present, otherwise to its default value.
339
0
            auto value = descriptor.value.value_or(js_undefined());
340
0
            PropertyAttributes attributes;
341
0
            attributes.set_writable(descriptor.writable.value_or(false));
342
0
            attributes.set_enumerable(enumerable);
343
0
            attributes.set_configurable(configurable);
344
0
            object->storage_set(property_key, { value, attributes });
345
0
        }
346
        // c. Else,
347
0
        else {
348
            // i. For each field of Desc, set the corresponding attribute of the property named P of object O to the value of the field.
349
0
            Value value;
350
0
            if (descriptor.is_accessor_descriptor() || (current->is_accessor_descriptor() && !descriptor.is_data_descriptor())) {
351
0
                auto getter = descriptor.get.value_or(current->get.value_or(nullptr));
352
0
                auto setter = descriptor.set.value_or(current->set.value_or(nullptr));
353
0
                value = Accessor::create(object->vm(), getter, setter);
354
0
            } else {
355
0
                value = descriptor.value.value_or(current->value.value_or({}));
356
0
            }
357
0
            PropertyAttributes attributes;
358
0
            attributes.set_writable(descriptor.writable.value_or(current->writable.value_or(false)));
359
0
            attributes.set_enumerable(descriptor.enumerable.value_or(current->enumerable.value_or(false)));
360
0
            attributes.set_configurable(descriptor.configurable.value_or(current->configurable.value_or(false)));
361
0
            object->storage_set(property_key, { value, attributes });
362
0
        }
363
0
    }
364
365
    // 7. Return true.
366
0
    return true;
367
0
}
368
369
// 10.1.14 GetPrototypeFromConstructor ( constructor, intrinsicDefaultProto ), https://tc39.es/ecma262/#sec-getprototypefromconstructor
370
ThrowCompletionOr<Object*> get_prototype_from_constructor(VM& vm, FunctionObject const& constructor, NonnullGCPtr<Object> (Intrinsics::*intrinsic_default_prototype)())
371
0
{
372
    // 1. Assert: intrinsicDefaultProto is this specification's name of an intrinsic object. The corresponding object must be an intrinsic that is intended to be used as the [[Prototype]] value of an object.
373
374
    // 2. Let proto be ? Get(constructor, "prototype").
375
0
    auto prototype = TRY(constructor.get(vm.names.prototype));
376
377
    // 3. If Type(proto) is not Object, then
378
0
    if (!prototype.is_object()) {
379
        // a. Let realm be ? GetFunctionRealm(constructor).
380
0
        auto* realm = TRY(get_function_realm(vm, constructor));
381
382
        // b. Set proto to realm's intrinsic object named intrinsicDefaultProto.
383
0
        prototype = (realm->intrinsics().*intrinsic_default_prototype)();
384
0
    }
385
386
    // 4. Return proto.
387
0
    return &prototype.as_object();
388
0
}
389
390
// 9.1.2.2 NewDeclarativeEnvironment ( E ), https://tc39.es/ecma262/#sec-newdeclarativeenvironment
391
NonnullGCPtr<DeclarativeEnvironment> new_declarative_environment(Environment& environment)
392
0
{
393
0
    auto& heap = environment.heap();
394
395
    // 1. Let env be a new Declarative Environment Record containing no bindings.
396
    // 2. Set env.[[OuterEnv]] to E.
397
    // 3. Return env.
398
0
    return heap.allocate_without_realm<DeclarativeEnvironment>(&environment);
399
0
}
400
401
// 9.1.2.3 NewObjectEnvironment ( O, W, E ), https://tc39.es/ecma262/#sec-newobjectenvironment
402
NonnullGCPtr<ObjectEnvironment> new_object_environment(Object& object, bool is_with_environment, Environment* environment)
403
0
{
404
0
    auto& heap = object.heap();
405
406
    // 1. Let env be a new Object Environment Record.
407
    // 2. Set env.[[BindingObject]] to O.
408
    // 3. Set env.[[IsWithEnvironment]] to W.
409
    // 4. Set env.[[OuterEnv]] to E.
410
    // 5. Return env.
411
0
    return heap.allocate_without_realm<ObjectEnvironment>(object, is_with_environment ? ObjectEnvironment::IsWithEnvironment::Yes : ObjectEnvironment::IsWithEnvironment::No, environment);
412
0
}
413
414
// 9.1.2.4 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/ecma262/#sec-newfunctionenvironment
415
NonnullGCPtr<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject& function, Object* new_target)
416
0
{
417
0
    auto& heap = function.heap();
418
419
    // 1. Let env be a new function Environment Record containing no bindings.
420
0
    auto env = heap.allocate_without_realm<FunctionEnvironment>(function.environment());
421
422
    // 2. Set env.[[FunctionObject]] to F.
423
0
    env->set_function_object(function);
424
425
    // 3. If F.[[ThisMode]] is lexical, set env.[[ThisBindingStatus]] to lexical.
426
0
    if (function.this_mode() == ECMAScriptFunctionObject::ThisMode::Lexical)
427
0
        env->set_this_binding_status(FunctionEnvironment::ThisBindingStatus::Lexical);
428
    // 4. Else, set env.[[ThisBindingStatus]] to uninitialized.
429
0
    else
430
0
        env->set_this_binding_status(FunctionEnvironment::ThisBindingStatus::Uninitialized);
431
432
    // 5. Set env.[[NewTarget]] to newTarget.
433
0
    env->set_new_target(new_target ?: js_undefined());
434
435
    // 6. Set env.[[OuterEnv]] to F.[[Environment]].
436
    // NOTE: Done in step 1 via the FunctionEnvironment constructor.
437
438
    // 7. Return env.
439
0
    return env;
440
0
}
441
442
// 9.2.1.1 NewPrivateEnvironment ( outerPrivEnv ), https://tc39.es/ecma262/#sec-newprivateenvironment
443
NonnullGCPtr<PrivateEnvironment> new_private_environment(VM& vm, PrivateEnvironment* outer)
444
0
{
445
    // 1. Let names be a new empty List.
446
    // 2. Return the PrivateEnvironment Record { [[OuterPrivateEnvironment]]: outerPrivEnv, [[Names]]: names }.
447
0
    return vm.heap().allocate_without_realm<PrivateEnvironment>(outer);
448
0
}
449
450
// 9.4.3 GetThisEnvironment ( ), https://tc39.es/ecma262/#sec-getthisenvironment
451
NonnullGCPtr<Environment> get_this_environment(VM& vm)
452
0
{
453
    // 1. Let env be the running execution context's LexicalEnvironment.
454
    // 2. Repeat,
455
0
    for (auto* env = vm.lexical_environment(); env; env = env->outer_environment()) {
456
        // a. Let exists be env.HasThisBinding().
457
        // b. If exists is true, return env.
458
0
        if (env->has_this_binding())
459
0
            return *env;
460
461
        // c. Let outer be env.[[OuterEnv]].
462
        // d. Assert: outer is not null.
463
        // e. Set env to outer.
464
0
    }
465
0
    VERIFY_NOT_REACHED();
466
0
}
467
468
// 9.14 CanBeHeldWeakly ( v ), https://tc39.es/proposal-symbols-as-weakmap-keys/#sec-canbeheldweakly-abstract-operation
469
bool can_be_held_weakly(Value value)
470
0
{
471
    // 1. If Type(v) is Object, return true.
472
0
    if (value.is_object())
473
0
        return true;
474
475
    // 2. If Type(v) is Symbol, then
476
0
    if (value.is_symbol()) {
477
        // a. For each element e of the GlobalSymbolRegistry List (see 19.4.2.2), do
478
        //     i. If SameValue(e.[[Symbol]], v) is true, return false.
479
        // b. Return true.
480
0
        return !value.as_symbol().is_global();
481
0
    }
482
483
    // 3. Return false.
484
0
    return false;
485
0
}
486
487
// 13.3.7.2 GetSuperConstructor ( ), https://tc39.es/ecma262/#sec-getsuperconstructor
488
Object* get_super_constructor(VM& vm)
489
0
{
490
    // 1. Let envRec be GetThisEnvironment().
491
0
    auto env = get_this_environment(vm);
492
493
    // 2. Assert: envRec is a function Environment Record.
494
    // 3. Let activeFunction be envRec.[[FunctionObject]].
495
    // 4. Assert: activeFunction is an ECMAScript function object.
496
0
    auto& active_function = verify_cast<FunctionEnvironment>(*env).function_object();
497
498
    // 5. Let superConstructor be ! activeFunction.[[GetPrototypeOf]]().
499
0
    auto* super_constructor = MUST(active_function.internal_get_prototype_of());
500
501
    // 6. Return superConstructor.
502
0
    return super_constructor;
503
0
}
504
505
// 19.2.1.1 PerformEval ( x, strictCaller, direct ), https://tc39.es/ecma262/#sec-performeval
506
ThrowCompletionOr<Value> perform_eval(VM& vm, Value x, CallerMode strict_caller, EvalMode direct)
507
0
{
508
    // 1. Assert: If direct is false, then strictCaller is also false.
509
0
    VERIFY(direct == EvalMode::Direct || strict_caller == CallerMode::NonStrict);
510
511
    // 2. If Type(x) is not String, return x.
512
0
    if (!x.is_string())
513
0
        return x;
514
0
    auto& code_string = x.as_string();
515
516
    // 3. Let evalRealm be the current Realm Record.
517
0
    auto& eval_realm = *vm.running_execution_context().realm;
518
519
    // 4. NOTE: In the case of a direct eval, evalRealm is the realm of both the caller of eval and of the eval function itself.
520
    // 5. Perform ? HostEnsureCanCompileStrings(evalRealm, « », x, direct).
521
0
    TRY(vm.host_ensure_can_compile_strings(eval_realm, {}, code_string.utf8_string_view(), direct));
522
523
    // 6. Let inFunction be false.
524
0
    bool in_function = false;
525
526
    // 7. Let inMethod be false.
527
0
    bool in_method = false;
528
529
    // 8. Let inDerivedConstructor be false.
530
0
    bool in_derived_constructor = false;
531
532
    // 9. Let inClassFieldInitializer be false.
533
0
    bool in_class_field_initializer = false;
534
535
    // 10. If direct is true, then
536
0
    if (direct == EvalMode::Direct) {
537
        // a. Let thisEnvRec be GetThisEnvironment().
538
0
        auto this_environment_record = get_this_environment(vm);
539
540
        // b. If thisEnvRec is a function Environment Record, then
541
0
        if (is<FunctionEnvironment>(*this_environment_record)) {
542
0
            auto& this_function_environment_record = static_cast<FunctionEnvironment&>(*this_environment_record);
543
544
            // i. Let F be thisEnvRec.[[FunctionObject]].
545
0
            auto& function = this_function_environment_record.function_object();
546
547
            // ii. Set inFunction to true.
548
0
            in_function = true;
549
550
            // iii. Set inMethod to thisEnvRec.HasSuperBinding().
551
0
            in_method = this_function_environment_record.has_super_binding();
552
553
            // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true.
554
0
            if (function.constructor_kind() == ECMAScriptFunctionObject::ConstructorKind::Derived)
555
0
                in_derived_constructor = true;
556
557
            // v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]].
558
0
            auto& class_field_initializer_name = function.class_field_initializer_name();
559
560
            // vi. If classFieldInitializerName is not empty, set inClassFieldInitializer to true.
561
0
            if (!class_field_initializer_name.has<Empty>())
562
0
                in_class_field_initializer = true;
563
0
        }
564
0
    }
565
566
    // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
567
    //     a. Let script be ParseText(StringToCodePoints(x), Script).
568
    //     c. If script Contains ScriptBody is false, return undefined.
569
    //     d. Let body be the ScriptBody of script.
570
    //     NOTE: We do these next steps by passing initial state to the parser.
571
    //     e. If inFunction is false, and body Contains NewTarget, throw a SyntaxError exception.
572
    //     f. If inMethod is false, and body Contains SuperProperty, throw a SyntaxError exception.
573
    //     g. If inDerivedConstructor is false, and body Contains SuperCall, throw a SyntaxError exception.
574
    //     h. If inClassFieldInitializer is true, and ContainsArguments of body is true, throw a SyntaxError exception.
575
0
    Parser::EvalInitialState initial_state {
576
0
        .in_eval_function_context = in_function,
577
0
        .allow_super_property_lookup = in_method,
578
0
        .allow_super_constructor_call = in_derived_constructor,
579
0
        .in_class_field_initializer = in_class_field_initializer,
580
0
    };
581
582
0
    Parser parser { Lexer { code_string.byte_string() }, Program::Type::Script, move(initial_state) };
583
0
    auto program = parser.parse_program(strict_caller == CallerMode::Strict);
584
585
    //     b. If script is a List of errors, throw a SyntaxError exception.
586
0
    if (parser.has_errors()) {
587
0
        auto& error = parser.errors()[0];
588
0
        return vm.throw_completion<SyntaxError>(error.to_string());
589
0
    }
590
591
0
    bool strict_eval = false;
592
593
    // 12. If strictCaller is true, let strictEval be true.
594
0
    if (strict_caller == CallerMode::Strict)
595
0
        strict_eval = true;
596
    // 13. Else, let strictEval be IsStrict of script.
597
0
    else
598
0
        strict_eval = program->is_strict_mode();
599
600
    // 14. Let runningContext be the running execution context.
601
    // 15. NOTE: If direct is true, runningContext will be the execution context that performed the direct eval. If direct is false, runningContext will be the execution context for the invocation of the eval function.
602
0
    auto& running_context = vm.running_execution_context();
603
604
0
    Environment* lexical_environment;
605
0
    Environment* variable_environment;
606
0
    PrivateEnvironment* private_environment;
607
608
    // 16. If direct is true, then
609
0
    if (direct == EvalMode::Direct) {
610
        // a. Let lexEnv be NewDeclarativeEnvironment(runningContext's LexicalEnvironment).
611
0
        lexical_environment = new_declarative_environment(*running_context.lexical_environment);
612
613
        // b. Let varEnv be runningContext's VariableEnvironment.
614
0
        variable_environment = running_context.variable_environment;
615
616
        // c. Let privateEnv be runningContext's PrivateEnvironment.
617
0
        private_environment = running_context.private_environment;
618
0
    }
619
    // 17. Else,
620
0
    else {
621
        // a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
622
0
        lexical_environment = new_declarative_environment(eval_realm.global_environment());
623
624
        // b. Let varEnv be evalRealm.[[GlobalEnv]].
625
0
        variable_environment = &eval_realm.global_environment();
626
627
        // c. Let privateEnv be null.
628
0
        private_environment = nullptr;
629
0
    }
630
631
    // 18. If strictEval is true, set varEnv to lexEnv.
632
0
    if (strict_eval)
633
0
        variable_environment = lexical_environment;
634
635
0
    if (direct == EvalMode::Direct && !strict_eval) {
636
        // NOTE: Non-strict direct eval() forces us to deoptimize variable accesses.
637
        //       Mark the variable environment chain as screwed since we will not be able
638
        //       to rely on cached environment coordinates from this point on.
639
0
        variable_environment->set_permanently_screwed_by_eval();
640
0
    }
641
642
    // 19. If runningContext is not already suspended, suspend runningContext.
643
    // FIXME: We don't have this concept yet.
644
645
    // 20. Let evalContext be a new ECMAScript code execution context.
646
0
    auto eval_context = ExecutionContext::create();
647
648
    // 21. Set evalContext's Function to null.
649
    // NOTE: This was done in the construction of eval_context.
650
651
    // 22. Set evalContext's Realm to evalRealm.
652
0
    eval_context->realm = &eval_realm;
653
654
    // 23. Set evalContext's ScriptOrModule to runningContext's ScriptOrModule.
655
0
    eval_context->script_or_module = running_context.script_or_module;
656
657
    // 24. Set evalContext's VariableEnvironment to varEnv.
658
0
    eval_context->variable_environment = variable_environment;
659
660
    // 25. Set evalContext's LexicalEnvironment to lexEnv.
661
0
    eval_context->lexical_environment = lexical_environment;
662
663
    // 26. Set evalContext's PrivateEnvironment to privateEnv.
664
0
    eval_context->private_environment = private_environment;
665
666
    // NOTE: This isn't in the spec, but we require it.
667
0
    eval_context->is_strict_mode = strict_eval;
668
669
    // 27. Push evalContext onto the execution context stack; evalContext is now the running execution context.
670
0
    TRY(vm.push_execution_context(*eval_context, {}));
671
672
    // NOTE: We use a ScopeGuard to automatically pop the execution context when any of the `TRY`s below return a throw completion.
673
0
    ScopeGuard pop_guard = [&] {
674
        // FIXME: 31. Suspend evalContext and remove it from the execution context stack.
675
676
        // 32. Resume the context that is now on the top of the execution context stack as the running execution context.
677
0
        vm.pop_execution_context();
678
0
    };
679
680
    // 28. Let result be Completion(EvalDeclarationInstantiation(body, varEnv, lexEnv, privateEnv, strictEval)).
681
0
    TRY(eval_declaration_instantiation(vm, program, variable_environment, lexical_environment, private_environment, strict_eval));
682
683
0
    Optional<Value> eval_result;
684
685
    // 29. If result.[[Type]] is normal, then
686
    //     a. Set result to the result of evaluating body.
687
0
    auto executable_result = Bytecode::Generator::generate_from_ast_node(vm, program, {});
688
0
    if (executable_result.is_error())
689
0
        return vm.throw_completion<InternalError>(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string()));
690
691
0
    auto executable = executable_result.release_value();
692
0
    executable->name = "eval"sv;
693
0
    if (Bytecode::g_dump_bytecode)
694
0
        executable->dump();
695
0
    auto result_or_error = vm.bytecode_interpreter().run_executable(*executable, {});
696
0
    if (result_or_error.value.is_error())
697
0
        return result_or_error.value.release_error();
698
699
0
    auto& result = result_or_error.return_register_value;
700
0
    if (!result.is_empty())
701
0
        eval_result = result;
702
703
    // 30. If result.[[Type]] is normal and result.[[Value]] is empty, then
704
    //     a. Set result to NormalCompletion(undefined).
705
    // NOTE: Step 31 and 32 is handled by `pop_guard` above.
706
    // 33. Return ? result.
707
    // NOTE: Step 33 is also performed with each use of `TRY` above.
708
0
    return eval_result.value_or(js_undefined());
709
0
}
710
711
// 19.2.1.3 EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict ), https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
712
// 9.1.1.1 EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict ), https://tc39.es/proposal-explicit-resource-management/#sec-evaldeclarationinstantiation
713
ThrowCompletionOr<void> eval_declaration_instantiation(VM& vm, Program const& program, Environment* variable_environment, Environment* lexical_environment, PrivateEnvironment* private_environment, bool strict)
714
0
{
715
0
    auto& realm = *vm.current_realm();
716
0
    GlobalEnvironment* global_var_environment = variable_environment->is_global_environment() ? static_cast<GlobalEnvironment*>(variable_environment) : nullptr;
717
718
    // 1. Let varNames be the VarDeclaredNames of body.
719
    // 2. Let varDeclarations be the VarScopedDeclarations of body.
720
    // 3. If strict is false, then
721
0
    if (!strict) {
722
        // a. If varEnv is a global Environment Record, then
723
0
        if (global_var_environment) {
724
            // i. For each element name of varNames, do
725
0
            TRY(program.for_each_var_declared_identifier([&](auto const& identifier) -> ThrowCompletionOr<void> {
726
                // 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
727
0
                if (global_var_environment->has_lexical_declaration(identifier.string()))
728
0
                    return vm.throw_completion<SyntaxError>(ErrorType::TopLevelVariableAlreadyDeclared, identifier.string());
729
730
                // 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
731
0
                return {};
732
0
            }));
733
0
        }
734
735
        // b. Let thisEnv be lexEnv.
736
0
        auto* this_environment = lexical_environment;
737
        // c. Assert: The following loop will terminate.
738
739
        // d. Repeat, while thisEnv is not the same as varEnv,
740
0
        while (this_environment != variable_environment) {
741
            // i. If thisEnv is not an object Environment Record, then
742
0
            if (!is<ObjectEnvironment>(*this_environment)) {
743
                // 1. NOTE: The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.
744
                // 2. For each element name of varNames, do
745
0
                TRY(program.for_each_var_declared_identifier([&](auto const& identifier) -> ThrowCompletionOr<void> {
746
0
                    auto const& name = identifier.string();
747
                    // a. If ! thisEnv.HasBinding(name) is true, then
748
0
                    if (MUST(this_environment->has_binding(name))) {
749
                        // i. Throw a SyntaxError exception.
750
0
                        return vm.throw_completion<SyntaxError>(ErrorType::TopLevelVariableAlreadyDeclared, name);
751
752
                        // FIXME: ii. NOTE: Annex B.3.4 defines alternate semantics for the above step.
753
                        // In particular it only throw the syntax error if it is not an environment from a catchclause.
754
0
                    }
755
                    // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.
756
0
                    return {};
757
0
                }));
758
0
            }
759
760
            // ii. Set thisEnv to thisEnv.[[OuterEnv]].
761
0
            this_environment = this_environment->outer_environment();
762
0
            VERIFY(this_environment);
763
0
        }
764
0
    }
765
766
    // 4. Let privateIdentifiers be a new empty List.
767
    // 5. Let pointer be privateEnv.
768
    // 6. Repeat, while pointer is not null,
769
    //     a. For each Private Name binding of pointer.[[Names]], do
770
    //         i. If privateIdentifiers does not contain binding.[[Description]], append binding.[[Description]] to privateIdentifiers.
771
    //     b. Set pointer to pointer.[[OuterPrivateEnvironment]].
772
    // 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.
773
    // FIXME: Add Private identifiers check here.
774
775
    // 8. Let functionsToInitialize be a new empty List.
776
0
    Vector<FunctionDeclaration const&> functions_to_initialize;
777
778
    // 9. Let declaredFunctionNames be a new empty List.
779
0
    HashTable<DeprecatedFlyString> declared_function_names;
780
781
    // 10. For each element d of varDeclarations, in reverse List order, do
782
0
    TRY(program.for_each_var_function_declaration_in_reverse_order([&](FunctionDeclaration const& function) -> ThrowCompletionOr<void> {
783
        // a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then
784
        // i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.
785
        // Note: This is done by for_each_var_function_declaration_in_reverse_order.
786
787
        // ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.
788
        // iii. Let fn be the sole element of the BoundNames of d.
789
        // iv. If fn is not an element of declaredFunctionNames, then
790
0
        if (declared_function_names.set(function.name()) != AK::HashSetResult::InsertedNewEntry)
791
0
            return {};
792
793
        // 1. If varEnv is a global Environment Record, then
794
0
        if (global_var_environment) {
795
            // a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).
796
797
0
            auto function_definable = TRY(global_var_environment->can_declare_global_function(function.name()));
798
799
            // b. If fnDefinable is false, throw a TypeError exception.
800
0
            if (!function_definable)
801
0
                return vm.throw_completion<TypeError>(ErrorType::CannotDeclareGlobalFunction, function.name());
802
0
        }
803
804
        // 2. Append fn to declaredFunctionNames.
805
        // Note: Already done in step iv.
806
807
        // 3. Insert d as the first element of functionsToInitialize.
808
        // NOTE: Since prepending is much slower, we just append
809
        //       and iterate in reverse order in step 17 below.
810
0
        functions_to_initialize.append(function);
811
0
        return {};
812
0
    }));
813
814
    // 11. NOTE: Annex B.3.2.3 adds additional steps at this point.
815
    // B.3.2.3 Changes to EvalDeclarationInstantiation, https://tc39.es/ecma262/#sec-web-compat-evaldeclarationinstantiation
816
    // 11. If strict is false, then
817
0
    if (!strict) {
818
        // a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.
819
        // The spec here uses 'declaredVarNames' but that has not been declared yet.
820
0
        HashTable<DeprecatedFlyString> hoisted_functions;
821
822
        // b. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause, or DefaultClause Contained within body, do
823
0
        TRY(program.for_each_function_hoistable_with_annexB_extension([&](FunctionDeclaration& function_declaration) -> ThrowCompletionOr<void> {
824
            // i. Let F be StringValue of the BindingIdentifier of f.
825
0
            auto function_name = function_declaration.name();
826
827
            // ii. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier would not produce any Early Errors for body, then
828
            // Note: This is checked during parsing and for_each_function_hoistable_with_annexB_extension so it always passes here.
829
830
            // 1. Let bindingExists be false.
831
            // 2. Let thisEnv be lexEnv.
832
0
            auto* this_environment = lexical_environment;
833
834
            // 3. Assert: The following loop will terminate.
835
836
            // 4. Repeat, while thisEnv is not the same as varEnv,
837
0
            while (this_environment != variable_environment) {
838
                // a. If thisEnv is not an object Environment Record, then
839
0
                if (!is<ObjectEnvironment>(*this_environment)) {
840
                    // i. If ! thisEnv.HasBinding(F) is true, then
841
0
                    if (MUST(this_environment->has_binding(function_name))) {
842
                        // i. Let bindingExists be true.
843
                        // Note: When bindingExists is true we skip all the other steps.
844
0
                        return {};
845
0
                    }
846
0
                }
847
848
                // b. Set thisEnv to thisEnv.[[OuterEnv]].
849
0
                this_environment = this_environment->outer_environment();
850
0
                VERIFY(this_environment);
851
0
            }
852
853
            // Note: At this point bindingExists is false.
854
            // 5. If bindingExists is false and varEnv is a global Environment Record, then
855
0
            if (global_var_environment) {
856
                // a. If varEnv.HasLexicalDeclaration(F) is false, then
857
0
                if (!global_var_environment->has_lexical_declaration(function_name)) {
858
                    // i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F).
859
0
                    if (!TRY(global_var_environment->can_declare_global_var(function_name)))
860
0
                        return {};
861
0
                }
862
                // b. Else,
863
0
                else {
864
                    // i. Let fnDefinable be false.
865
0
                    return {};
866
0
                }
867
0
            }
868
            // 6. Else,
869
            //     a. Let fnDefinable be true.
870
871
            // Note: At this point fnDefinable is true.
872
            // 7. If bindingExists is false and fnDefinable is true, then
873
874
            // a. If declaredFunctionOrVarNames does not contain F, then
875
0
            if (!declared_function_names.contains(function_name) && !hoisted_functions.contains(function_name)) {
876
                // i. If varEnv is a global Environment Record, then
877
0
                if (global_var_environment) {
878
                    // i. Perform ? varEnv.CreateGlobalVarBinding(F, true).
879
0
                    TRY(global_var_environment->create_global_var_binding(function_name, true));
880
0
                }
881
                // ii. Else,
882
0
                else {
883
884
                    // i. Let bindingExists be ! varEnv.HasBinding(F).
885
                    // ii. If bindingExists is false, then
886
0
                    if (!MUST(variable_environment->has_binding(function_name))) {
887
                        // i. Perform ! varEnv.CreateMutableBinding(F, true).
888
0
                        MUST(variable_environment->create_mutable_binding(vm, function_name, true));
889
                        // ii. Perform ! varEnv.InitializeBinding(F, undefined, normal).
890
0
                        MUST(variable_environment->initialize_binding(vm, function_name, js_undefined(), Environment::InitializeBindingHint::Normal));
891
0
                    }
892
0
                }
893
0
            }
894
895
            // iii. Append F to declaredFunctionOrVarNames.
896
0
            hoisted_functions.set(function_name);
897
898
            // b. When the FunctionDeclaration f is evaluated, perform the following steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:
899
            //     i. Let genv be the running execution context's VariableEnvironment.
900
            //     ii. Let benv be the running execution context's LexicalEnvironment.
901
            //     iii. Let fobj be ! benv.GetBindingValue(F, false).
902
            //     iv. Perform ? genv.SetMutableBinding(F, fobj, false).
903
            //     v. Return unused.
904
0
            function_declaration.set_should_do_additional_annexB_steps();
905
906
0
            return {};
907
0
        }));
908
0
    }
909
910
    // 12. Let declaredVarNames be a new empty List.
911
0
    HashTable<DeprecatedFlyString> declared_var_names;
912
913
    // 13. For each element d of varDeclarations, do
914
0
    TRY(program.for_each_var_scoped_variable_declaration([&](VariableDeclaration const& declaration) {
915
        // a. If d is a VariableDeclaration, a ForBinding, or a BindingIdentifier, then
916
        // Note: This is handled by for_each_var_scoped_variable_declaration.
917
918
        // i. For each String vn of the BoundNames of d, do
919
0
        return declaration.for_each_bound_identifier([&](auto const& identifier) -> ThrowCompletionOr<void> {
920
0
            auto const& name = identifier.string();
921
922
            // 1. If vn is not an element of declaredFunctionNames, then
923
0
            if (!declared_function_names.contains(name)) {
924
                // a. If varEnv is a global Environment Record, then
925
0
                if (global_var_environment) {
926
                    // i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).
927
0
                    auto variable_definable = TRY(global_var_environment->can_declare_global_var(name));
928
929
                    // ii. If vnDefinable is false, throw a TypeError exception.
930
0
                    if (!variable_definable)
931
0
                        return vm.throw_completion<TypeError>(ErrorType::CannotDeclareGlobalVariable, name);
932
0
                }
933
934
                // b. If vn is not an element of declaredVarNames, then
935
                // i. Append vn to declaredVarNames.
936
0
                declared_var_names.set(name);
937
0
            }
938
0
            return {};
939
0
        });
940
0
    }));
941
942
    // 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a global Environment Record and the global object is a Proxy exotic object.
943
944
    // 15. Let lexDeclarations be the LexicallyScopedDeclarations of body.
945
    // 16. For each element d of lexDeclarations, do
946
0
    TRY(program.for_each_lexically_scoped_declaration([&](Declaration const& declaration) {
947
        // a. NOTE: Lexically declared names are only instantiated here but not initialized.
948
949
        // b. For each element dn of the BoundNames of d, do
950
0
        return declaration.for_each_bound_identifier([&](auto const& identifier) -> ThrowCompletionOr<void> {
951
0
            auto const& name = identifier.string();
952
953
            // i. If IsConstantDeclaration of d is true, then
954
0
            if (declaration.is_constant_declaration()) {
955
                // 1. Perform ? lexEnv.CreateImmutableBinding(dn, true).
956
0
                TRY(lexical_environment->create_immutable_binding(vm, name, true));
957
0
            }
958
            // ii. Else,
959
0
            else {
960
                // 1. Perform ? lexEnv.CreateMutableBinding(dn, false).
961
0
                TRY(lexical_environment->create_mutable_binding(vm, name, false));
962
0
            }
963
0
            return {};
964
0
        });
965
0
    }));
966
967
    // 17. For each Parse Node f of functionsToInitialize, do
968
    // NOTE: We iterate in reverse order since we appended the functions
969
    //       instead of prepending. We append because prepending is much slower
970
    //       and we only use the created vector here.
971
0
    for (auto& declaration : functions_to_initialize.in_reverse()) {
972
        // a. Let fn be the sole element of the BoundNames of f.
973
        // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.
974
0
        auto function = ECMAScriptFunctionObject::create(realm, declaration.name(), declaration.source_text(), declaration.body(), declaration.parameters(), declaration.function_length(), declaration.local_variables_names(), lexical_environment, private_environment, declaration.kind(), declaration.is_strict_mode(),
975
0
            declaration.parsing_insights());
976
977
        // c. If varEnv is a global Environment Record, then
978
0
        if (global_var_environment) {
979
            // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true).
980
0
            TRY(global_var_environment->create_global_function_binding(declaration.name(), function, true));
981
0
        }
982
        // d. Else,
983
0
        else {
984
            // i. Let bindingExists be ! varEnv.HasBinding(fn).
985
0
            auto binding_exists = MUST(variable_environment->has_binding(declaration.name()));
986
987
            // ii. If bindingExists is false, then
988
0
            if (!binding_exists) {
989
                // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.
990
                // 2. Perform ! varEnv.CreateMutableBinding(fn, true).
991
0
                MUST(variable_environment->create_mutable_binding(vm, declaration.name(), true));
992
993
                // 3. Perform ! varEnv.InitializeBinding(fn, fo, normal).
994
0
                MUST(variable_environment->initialize_binding(vm, declaration.name(), function, Environment::InitializeBindingHint::Normal));
995
0
            }
996
            // iii. Else,
997
0
            else {
998
                // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).
999
0
                MUST(variable_environment->set_mutable_binding(vm, declaration.name(), function, false));
1000
0
            }
1001
0
        }
1002
0
    }
1003
1004
    // 18. For each String vn of declaredVarNames, do
1005
1006
0
    for (auto& var_name : declared_var_names) {
1007
        // a. If varEnv is a global Environment Record, then
1008
0
        if (global_var_environment) {
1009
            // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).
1010
0
            TRY(global_var_environment->create_global_var_binding(var_name, true));
1011
0
        }
1012
        // b. Else,
1013
0
        else {
1014
            // i. Let bindingExists be ! varEnv.HasBinding(vn).
1015
0
            auto binding_exists = MUST(variable_environment->has_binding(var_name));
1016
1017
            // ii. If bindingExists is false, then
1018
0
            if (!binding_exists) {
1019
                // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.
1020
                // 2. Perform ! varEnv.CreateMutableBinding(vn, true).
1021
0
                MUST(variable_environment->create_mutable_binding(vm, var_name, true));
1022
1023
                // 3. Perform ! varEnv.InitializeBinding(vn, undefined, normal).
1024
0
                MUST(variable_environment->initialize_binding(vm, var_name, js_undefined(), Environment::InitializeBindingHint::Normal));
1025
0
            }
1026
0
        }
1027
0
    }
1028
1029
    // 19. Return unused.
1030
0
    return {};
1031
0
}
1032
1033
// 10.4.4.6 CreateUnmappedArgumentsObject ( argumentsList ), https://tc39.es/ecma262/#sec-createunmappedargumentsobject
1034
Object* create_unmapped_arguments_object(VM& vm, ReadonlySpan<Value> arguments)
1035
0
{
1036
0
    auto& realm = *vm.current_realm();
1037
1038
    // 1. Let len be the number of elements in argumentsList.
1039
0
    auto length = arguments.size();
1040
1041
    // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »).
1042
    // 3. Set obj.[[ParameterMap]] to undefined.
1043
0
    auto object = Object::create(realm, realm.intrinsics().object_prototype());
1044
0
    object->set_has_parameter_map();
1045
1046
    // 4. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
1047
0
    MUST(object->define_property_or_throw(vm.names.length, { .value = Value(length), .writable = true, .enumerable = false, .configurable = true }));
1048
1049
    // 5. Let index be 0.
1050
    // 6. Repeat, while index < len,
1051
0
    for (size_t index = 0; index < length; ++index) {
1052
        // a. Let val be argumentsList[index].
1053
0
        auto value = arguments[index];
1054
1055
        // b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).
1056
0
        MUST(object->create_data_property_or_throw(index, value));
1057
1058
        // c. Set index to index + 1.
1059
0
    }
1060
1061
    // 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
1062
0
    auto array_prototype_values = realm.intrinsics().array_prototype_values_function();
1063
0
    MUST(object->define_property_or_throw(vm.well_known_symbol_iterator(), { .value = array_prototype_values, .writable = true, .enumerable = false, .configurable = true }));
1064
1065
    // 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false }).
1066
0
    auto throw_type_error = realm.intrinsics().throw_type_error_function();
1067
0
    MUST(object->define_property_or_throw(vm.names.callee, { .get = throw_type_error, .set = throw_type_error, .enumerable = false, .configurable = false }));
1068
1069
    // 9. Return obj.
1070
0
    return object;
1071
0
}
1072
1073
// 10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env ), https://tc39.es/ecma262/#sec-createmappedargumentsobject
1074
Object* create_mapped_arguments_object(VM& vm, FunctionObject& function, Vector<FunctionParameter> const& formals, ReadonlySpan<Value> arguments, Environment& environment)
1075
0
{
1076
0
    auto& realm = *vm.current_realm();
1077
1078
    // 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers. It may contain duplicate identifiers.
1079
1080
    // 2. Let len be the number of elements in argumentsList.
1081
0
    VERIFY(arguments.size() <= NumericLimits<i32>::max());
1082
0
    i32 length = static_cast<i32>(arguments.size());
1083
1084
    // 3. Let obj be MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »).
1085
    // 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1.
1086
    // 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2.
1087
    // 6. Set obj.[[Get]] as specified in 10.4.4.3.
1088
    // 7. Set obj.[[Set]] as specified in 10.4.4.4.
1089
    // 8. Set obj.[[Delete]] as specified in 10.4.4.5.
1090
    // 9. Set obj.[[Prototype]] to %Object.prototype%.
1091
0
    auto object = vm.heap().allocate<ArgumentsObject>(realm, realm, environment);
1092
1093
    // 14. Let index be 0.
1094
    // 15. Repeat, while index < len,
1095
0
    for (i32 index = 0; index < length; ++index) {
1096
        // a. Let val be argumentsList[index].
1097
0
        auto value = arguments[index];
1098
1099
        // b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).
1100
0
        MUST(object->create_data_property_or_throw(index, value));
1101
1102
        // c. Set index to index + 1.
1103
0
    }
1104
1105
    // 16. Perform ! DefinePropertyOrThrow(obj, "length", PropertyDescriptor { [[Value]]: 𝔽(len), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
1106
0
    MUST(object->define_property_or_throw(vm.names.length, { .value = Value(length), .writable = true, .enumerable = false, .configurable = true }));
1107
1108
    // 17. Let mappedNames be a new empty List.
1109
0
    HashTable<DeprecatedFlyString> mapped_names;
1110
1111
    // 18. Set index to numberOfParameters - 1.
1112
    // 19. Repeat, while index ≥ 0,
1113
0
    VERIFY(formals.size() <= NumericLimits<i32>::max());
1114
0
    for (i32 index = static_cast<i32>(formals.size()) - 1; index >= 0; --index) {
1115
        // a. Let name be parameterNames[index].
1116
0
        auto const& name = formals[index].binding.get<NonnullRefPtr<Identifier const>>()->string();
1117
1118
        // b. If name is not an element of mappedNames, then
1119
0
        if (mapped_names.contains(name))
1120
0
            continue;
1121
1122
        // i. Add name as an element of the list mappedNames.
1123
0
        mapped_names.set(name);
1124
1125
        // ii. If index < len, then
1126
0
        if (index < length) {
1127
            // 1. Let g be MakeArgGetter(name, env).
1128
            // 2. Let p be MakeArgSetter(name, env).
1129
            // 3. Perform ! map.[[DefineOwnProperty]](! ToString(𝔽(index)), PropertyDescriptor { [[Set]]: p, [[Get]]: g, [[Enumerable]]: false, [[Configurable]]: true }).
1130
0
            object->parameter_map().define_native_accessor(
1131
0
                realm,
1132
0
                PropertyKey { index },
1133
0
                [&environment, name](VM& vm) -> ThrowCompletionOr<Value> {
1134
0
                    return MUST(environment.get_binding_value(vm, name, false));
1135
0
                },
1136
0
                [&environment, name](VM& vm) {
1137
0
                    MUST(environment.set_mutable_binding(vm, name, vm.argument(0), false));
1138
0
                    return js_undefined();
1139
0
                },
1140
0
                Attribute::Configurable);
1141
0
        }
1142
0
    }
1143
1144
    // 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor { [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
1145
0
    auto array_prototype_values = realm.intrinsics().array_prototype_values_function();
1146
0
    MUST(object->define_property_or_throw(vm.well_known_symbol_iterator(), { .value = array_prototype_values, .writable = true, .enumerable = false, .configurable = true }));
1147
1148
    // 21. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor { [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).
1149
0
    MUST(object->define_property_or_throw(vm.names.callee, { .value = &function, .writable = true, .enumerable = false, .configurable = true }));
1150
1151
    // 22. Return obj.
1152
0
    return object;
1153
0
}
1154
1155
// 7.1.21 CanonicalNumericIndexString ( argument ), https://tc39.es/ecma262/#sec-canonicalnumericindexstring
1156
CanonicalIndex canonical_numeric_index_string(PropertyKey const& property_key, CanonicalIndexMode mode)
1157
0
{
1158
    // NOTE: If the property name is a number type (An implementation-defined optimized
1159
    // property key type), it can be treated as a string property that has already been
1160
    // converted successfully into a canonical numeric index.
1161
1162
0
    VERIFY(property_key.is_string() || property_key.is_number());
1163
1164
0
    if (property_key.is_number())
1165
0
        return CanonicalIndex(CanonicalIndex::Type::Index, property_key.as_number());
1166
1167
0
    if (mode != CanonicalIndexMode::DetectNumericRoundtrip)
1168
0
        return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1169
1170
0
    auto& argument = property_key.as_string();
1171
1172
    // Handle trivial cases without a full round trip test
1173
    // We do not need to check for argument == "0" at this point because we
1174
    // already covered it with the is_number() == true path.
1175
0
    if (argument.is_empty())
1176
0
        return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1177
0
    u32 current_index = 0;
1178
0
    if (argument.characters()[current_index] == '-') {
1179
0
        current_index++;
1180
0
        if (current_index == argument.length())
1181
0
            return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1182
0
    }
1183
0
    if (argument.characters()[current_index] == '0') {
1184
0
        current_index++;
1185
0
        if (current_index == argument.length())
1186
0
            return CanonicalIndex(CanonicalIndex::Type::Numeric, 0);
1187
0
        if (argument.characters()[current_index] != '.')
1188
0
            return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1189
0
        current_index++;
1190
0
        if (current_index == argument.length())
1191
0
            return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1192
0
    }
1193
1194
    // Short circuit a few common cases
1195
0
    if (argument == "Infinity"sv || argument == "-Infinity"sv || argument == "NaN"sv)
1196
0
        return CanonicalIndex(CanonicalIndex::Type::Numeric, 0);
1197
1198
    // Short circuit any string that doesn't start with digits
1199
0
    if (char first_non_zero = argument.characters()[current_index]; first_non_zero < '0' || first_non_zero > '9')
1200
0
        return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1201
1202
    // 2. Let n be ! ToNumber(argument).
1203
0
    auto maybe_double = argument.to_number<double>(AK::TrimWhitespace::No);
1204
0
    if (!maybe_double.has_value())
1205
0
        return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1206
1207
    // FIXME: We return 0 instead of n but it might not observable?
1208
    // 3. If SameValue(! ToString(n), argument) is true, return n.
1209
0
    if (number_to_string(*maybe_double) == argument.view())
1210
0
        return CanonicalIndex(CanonicalIndex::Type::Numeric, 0);
1211
1212
    // 4. Return undefined.
1213
0
    return CanonicalIndex(CanonicalIndex::Type::Undefined, 0);
1214
0
}
1215
1216
// 22.1.3.19.1 GetSubstitution ( matched, str, position, captures, namedCaptures, replacementTemplate ), https://tc39.es/ecma262/#sec-getsubstitution
1217
ThrowCompletionOr<String> get_substitution(VM& vm, Utf16View const& matched, Utf16View const& str, size_t position, Span<Value> captures, Value named_captures, Value replacement_template)
1218
0
{
1219
    // 1. Let stringLength be the length of str.
1220
0
    auto string_length = str.length_in_code_units();
1221
1222
    // 2. Assert: position ≤ stringLength.
1223
0
    VERIFY(position <= string_length);
1224
1225
    // 3. Let result be the empty String.
1226
0
    Utf16Data result;
1227
1228
    // 4. Let templateRemainder be replacementTemplate.
1229
0
    auto replace_template_string = TRY(replacement_template.to_utf16_string(vm));
1230
0
    auto template_remainder = replace_template_string.view();
1231
1232
    // 5. Repeat, while templateRemainder is not the empty String,
1233
0
    while (!template_remainder.is_empty()) {
1234
        // a. NOTE: The following steps isolate ref (a prefix of templateRemainder), determine refReplacement (its replacement), and then append that replacement to result.
1235
1236
0
        Utf16View ref;
1237
0
        Utf16View ref_replacement;
1238
0
        Optional<Utf16String> capture_string;
1239
1240
        // b. If templateRemainder starts with "$$", then
1241
0
        if (template_remainder.starts_with(u"$$")) {
1242
            // i. Let ref be "$$".
1243
0
            ref = u"$$";
1244
1245
            // ii. Let refReplacement be "$".
1246
0
            ref_replacement = u"$";
1247
0
        }
1248
        // c. Else if templateRemainder starts with "$`", then
1249
0
        else if (template_remainder.starts_with(u"$`")) {
1250
            // i. Let ref be "$`".
1251
0
            ref = u"$`";
1252
1253
            // ii. Let refReplacement be the substring of str from 0 to position.
1254
0
            ref_replacement = str.substring_view(0, position);
1255
0
        }
1256
        // d. Else if templateRemainder starts with "$&", then
1257
0
        else if (template_remainder.starts_with(u"$&")) {
1258
            // i. Let ref be "$&".
1259
0
            ref = u"$&";
1260
1261
            // ii. Let refReplacement be matched.
1262
0
            ref_replacement = matched;
1263
0
        }
1264
        // e. Else if templateRemainder starts with "$'" (0x0024 (DOLLAR SIGN) followed by 0x0027 (APOSTROPHE)), then
1265
0
        else if (template_remainder.starts_with(u"$'")) {
1266
            // i. Let ref be "$'".
1267
0
            ref = u"$'";
1268
1269
            // ii. Let matchLength be the length of matched.
1270
0
            auto match_length = matched.length_in_code_units();
1271
1272
            // iii. Let tailPos be position + matchLength.
1273
0
            auto tail_pos = position + match_length;
1274
1275
            // iv. Let refReplacement be the substring of str from min(tailPos, stringLength).
1276
0
            ref_replacement = str.substring_view(min(tail_pos, string_length));
1277
1278
            // v. NOTE: tailPos can exceed stringLength only if this abstract operation was invoked by a call to the intrinsic @@replace method of %RegExp.prototype% on an object whose "exec" property is not the intrinsic %RegExp.prototype.exec%.
1279
0
        }
1280
        // f. Else if templateRemainder starts with "$" followed by 1 or more decimal digits, then
1281
0
        else if (template_remainder.starts_with(u"$") && template_remainder.length_in_code_units() > 1 && is_ascii_digit(template_remainder.code_unit_at(1))) {
1282
            // i. If templateRemainder starts with "$" followed by 2 or more decimal digits, let digitCount be 2. Otherwise, let digitCount be 1.
1283
0
            size_t digit_count = 1;
1284
1285
0
            if (template_remainder.length_in_code_units() > 2 && is_ascii_digit(template_remainder.code_point_at(2)))
1286
0
                digit_count = 2;
1287
1288
            // ii. Let digits be the substring of templateRemainder from 1 to 1 + digitCount.
1289
0
            auto digits = template_remainder.substring_view(1, digit_count);
1290
1291
            // iii. Let index be ℝ(StringToNumber(digits)).
1292
0
            auto utf8_digits = MUST(digits.to_utf8());
1293
0
            auto index = static_cast<size_t>(string_to_number(utf8_digits));
1294
1295
            // iv. Assert: 0 ≤ index ≤ 99.
1296
0
            VERIFY(index <= 99);
1297
1298
            // v. Let captureLen be the number of elements in captures.
1299
0
            auto capture_length = captures.size();
1300
1301
            // vi. If index > captureLen and digitCount = 2, then
1302
0
            if (index > capture_length && digit_count == 2) {
1303
                // 1. NOTE: When a two-digit replacement pattern specifies an index exceeding the count of capturing groups, it is treated as a one-digit replacement pattern followed by a literal digit.
1304
1305
                // 2. Set digitCount to 1.
1306
0
                digit_count = 1;
1307
1308
                // 3. Set digits to the substring of digits from 0 to 1.
1309
0
                digits = digits.substring_view(0, 1);
1310
1311
                // 4. Set index to ℝ(StringToNumber(digits)).
1312
0
                utf8_digits = MUST(digits.to_utf8());
1313
0
                index = static_cast<size_t>(string_to_number(utf8_digits));
1314
0
            }
1315
1316
            // vii. Let ref be the substring of templateRemainder from 0 to 1 + digitCount.
1317
0
            ref = template_remainder.substring_view(0, 1 + digit_count);
1318
1319
            // viii. If 1 ≤ index ≤ captureLen, then
1320
0
            if (1 <= index && index <= capture_length) {
1321
                // 1. Let capture be captures[index - 1].
1322
0
                auto capture = captures[index - 1];
1323
1324
                // 2. If capture is undefined, then
1325
0
                if (capture.is_undefined()) {
1326
                    // a. Let refReplacement be the empty String.
1327
0
                    ref_replacement = {};
1328
0
                }
1329
                // 3. Else,
1330
0
                else {
1331
                    // a. Let refReplacement be capture.
1332
0
                    capture_string = TRY(capture.to_utf16_string(vm));
1333
0
                    ref_replacement = capture_string->view();
1334
0
                }
1335
0
            }
1336
            // ix. Else,
1337
0
            else {
1338
                // 1. Let refReplacement be ref.
1339
0
                ref_replacement = ref;
1340
0
            }
1341
0
        }
1342
        // g. Else if templateRemainder starts with "$<", then
1343
0
        else if (template_remainder.starts_with(u"$<")) {
1344
            // i. Let gtPos be StringIndexOf(templateRemainder, ">", 0).
1345
            // NOTE: We can actually start at index 2 because we know the string starts with "$<".
1346
0
            auto greater_than_position = string_index_of(template_remainder, u">", 2);
1347
1348
            // ii. If gtPos = -1 or namedCaptures is undefined, then
1349
0
            if (!greater_than_position.has_value() || named_captures.is_undefined()) {
1350
                // 1. Let ref be "$<".
1351
0
                ref = u"$<";
1352
1353
                // 2. Let refReplacement be ref.
1354
0
                ref_replacement = ref;
1355
0
            }
1356
            // iii. Else,
1357
0
            else {
1358
                // 1. Let ref be the substring of templateRemainder from 0 to gtPos + 1.
1359
0
                ref = template_remainder.substring_view(0, *greater_than_position + 1);
1360
1361
                // 2. Let groupName be the substring of templateRemainder from 2 to gtPos.
1362
0
                auto group_name_view = template_remainder.substring_view(2, *greater_than_position - 2);
1363
0
                auto group_name = MUST(group_name_view.to_byte_string(Utf16View::AllowInvalidCodeUnits::Yes));
1364
1365
                // 3. Assert: namedCaptures is an Object.
1366
0
                VERIFY(named_captures.is_object());
1367
1368
                // 4. Let capture be ? Get(namedCaptures, groupName).
1369
0
                auto capture = TRY(named_captures.as_object().get(group_name));
1370
1371
                // 5. If capture is undefined, then
1372
0
                if (capture.is_undefined()) {
1373
                    // a. Let refReplacement be the empty String.
1374
0
                    ref_replacement = {};
1375
0
                }
1376
                // 6. Else,
1377
0
                else {
1378
                    // a. Let refReplacement be ? ToString(capture).
1379
0
                    capture_string = TRY(capture.to_utf16_string(vm));
1380
0
                    ref_replacement = capture_string->view();
1381
0
                }
1382
0
            }
1383
0
        }
1384
        // h. Else,
1385
0
        else {
1386
            // i. Let ref be the substring of templateRemainder from 0 to 1.
1387
0
            ref = template_remainder.substring_view(0, 1);
1388
1389
            // ii. Let refReplacement be ref.
1390
0
            ref_replacement = ref;
1391
0
        }
1392
1393
        // i. Let refLength be the length of ref.
1394
0
        auto ref_length = ref.length_in_code_units();
1395
1396
        // k. Set result to the string-concatenation of result and refReplacement.
1397
0
        result.append(ref_replacement.data(), ref_replacement.length_in_code_points());
1398
1399
        // j. Set templateRemainder to the substring of templateRemainder from refLength.
1400
        // NOTE: We do this step last because refReplacement may point to templateRemainder.
1401
0
        template_remainder = template_remainder.substring_view(ref_length);
1402
0
    }
1403
1404
    // 6. Return result.
1405
0
    return MUST(Utf16View { result }.to_utf8(Utf16View::AllowInvalidCodeUnits::Yes));
1406
0
}
1407
1408
// 2.1.2 AddDisposableResource ( disposable, V, hint [ , method ] ), https://tc39.es/proposal-explicit-resource-management/#sec-adddisposableresource-disposable-v-hint-disposemethod
1409
ThrowCompletionOr<void> add_disposable_resource(VM& vm, Vector<DisposableResource>& disposable, Value value, Environment::InitializeBindingHint hint, FunctionObject* method)
1410
0
{
1411
    // NOTE: For now only sync is a valid hint
1412
0
    VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
1413
1414
0
    Optional<DisposableResource> resource;
1415
1416
    // 1. If method is not present then,
1417
0
    if (!method) {
1418
        // a. If V is null or undefined, return NormalCompletion(empty).
1419
0
        if (value.is_nullish())
1420
0
            return {};
1421
1422
        // b. If Type(V) is not Object, throw a TypeError exception.
1423
0
        if (!value.is_object())
1424
0
            return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
1425
1426
        // c. Let resource be ? CreateDisposableResource(V, hint).
1427
0
        resource = TRY(create_disposable_resource(vm, value, hint));
1428
0
    }
1429
    // 2. Else,
1430
0
    else {
1431
        // a. If V is null or undefined, then
1432
0
        if (value.is_nullish()) {
1433
            // i. Let resource be ? CreateDisposableResource(undefined, hint, method).
1434
0
            resource = TRY(create_disposable_resource(vm, js_undefined(), hint, method));
1435
0
        }
1436
        // b. Else,
1437
0
        else {
1438
            // i. If Type(V) is not Object, throw a TypeError exception.
1439
0
            if (!value.is_object())
1440
0
                return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
1441
1442
            // ii. Let resource be ? CreateDisposableResource(V, hint, method).
1443
0
            resource = TRY(create_disposable_resource(vm, value, hint, method));
1444
0
        }
1445
0
    }
1446
1447
    // 3. Append resource to disposable.[[DisposableResourceStack]].
1448
0
    VERIFY(resource.has_value());
1449
0
    disposable.append(resource.release_value());
1450
1451
    // 4. Return NormalCompletion(empty).
1452
0
    return {};
1453
0
}
1454
1455
// 2.1.3 CreateDisposableResource ( V, hint [ , method ] ), https://tc39.es/proposal-explicit-resource-management/#sec-createdisposableresource
1456
ThrowCompletionOr<DisposableResource> create_disposable_resource(VM& vm, Value value, Environment::InitializeBindingHint hint, FunctionObject* method)
1457
0
{
1458
    // 1. If method is not present, then
1459
0
    if (!method) {
1460
        // a. If V is undefined, throw a TypeError exception.
1461
0
        if (value.is_undefined())
1462
0
            return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "value");
1463
1464
        // b. Set method to ? GetDisposeMethod(V, hint).
1465
0
        method = TRY(get_dispose_method(vm, value, hint));
1466
1467
        // c. If method is undefined, throw a TypeError exception.
1468
0
        if (!method)
1469
0
            return vm.throw_completion<TypeError>(ErrorType::NoDisposeMethod, value.to_string_without_side_effects());
1470
0
    }
1471
    // 2. Else,
1472
    // a. If IsCallable(method) is false, throw a TypeError exception.
1473
    // NOTE: This is guaranteed to never occur from the type.
1474
0
    VERIFY(method);
1475
1476
    // 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }.
1477
    // NOTE: Since we only support sync dispose we don't store the hint for now.
1478
0
    VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
1479
0
    return DisposableResource {
1480
0
        value,
1481
0
        *method
1482
0
    };
1483
0
}
1484
1485
// 2.1.4 GetDisposeMethod ( V, hint ), https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod
1486
ThrowCompletionOr<GCPtr<FunctionObject>> get_dispose_method(VM& vm, Value value, Environment::InitializeBindingHint hint)
1487
0
{
1488
    // NOTE: We only have sync dispose for now which means we ignore step 1.
1489
0
    VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
1490
1491
    // 2. Else,
1492
    // a. Let method be ? GetMethod(V, @@dispose).
1493
0
    return TRY(value.get_method(vm, vm.well_known_symbol_dispose()));
1494
0
}
1495
1496
// 2.1.5 Dispose ( V, hint, method ), https://tc39.es/proposal-explicit-resource-management/#sec-dispose
1497
Completion dispose(VM& vm, Value value, NonnullGCPtr<FunctionObject> method)
1498
0
{
1499
    // 1. Let result be ? Call(method, V).
1500
0
    [[maybe_unused]] auto result = TRY(call(vm, *method, value));
1501
1502
    // NOTE: Hint can only be sync-dispose so we ignore step 2.
1503
    // 2. If hint is async-dispose and result is not undefined, then
1504
    //    a. Perform ? Await(result).
1505
1506
    // 3. Return undefined.
1507
0
    return js_undefined();
1508
0
}
1509
1510
// 2.1.6 DisposeResources ( disposable, completion ), https://tc39.es/proposal-explicit-resource-management/#sec-disposeresources-disposable-completion-errors
1511
Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposable, Completion completion)
1512
0
{
1513
    // 1. If disposable is not undefined, then
1514
    // NOTE: At this point disposable is always defined.
1515
1516
    // a. For each resource of disposable.[[DisposableResourceStack]], in reverse list order, do
1517
0
    for (auto const& resource : disposable.in_reverse()) {
1518
        // i. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]).
1519
0
        auto result = dispose(vm, resource.resource_value, resource.dispose_method);
1520
1521
        // ii. If result.[[Type]] is throw, then
1522
0
        if (result.is_error()) {
1523
            // 1. If completion.[[Type]] is throw, then
1524
0
            if (completion.is_error()) {
1525
                // a. Set result to result.[[Value]].
1526
1527
                // b. Let suppressed be completion.[[Value]].
1528
0
                auto suppressed = completion.value().value();
1529
1530
                // c. Let error be a newly created SuppressedError object.
1531
0
                auto error = SuppressedError::create(*vm.current_realm());
1532
1533
                // d. Perform ! DefinePropertyOrThrow(error, "error", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: result }).
1534
0
                MUST(error->define_property_or_throw(vm.names.error, { .value = result.value(), .writable = true, .enumerable = true, .configurable = true }));
1535
1536
                // e. Perform ! DefinePropertyOrThrow(error, "suppressed", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: suppressed }).
1537
0
                MUST(error->define_property_or_throw(vm.names.suppressed, { .value = suppressed, .writable = true, .enumerable = false, .configurable = true }));
1538
1539
                // f. Set completion to ThrowCompletion(error).
1540
0
                completion = throw_completion(error);
1541
0
            }
1542
            // 2. Else,
1543
0
            else {
1544
                // a. Set completion to result.
1545
0
                completion = result;
1546
0
            }
1547
0
        }
1548
0
    }
1549
1550
    // 2. Return completion.
1551
0
    return completion;
1552
0
}
1553
1554
Completion dispose_resources(VM& vm, GCPtr<DeclarativeEnvironment> disposable, Completion completion)
1555
0
{
1556
    // 1. If disposable is not undefined, then
1557
0
    if (disposable)
1558
0
        return dispose_resources(vm, disposable->disposable_resource_stack(), completion);
1559
1560
    // 2. Return completion.
1561
0
    return completion;
1562
0
}
1563
1564
// https://tc39.es/proposal-import-attributes/#sec-AllImportAttributesSupported
1565
static bool all_import_attributes_supported(VM& vm, Vector<ImportAttribute> const& attributes)
1566
0
{
1567
    // 1. Let supported be HostGetSupportedImportAttributes().
1568
0
    auto supported = vm.host_get_supported_import_attributes();
1569
1570
    // 2. For each ImportAttribute Record attribute of attributes, do
1571
0
    for (auto const& attribute : attributes) {
1572
        // a. If supported does not contain attribute.[[Key]], return false.
1573
0
        if (!supported.contains_slow(attribute.key))
1574
0
            return false;
1575
0
    }
1576
1577
    // 3. Return true.
1578
0
    return true;
1579
0
}
1580
1581
ThrowCompletionOr<Value> perform_import_call(VM& vm, Value specifier, Value options_value)
1582
0
{
1583
0
    auto& realm = *vm.current_realm();
1584
1585
    // 13.3.10.2 EvaluateImportCall ( specifierExpression [ , optionsExpression ] ), https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call
1586
    // 1. Let referrer be GetActiveScriptOrModule().
1587
0
    auto referrer = [&]() -> ImportedModuleReferrer {
1588
0
        auto active_script_or_module = vm.get_active_script_or_module();
1589
1590
        // 2. If referrer is null, set referrer to the current Realm Record.
1591
0
        if (active_script_or_module.has<Empty>())
1592
0
            return NonnullGCPtr<Realm> { realm };
1593
1594
0
        if (active_script_or_module.has<NonnullGCPtr<Script>>())
1595
0
            return active_script_or_module.get<NonnullGCPtr<Script>>();
1596
1597
0
        return NonnullGCPtr<CyclicModule> { verify_cast<CyclicModule>(*active_script_or_module.get<NonnullGCPtr<Module>>()) };
1598
0
    }();
1599
1600
    // 7. Let promiseCapability be ! NewPromiseCapability(%Promise%).
1601
0
    auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
1602
1603
    // 8. Let specifierString be Completion(ToString(specifier)).
1604
    // 9. IfAbruptRejectPromise(specifierString, promiseCapability).
1605
0
    auto specifier_string = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, specifier.to_byte_string(vm));
1606
1607
    // 10. Let attributes be a new empty List.
1608
0
    Vector<ImportAttribute> attributes;
1609
1610
    // 11. If options is not undefined, then
1611
0
    if (!options_value.is_undefined()) {
1612
        // a. If Type(options) is not Object,
1613
0
        if (!options_value.is_object()) {
1614
0
            auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptions")));
1615
            // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
1616
0
            MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
1617
1618
            // ii. Return promiseCapability.[[Promise]].
1619
0
            return Value { promise_capability->promise() };
1620
0
        }
1621
1622
        // b. Let attributesObj be Completion(Get(options, "with")).
1623
        // c. IfAbruptRejectPromise(attributesObj, promiseCapability).
1624
0
        auto attributes_obj = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, options_value.get(vm, vm.names.with));
1625
1626
        // d. Normative Optional, Deprecated
1627
        // 11. If the host supports the deprecated assert keyword for import attributes and attributesObj is undefined, then
1628
0
        if (attributes_obj.is_undefined()) {
1629
            // i. Set attributesObj to Completion(Get(options, "assert")).
1630
            // ii. IfAbruptRejectPromise(attributesObj, promiseCapability).
1631
0
            attributes_obj = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, options_value.get(vm, vm.names.assert));
1632
0
        }
1633
1634
        // e. If attributesObj is not undefined,
1635
0
        if (!attributes_obj.is_undefined()) {
1636
            // i. If Type(attributesObj) is not Object,
1637
0
            if (!attributes_obj.is_object()) {
1638
0
                auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptionsAssertions")));
1639
                // 1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
1640
0
                MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
1641
1642
                // 2. Return promiseCapability.[[Promise]].
1643
0
                return Value { promise_capability->promise() };
1644
0
            }
1645
1646
            // ii. Let entries be Completion(EnumerableOwnProperties(attributesObj, key+value)).
1647
            // iii. IfAbruptRejectPromise(entries, promiseCapability).
1648
0
            auto entries = TRY_OR_REJECT_WITH_VALUE(vm, promise_capability, attributes_obj.as_object().enumerable_own_property_names(Object::PropertyKind::KeyAndValue));
1649
1650
            // iv. For each entry of entries, do
1651
0
            for (auto const& entry : entries) {
1652
                // 1. Let key be ! Get(entry, "0").
1653
0
                auto key = MUST(entry.get(vm, PropertyKey(0)));
1654
1655
                // 2. Let value be ! Get(entry, "1").
1656
0
                auto value = MUST(entry.get(vm, PropertyKey(1)));
1657
1658
                // 3. If Type(value) is not String, then
1659
0
                if (!value.is_string()) {
1660
0
                    auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAString.message(), "Import Assertion option value")));
1661
                    // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
1662
0
                    MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
1663
1664
                    // b. Return promiseCapability.[[Promise]].
1665
0
                    return Value { promise_capability->promise() };
1666
0
                }
1667
1668
                // 4. Append the ImportAttribute Record { [[Key]]: key, [[Value]]: value } to attributes.
1669
0
                attributes.empend(key.as_string().byte_string(), value.as_string().byte_string());
1670
0
            }
1671
0
        }
1672
1673
        // f. If AllImportAttributesSupported(attributes) is false, then
1674
0
        if (!all_import_attributes_supported(vm, attributes)) {
1675
0
            auto error = TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted(ErrorType::NotAnObject.message(), "ImportOptionsAssertions")));
1676
            // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
1677
0
            MUST(call(vm, *promise_capability->reject(), js_undefined(), error));
1678
1679
            // ii. Return promiseCapability.[[Promise]].
1680
0
            return Value { promise_capability->promise() };
1681
0
        }
1682
1683
        // g. Sort attributes according to the lexicographic order of their [[Key]] fields,
1684
        //    treating the value of each such field as a sequence of UTF-16 code unit values.
1685
        //    NOTE: This sorting is observable only in that hosts are prohibited from
1686
        //    distinguishing among attributes by the order they occur in.
1687
        // NOTE: This is done when constructing the ModuleRequest.
1688
0
    }
1689
1690
    // 12. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]: specifierString, [[Attributes]]: attributes }.
1691
0
    ModuleRequest request { specifier_string, attributes };
1692
1693
    // 13. Perform HostLoadImportedModule(referrer, moduleRequest, empty, promiseCapability).
1694
0
    vm.host_load_imported_module(referrer, move(request), nullptr, promise_capability);
1695
1696
    // 13. Return promiseCapability.[[Promise]].
1697
0
    return Value { promise_capability->promise() };
1698
0
}
1699
1700
}