Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
3
 * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <LibJS/Runtime/AbstractOperations.h>
9
#include <LibJS/Runtime/Accessor.h>
10
#include <LibJS/Runtime/Array.h>
11
#include <LibJS/Runtime/Error.h>
12
#include <LibJS/Runtime/GlobalObject.h>
13
#include <LibJS/Runtime/PropertyDescriptor.h>
14
#include <LibJS/Runtime/ProxyObject.h>
15
#include <LibJS/Runtime/ValueInlines.h>
16
17
namespace JS {
18
19
JS_DEFINE_ALLOCATOR(ProxyObject);
20
21
// NOTE: We can't rely on native stack overflows to catch infinite recursion in Proxy traps,
22
//       since the compiler may decide to optimize tail/sibling calls into loops.
23
//       So instead we keep track of the recursion depth and throw a TypeError if it exceeds a certain limit.
24
25
static int s_recursion_depth = 0;
26
27
struct RecursionDepthUpdater {
28
0
    RecursionDepthUpdater() { ++s_recursion_depth; }
29
0
    ~RecursionDepthUpdater() { --s_recursion_depth; }
30
};
31
32
#define LIMIT_PROXY_RECURSION_DEPTH()                                                      \
33
0
    RecursionDepthUpdater recursion_depth_updater;                                         \
34
0
    do {                                                                                   \
35
0
        if (s_recursion_depth >= 10000)                                                    \
36
0
            return vm().throw_completion<InternalError>(ErrorType::CallStackSizeExceeded); \
37
0
    } while (0)
38
39
NonnullGCPtr<ProxyObject> ProxyObject::create(Realm& realm, Object& target, Object& handler)
40
0
{
41
0
    return realm.heap().allocate<ProxyObject>(realm, target, handler, realm.intrinsics().object_prototype());
42
0
}
43
44
ProxyObject::ProxyObject(Object& target, Object& handler, Object& prototype)
45
0
    : FunctionObject(prototype, MayInterfereWithIndexedPropertyAccess::Yes)
46
0
    , m_target(target)
47
0
    , m_handler(handler)
48
0
{
49
0
}
50
51
static Value property_key_to_value(VM& vm, PropertyKey const& property_key)
52
0
{
53
0
    VERIFY(property_key.is_valid());
54
0
    if (property_key.is_symbol())
55
0
        return property_key.as_symbol();
56
57
0
    if (property_key.is_string())
58
0
        return PrimitiveString::create(vm, property_key.as_string());
59
60
0
    VERIFY(property_key.is_number());
61
0
    return PrimitiveString::create(vm, ByteString::number(property_key.as_number()));
62
0
}
63
64
// 10.5.1 [[GetPrototypeOf]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof
65
ThrowCompletionOr<Object*> ProxyObject::internal_get_prototype_of() const
66
0
{
67
0
    LIMIT_PROXY_RECURSION_DEPTH();
68
69
0
    auto& vm = this->vm();
70
71
    // 1. Let handler be O.[[ProxyHandler]].
72
73
    // 2. If handler is null, throw a TypeError exception.
74
0
    if (m_is_revoked)
75
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
76
77
    // 3. Assert: Type(handler) is Object.
78
    // 4. Let target be O.[[ProxyTarget]].
79
80
    // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
81
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.getPrototypeOf));
82
83
    // 6. If trap is undefined, then
84
0
    if (!trap) {
85
        // a. Return ? target.[[GetPrototypeOf]]().
86
0
        return TRY(m_target->internal_get_prototype_of());
87
0
    }
88
89
    // 7. Let handlerProto be ? Call(trap, handler, « target »).
90
0
    auto handler_proto = TRY(call(vm, *trap, m_handler, m_target));
91
92
    // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.
93
0
    if (!handler_proto.is_object() && !handler_proto.is_null())
94
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyGetPrototypeOfReturn);
95
96
    // 9. Let extensibleTarget be ? IsExtensible(target).
97
0
    auto extensible_target = TRY(m_target->is_extensible());
98
99
    // 10. If extensibleTarget is true, return handlerProto.
100
0
    if (extensible_target)
101
0
        return handler_proto.is_null() ? nullptr : &handler_proto.as_object();
102
103
    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
104
0
    auto* target_proto = TRY(m_target->internal_get_prototype_of());
105
106
    // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
107
0
    if (!same_value(handler_proto, target_proto))
108
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyGetPrototypeOfNonExtensible);
109
110
    // 13. Return handlerProto.
111
0
    return handler_proto.is_null() ? nullptr : &handler_proto.as_object();
112
0
}
113
114
// 10.5.2 [[SetPrototypeOf]] ( V ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v
115
ThrowCompletionOr<bool> ProxyObject::internal_set_prototype_of(Object* prototype)
116
0
{
117
0
    LIMIT_PROXY_RECURSION_DEPTH();
118
119
0
    auto& vm = this->vm();
120
121
    // 1. Let handler be O.[[ProxyHandler]].
122
123
    // 2. If handler is null, throw a TypeError exception.
124
0
    if (m_is_revoked)
125
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
126
127
    // 3. Assert: Type(handler) is Object.
128
    // 4. Let target be O.[[ProxyTarget]].
129
130
    // 5. Let trap be ? GetMethod(handler, "setPrototypeOf").
131
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.setPrototypeOf));
132
133
    // 6. If trap is undefined, then
134
0
    if (!trap) {
135
        // a. Return ? target.[[SetPrototypeOf]](V).
136
0
        return m_target->internal_set_prototype_of(prototype);
137
0
    }
138
139
    // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, V »)).
140
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, prototype)).to_boolean();
141
142
    // 8. If booleanTrapResult is false, return false.
143
0
    if (!trap_result)
144
0
        return false;
145
146
    // 9. Let extensibleTarget be ? IsExtensible(target).
147
0
    auto extensible_target = TRY(m_target->is_extensible());
148
149
    // 10. If extensibleTarget is true, return true.
150
0
    if (extensible_target)
151
0
        return true;
152
153
    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
154
0
    auto* target_proto = TRY(m_target->internal_get_prototype_of());
155
156
    // 12. If SameValue(V, targetProto) is false, throw a TypeError exception.
157
0
    if (!same_value(prototype, target_proto))
158
0
        return vm.throw_completion<TypeError>(ErrorType::ProxySetPrototypeOfNonExtensible);
159
160
    // 13. Return true.
161
0
    return true;
162
0
}
163
164
// 10.5.3 [[IsExtensible]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible
165
ThrowCompletionOr<bool> ProxyObject::internal_is_extensible() const
166
0
{
167
0
    LIMIT_PROXY_RECURSION_DEPTH();
168
169
0
    auto& vm = this->vm();
170
171
    // 1. Let handler be O.[[ProxyHandler]].
172
173
    // 2. If handler is null, throw a TypeError exception.
174
0
    if (m_is_revoked)
175
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
176
177
    // 3. Assert: Type(handler) is Object.
178
    // 4. Let target be O.[[ProxyTarget]].
179
180
    // 5. Let trap be ? GetMethod(handler, "isExtensible").
181
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.isExtensible));
182
183
    // 6. If trap is undefined, then
184
0
    if (!trap) {
185
        // a. Return ? IsExtensible(target).
186
0
        return m_target->is_extensible();
187
0
    }
188
189
    // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target »)).
190
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target)).to_boolean();
191
192
    // 8. Let targetResult be ? IsExtensible(target).
193
0
    auto target_result = TRY(m_target->is_extensible());
194
195
    // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
196
0
    if (trap_result != target_result)
197
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyIsExtensibleReturn);
198
199
    // 10. Return booleanTrapResult.
200
0
    return trap_result;
201
0
}
202
203
// 10.5.4 [[PreventExtensions]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions
204
ThrowCompletionOr<bool> ProxyObject::internal_prevent_extensions()
205
0
{
206
0
    LIMIT_PROXY_RECURSION_DEPTH();
207
208
0
    auto& vm = this->vm();
209
210
    // 1. Let handler be O.[[ProxyHandler]].
211
212
    // 2. If handler is null, throw a TypeError exception.
213
0
    if (m_is_revoked)
214
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
215
216
    // 3. Assert: Type(handler) is Object.
217
    // 4. Let target be O.[[ProxyTarget]].
218
219
    // 5. Let trap be ? GetMethod(handler, "preventExtensions").
220
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.preventExtensions));
221
222
    // 6. If trap is undefined, then
223
0
    if (!trap) {
224
        // a. Return ? target.[[PreventExtensions]]().
225
0
        return m_target->internal_prevent_extensions();
226
0
    }
227
228
    // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target »)).
229
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target)).to_boolean();
230
231
    // 8. If booleanTrapResult is true, then
232
0
    if (trap_result) {
233
        // a. Let extensibleTarget be ? IsExtensible(target).
234
0
        auto extensible_target = TRY(m_target->is_extensible());
235
236
        // b. If extensibleTarget is true, throw a TypeError exception.
237
0
        if (extensible_target)
238
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyPreventExtensionsReturn);
239
0
    }
240
241
    // 9. Return booleanTrapResult.
242
0
    return trap_result;
243
0
}
244
245
// 10.5.5 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p
246
ThrowCompletionOr<Optional<PropertyDescriptor>> ProxyObject::internal_get_own_property(PropertyKey const& property_key) const
247
0
{
248
0
    LIMIT_PROXY_RECURSION_DEPTH();
249
250
0
    auto& vm = this->vm();
251
252
0
    VERIFY(property_key.is_valid());
253
254
    // 1. Let handler be O.[[ProxyHandler]].
255
256
    // 2. If handler is null, throw a TypeError exception.
257
0
    if (m_is_revoked)
258
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
259
260
    // 3. Assert: Type(handler) is Object.
261
    // 4. Let target be O.[[ProxyTarget]].
262
263
    // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
264
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.getOwnPropertyDescriptor));
265
266
    // 6. If trap is undefined, then
267
0
    if (!trap) {
268
        // a. Return ? target.[[GetOwnProperty]](P).
269
0
        return m_target->internal_get_own_property(property_key);
270
0
    }
271
272
    // 7. Let trapResultObj be ? Call(trap, handler, « target, P »).
273
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, property_key_to_value(vm, property_key)));
274
275
    // 8. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
276
0
    if (!trap_result.is_object() && !trap_result.is_undefined())
277
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyGetOwnDescriptorReturn);
278
279
    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
280
0
    auto target_descriptor = TRY(m_target->internal_get_own_property(property_key));
281
282
    // 10. If trapResultObj is undefined, then
283
0
    if (trap_result.is_undefined()) {
284
        // a. If targetDesc is undefined, return undefined.
285
0
        if (!target_descriptor.has_value())
286
0
            return Optional<PropertyDescriptor> {};
287
288
        // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
289
0
        if (!*target_descriptor->configurable)
290
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyGetOwnDescriptorNonConfigurable);
291
292
        // c. Let extensibleTarget be ? IsExtensible(target).
293
0
        auto extensible_target = TRY(m_target->is_extensible());
294
295
        // d. If extensibleTarget is false, throw a TypeError exception.
296
0
        if (!extensible_target)
297
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyGetOwnDescriptorUndefinedReturn);
298
299
        // e. Return undefined.
300
0
        return Optional<PropertyDescriptor> {};
301
0
    }
302
303
    // 11. Let extensibleTarget be ? IsExtensible(target).
304
0
    auto extensible_target = TRY(m_target->is_extensible());
305
306
    // 12. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
307
0
    auto result_desc = TRY(to_property_descriptor(vm, trap_result));
308
309
    // 13. Perform CompletePropertyDescriptor(resultDesc).
310
0
    result_desc.complete();
311
312
    // 14. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
313
0
    auto valid = is_compatible_property_descriptor(extensible_target, result_desc, target_descriptor);
314
315
    // 15. If valid is false, throw a TypeError exception.
316
0
    if (!valid)
317
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyGetOwnDescriptorInvalidDescriptor);
318
319
    // 16. If resultDesc.[[Configurable]] is false, then
320
0
    if (!*result_desc.configurable) {
321
        // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
322
0
        if (!target_descriptor.has_value() || *target_descriptor->configurable)
323
            // i. Throw a TypeError exception.
324
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyGetOwnDescriptorInvalidNonConfig);
325
326
        // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
327
0
        if (result_desc.writable.has_value() && !*result_desc.writable) {
328
            // i. If targetDesc.[[Writable]] is true, throw a TypeError exception.
329
0
            if (*target_descriptor->writable)
330
0
                return vm.throw_completion<TypeError>(ErrorType::ProxyGetOwnDescriptorNonConfigurableNonWritable);
331
0
        }
332
0
    }
333
334
    // 17. Return resultDesc.
335
0
    return result_desc;
336
0
}
337
338
// 10.5.6 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc
339
ThrowCompletionOr<bool> ProxyObject::internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& property_descriptor, Optional<PropertyDescriptor>*)
340
0
{
341
0
    LIMIT_PROXY_RECURSION_DEPTH();
342
343
0
    auto& vm = this->vm();
344
345
0
    VERIFY(property_key.is_valid());
346
347
    // 1. Let handler be O.[[ProxyHandler]].
348
349
    // 2. If handler is null, throw a TypeError exception.
350
0
    if (m_is_revoked)
351
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
352
353
    // 3. Assert: Type(handler) is Object.
354
    // 4. Let target be O.[[ProxyTarget]].
355
356
    // 5. Let trap be ? GetMethod(handler, "defineProperty").
357
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.defineProperty));
358
359
    // 6. If trap is undefined, then
360
0
    if (!trap) {
361
        // a. Return ? target.[[DefineOwnProperty]](P, Desc).
362
0
        return m_target->internal_define_own_property(property_key, property_descriptor);
363
0
    }
364
365
    // 7. Let descObj be FromPropertyDescriptor(Desc).
366
0
    auto descriptor_object = from_property_descriptor(vm, property_descriptor);
367
368
    // 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P, descObj »)).
369
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, property_key_to_value(vm, property_key), descriptor_object)).to_boolean();
370
371
    // 9. If booleanTrapResult is false, return false.
372
0
    if (!trap_result)
373
0
        return false;
374
375
    // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
376
0
    auto target_descriptor = TRY(m_target->internal_get_own_property(property_key));
377
378
    // 11. Let extensibleTarget be ? IsExtensible(target).
379
0
    auto extensible_target = TRY(m_target->is_extensible());
380
381
    // 12. Else, let settingConfigFalse be false.
382
0
    bool setting_config_false = false;
383
384
    // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
385
0
    if (property_descriptor.configurable.has_value() && !*property_descriptor.configurable) {
386
        // a. Let settingConfigFalse be true.
387
0
        setting_config_false = true;
388
0
    }
389
390
    // 14. If targetDesc is undefined, then
391
0
    if (!target_descriptor.has_value()) {
392
        // a. If extensibleTarget is false, throw a TypeError exception.
393
0
        if (!extensible_target)
394
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyDefinePropNonExtensible);
395
396
        // b. If settingConfigFalse is true, throw a TypeError exception.
397
0
        if (setting_config_false)
398
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyDefinePropNonConfigurableNonExisting);
399
0
    }
400
    // 15. Else,
401
0
    else {
402
        // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.
403
0
        if (!is_compatible_property_descriptor(extensible_target, property_descriptor, target_descriptor))
404
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyDefinePropIncompatibleDescriptor);
405
406
        // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
407
0
        if (setting_config_false && *target_descriptor->configurable)
408
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyDefinePropExistingConfigurable);
409
410
        // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then
411
0
        if (target_descriptor->is_data_descriptor() && !*target_descriptor->configurable && *target_descriptor->writable) {
412
            // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
413
0
            if (property_descriptor.writable.has_value() && !*property_descriptor.writable)
414
0
                return vm.throw_completion<TypeError>(ErrorType::ProxyDefinePropNonWritable);
415
0
        }
416
0
    }
417
418
    // 16. Return true.
419
0
    return true;
420
0
}
421
422
// 10.5.7 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p
423
ThrowCompletionOr<bool> ProxyObject::internal_has_property(PropertyKey const& property_key) const
424
0
{
425
0
    LIMIT_PROXY_RECURSION_DEPTH();
426
427
0
    auto& vm = this->vm();
428
429
0
    VERIFY(property_key.is_valid());
430
431
    // 1. Let handler be O.[[ProxyHandler]].
432
433
    // 2. If handler is null, throw a TypeError exception.
434
0
    if (m_is_revoked)
435
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
436
437
    // 3. Assert: Type(handler) is Object.
438
    // 4. Let target be O.[[ProxyTarget]].
439
440
    // NOTE: We need to protect ourselves from a Proxy with the handler's prototype set to the
441
    // Proxy itself, which would by default bounce between these functions indefinitely and lead to
442
    // a stack overflow when the Proxy's (p) or Proxy handler's (h) Object::get() is called and the
443
    // handler doesn't have a `has` trap:
444
    //
445
    // 1. p -> ProxyObject::internal_has_property()  <- you are here
446
    // 2. target -> Object::internal_has_property()
447
    // 3. target.[[Prototype]] (which is internal_has_property) -> Object::internal_has_property()
448
    //
449
    // In JS code: `const proxy = new Proxy({}, {}); proxy.__proto__ = Object.create(proxy); "foo" in proxy;`
450
0
    if (vm.did_reach_stack_space_limit())
451
0
        return vm.throw_completion<InternalError>(ErrorType::CallStackSizeExceeded);
452
453
    // 5. Let trap be ? GetMethod(handler, "has").
454
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.has));
455
456
    // 6. If trap is undefined, then
457
0
    if (!trap) {
458
        // a. Return ? target.[[HasProperty]](P).
459
0
        return m_target->internal_has_property(property_key);
460
0
    }
461
462
    // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P »)).
463
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, property_key_to_value(vm, property_key))).to_boolean();
464
465
    // 8. If booleanTrapResult is false, then
466
0
    if (!trap_result) {
467
        // a. Let targetDesc be ? target.[[GetOwnProperty]](P).
468
0
        auto target_descriptor = TRY(m_target->internal_get_own_property(property_key));
469
470
        // b. If targetDesc is not undefined, then
471
0
        if (target_descriptor.has_value()) {
472
            // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
473
0
            if (!*target_descriptor->configurable)
474
0
                return vm.throw_completion<TypeError>(ErrorType::ProxyHasExistingNonConfigurable);
475
476
            // ii. Let extensibleTarget be ? IsExtensible(target).
477
0
            auto extensible_target = TRY(m_target->is_extensible());
478
479
            // iii. If extensibleTarget is false, throw a TypeError exception.
480
0
            if (!extensible_target)
481
0
                return vm.throw_completion<TypeError>(ErrorType::ProxyHasExistingNonExtensible);
482
0
        }
483
0
    }
484
485
    // 9. Return booleanTrapResult.
486
0
    return trap_result;
487
0
}
488
489
// 10.5.8 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
490
ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) const
491
0
{
492
0
    LIMIT_PROXY_RECURSION_DEPTH();
493
494
    // NOTE: We don't return any cacheable metadata for proxy lookups.
495
496
0
    VERIFY(!receiver.is_empty());
497
498
0
    auto& vm = this->vm();
499
500
0
    VERIFY(property_key.is_valid());
501
0
    VERIFY(!receiver.is_empty());
502
503
    // 1. Let handler be O.[[ProxyHandler]].
504
505
    // 2. If handler is null, throw a TypeError exception.
506
0
    if (m_is_revoked)
507
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
508
509
    // 3. Assert: Type(handler) is Object.
510
    // 4. Let target be O.[[ProxyTarget]].
511
512
    // NOTE: We need to protect ourselves from a Proxy with its (or handler's) prototype set to the
513
    // Proxy itself, which would by default bounce between these functions indefinitely and lead to
514
    // a stack overflow when the Proxy's (p) or Proxy handler's (h) Object::get() is called and the
515
    // handler doesn't have a `get` trap:
516
    //
517
    // 1. p -> ProxyObject::internal_get()  <- you are here
518
    // 2. h -> Value::get_method()
519
    // 3. h -> Value::get()
520
    // 4. h -> Object::internal_get()
521
    // 5. h -> Object::internal_get_prototype_of() (result is p)
522
    // 6. goto 1
523
    //
524
    // In JS code: `h = {}; p = new Proxy({}, h); h.__proto__ = p; p.foo // or h.foo`
525
0
    if (vm.did_reach_stack_space_limit())
526
0
        return vm.throw_completion<InternalError>(ErrorType::CallStackSizeExceeded);
527
528
    // 5. Let trap be ? GetMethod(handler, "get").
529
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.get));
530
531
    // 6. If trap is undefined, then
532
0
    if (!trap) {
533
        // a. Return ? target.[[Get]](P, Receiver).
534
0
        return m_target->internal_get(property_key, receiver);
535
0
    }
536
537
    // 7. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
538
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, property_key_to_value(vm, property_key), receiver));
539
540
    // 8. Let targetDesc be ? target.[[GetOwnProperty]](P).
541
0
    auto target_descriptor = TRY(m_target->internal_get_own_property(property_key));
542
543
    // 9. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
544
0
    if (target_descriptor.has_value() && !*target_descriptor->configurable) {
545
        // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
546
0
        if (target_descriptor->is_data_descriptor() && !*target_descriptor->writable) {
547
            // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
548
0
            if (!same_value(trap_result, *target_descriptor->value))
549
0
                return vm.throw_completion<TypeError>(ErrorType::ProxyGetImmutableDataProperty);
550
0
        }
551
        // b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
552
0
        if (target_descriptor->is_accessor_descriptor() && !*target_descriptor->get) {
553
            // i. If trapResult is not undefined, throw a TypeError exception.
554
0
            if (!trap_result.is_undefined())
555
0
                return vm.throw_completion<TypeError>(ErrorType::ProxyGetNonConfigurableAccessor);
556
0
        }
557
0
    }
558
559
    // 10. Return trapResult.
560
0
    return trap_result;
561
0
}
562
563
// 10.5.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
564
ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*)
565
0
{
566
0
    LIMIT_PROXY_RECURSION_DEPTH();
567
568
0
    auto& vm = this->vm();
569
570
0
    VERIFY(property_key.is_valid());
571
0
    VERIFY(!value.is_empty());
572
0
    VERIFY(!receiver.is_empty());
573
574
    // 1. Let handler be O.[[ProxyHandler]].
575
576
    // 2. If handler is null, throw a TypeError exception.
577
0
    if (m_is_revoked)
578
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
579
580
    // 3. Assert: Type(handler) is Object.
581
    // 4. Let target be O.[[ProxyTarget]].
582
583
    // NOTE: We need to protect ourselves from a Proxy with its prototype set to the
584
    // Proxy itself, which would by default bounce between these functions indefinitely and lead to
585
    // a stack overflow when the Proxy's (p) or Proxy handler's (h) Object::get() is called and the
586
    // handler doesn't have a `has` trap:
587
    //
588
    // 1. p -> ProxyObject::internal_set()  <- you are here
589
    // 2. target -> Object::internal_set()
590
    // 3. target -> Object::ordinary_set_with_own_descriptor()
591
    // 4. target.[[Prototype]] -> Object::internal_set()
592
    // 5. target.[[Prototype]] -> Object::ordinary_set_with_own_descriptor()
593
    // 6. target.[[Prototype]].[[Prototype]] (which is ProxyObject) -> Object::internal_set()
594
    //
595
    // In JS code: `const proxy = new Proxy({}, {}); proxy.__proto__ = Object.create(proxy); proxy["foo"] = "bar";`
596
0
    if (vm.did_reach_stack_space_limit())
597
0
        return vm.throw_completion<InternalError>(ErrorType::CallStackSizeExceeded);
598
599
    // 5. Let trap be ? GetMethod(handler, "set").
600
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.set));
601
602
    // 6. If trap is undefined, then
603
0
    if (!trap) {
604
        // a. Return ? target.[[Set]](P, V, Receiver).
605
0
        return m_target->internal_set(property_key, value, receiver);
606
0
    }
607
608
    // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
609
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, property_key_to_value(vm, property_key), value, receiver)).to_boolean();
610
611
    // 8. If booleanTrapResult is false, return false.
612
0
    if (!trap_result)
613
0
        return false;
614
615
    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
616
0
    auto target_descriptor = TRY(m_target->internal_get_own_property(property_key));
617
618
    // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
619
0
    if (target_descriptor.has_value() && !*target_descriptor->configurable) {
620
        // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
621
0
        if (target_descriptor->is_data_descriptor() && !*target_descriptor->writable) {
622
            // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
623
0
            if (!same_value(value, *target_descriptor->value))
624
0
                return vm.throw_completion<TypeError>(ErrorType::ProxySetImmutableDataProperty);
625
0
        }
626
        // b. If IsAccessorDescriptor(targetDesc) is true, then
627
0
        if (target_descriptor->is_accessor_descriptor()) {
628
            // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
629
0
            if (!*target_descriptor->set)
630
0
                return vm.throw_completion<TypeError>(ErrorType::ProxySetNonConfigurableAccessor);
631
0
        }
632
0
    }
633
634
    // 11. Return true.
635
0
    return true;
636
0
}
637
638
// 10.5.10 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p
639
ThrowCompletionOr<bool> ProxyObject::internal_delete(PropertyKey const& property_key)
640
0
{
641
0
    LIMIT_PROXY_RECURSION_DEPTH();
642
643
0
    auto& vm = this->vm();
644
645
0
    VERIFY(property_key.is_valid());
646
647
    // 1. Let handler be O.[[ProxyHandler]].
648
649
    // 2. If handler is null, throw a TypeError exception.
650
0
    if (m_is_revoked)
651
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
652
653
    // 3. Assert: Type(handler) is Object.
654
    // 4. Let target be O.[[ProxyTarget]].
655
656
    // 5. Let trap be ? GetMethod(handler, "deleteProperty").
657
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.deleteProperty));
658
659
    // 6. If trap is undefined, then
660
0
    if (!trap) {
661
        // a. Return ? target.[[Delete]](P).
662
0
        return m_target->internal_delete(property_key);
663
0
    }
664
665
    // 7. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P »)).
666
0
    auto trap_result = TRY(call(vm, *trap, m_handler, m_target, property_key_to_value(vm, property_key))).to_boolean();
667
668
    // 8. If booleanTrapResult is false, return false.
669
0
    if (!trap_result)
670
0
        return false;
671
672
    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
673
0
    auto target_descriptor = TRY(m_target->internal_get_own_property(property_key));
674
675
    // 10. If targetDesc is undefined, return true.
676
0
    if (!target_descriptor.has_value())
677
0
        return true;
678
679
    // 11. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
680
0
    if (!*target_descriptor->configurable)
681
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyDeleteNonConfigurable);
682
683
    // 12. Let extensibleTarget be ? IsExtensible(target).
684
0
    auto extensible_target = TRY(m_target->is_extensible());
685
686
    // 13. If extensibleTarget is false, throw a TypeError exception.
687
0
    if (!extensible_target)
688
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyDeleteNonExtensible);
689
690
    // 14. Return true.
691
0
    return true;
692
0
}
693
694
// 10.5.11 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
695
ThrowCompletionOr<MarkedVector<Value>> ProxyObject::internal_own_property_keys() const
696
0
{
697
0
    LIMIT_PROXY_RECURSION_DEPTH();
698
699
0
    auto& vm = this->vm();
700
701
    // 1. Let handler be O.[[ProxyHandler]].
702
703
    // 2. If handler is null, throw a TypeError exception.
704
0
    if (m_is_revoked)
705
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
706
707
    // 3. Assert: Type(handler) is Object.
708
    // 4. Let target be O.[[ProxyTarget]].
709
710
    // 5. Let trap be ? GetMethod(handler, "ownKeys").
711
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.ownKeys));
712
713
    // 6. If trap is undefined, then
714
0
    if (!trap) {
715
        // a. Return ? target.[[OwnPropertyKeys]]().
716
0
        return m_target->internal_own_property_keys();
717
0
    }
718
719
    // 7. Let trapResultArray be ? Call(trap, handler, « target »).
720
0
    auto trap_result_array = TRY(call(vm, *trap, m_handler, m_target));
721
722
    // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »).
723
0
    HashTable<PropertyKey> unique_keys;
724
0
    auto trap_result = TRY(create_list_from_array_like(vm, trap_result_array, [&](auto value) -> ThrowCompletionOr<void> {
725
0
        if (!value.is_string() && !value.is_symbol())
726
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyOwnPropertyKeysNotStringOrSymbol);
727
0
        auto property_key = MUST(value.to_property_key(vm));
728
0
        unique_keys.set(property_key, AK::HashSetExistingEntryBehavior::Keep);
729
0
        return {};
730
0
    }));
731
732
    // 9. If trapResult contains any duplicate entries, throw a TypeError exception.
733
0
    if (unique_keys.size() != trap_result.size())
734
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyOwnPropertyKeysDuplicates);
735
736
    // 10. Let extensibleTarget be ? IsExtensible(target).
737
0
    auto extensible_target = TRY(m_target->is_extensible());
738
739
    // 11. Let targetKeys be ? target.[[OwnPropertyKeys]]().
740
0
    auto target_keys = TRY(m_target->internal_own_property_keys());
741
742
    // 12. Assert: targetKeys is a List of property keys.
743
    // 13. Assert: targetKeys contains no duplicate entries.
744
745
    // 14. Let targetConfigurableKeys be a new empty List.
746
0
    auto target_configurable_keys = MarkedVector<Value> { heap() };
747
748
    // 15. Let targetNonconfigurableKeys be a new empty List.
749
0
    auto target_nonconfigurable_keys = MarkedVector<Value> { heap() };
750
751
    // 16. For each element key of targetKeys, do
752
0
    for (auto& key : target_keys) {
753
0
        auto property_key = MUST(PropertyKey::from_value(vm, key));
754
755
        // a. Let desc be ? target.[[GetOwnProperty]](key).
756
0
        auto descriptor = TRY(m_target->internal_get_own_property(property_key));
757
758
        // b. If desc is not undefined and desc.[[Configurable]] is false, then
759
0
        if (descriptor.has_value() && !*descriptor->configurable) {
760
            // i. Append key as an element of targetNonconfigurableKeys.
761
0
            target_nonconfigurable_keys.append(key);
762
0
        }
763
        // c. Else,
764
0
        else {
765
            // i. Append key as an element of targetConfigurableKeys.
766
0
            target_configurable_keys.append(key);
767
0
        }
768
0
    }
769
770
    // 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then
771
0
    if (extensible_target && target_nonconfigurable_keys.is_empty()) {
772
        // a. Return trapResult.
773
0
        return { move(trap_result) };
774
0
    }
775
776
    // 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult.
777
0
    auto unchecked_result_keys = MarkedVector<Value> { heap() };
778
0
    unchecked_result_keys.extend(trap_result);
779
780
    // 19. For each element key of targetNonconfigurableKeys, do
781
0
    for (auto& key : target_nonconfigurable_keys) {
782
        // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
783
0
        if (!unchecked_result_keys.contains_slow(key))
784
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyOwnPropertyKeysSkippedNonconfigurableProperty, key.to_string_without_side_effects());
785
786
        // b. Remove key from uncheckedResultKeys.
787
0
        unchecked_result_keys.remove_first_matching([&](auto& value) {
788
0
            return same_value(value, key);
789
0
        });
790
0
    }
791
792
    // 20. If extensibleTarget is true, return trapResult.
793
0
    if (extensible_target)
794
0
        return { move(trap_result) };
795
796
    // 21. For each element key of targetConfigurableKeys, do
797
0
    for (auto& key : target_configurable_keys) {
798
        // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
799
0
        if (!unchecked_result_keys.contains_slow(key))
800
0
            return vm.throw_completion<TypeError>(ErrorType::ProxyOwnPropertyKeysNonExtensibleSkippedProperty, key.to_string_without_side_effects());
801
802
        // b. Remove key from uncheckedResultKeys.
803
0
        unchecked_result_keys.remove_first_matching([&](auto& value) {
804
0
            return same_value(value, key);
805
0
        });
806
0
    }
807
808
    // 22. If uncheckedResultKeys is not empty, throw a TypeError exception.
809
0
    if (!unchecked_result_keys.is_empty())
810
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyOwnPropertyKeysNonExtensibleNewProperty, unchecked_result_keys[0].to_string_without_side_effects());
811
812
    // 23. Return trapResult.
813
0
    return { move(trap_result) };
814
0
}
815
816
// 10.5.12 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist
817
ThrowCompletionOr<Value> ProxyObject::internal_call(Value this_argument, ReadonlySpan<Value> arguments_list)
818
0
{
819
0
    LIMIT_PROXY_RECURSION_DEPTH();
820
821
0
    auto& vm = this->vm();
822
0
    auto& realm = *vm.current_realm();
823
824
    // A Proxy exotic object only has a [[Call]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Call]] internal method.
825
0
    VERIFY(is_function());
826
827
    // 1. Let handler be O.[[ProxyHandler]].
828
829
    // 2. If handler is null, throw a TypeError exception.
830
0
    if (m_is_revoked)
831
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
832
833
    // 3. Assert: Type(handler) is Object.
834
    // 4. Let target be O.[[ProxyTarget]].
835
836
    // 5. Let trap be ? GetMethod(handler, "apply").
837
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.apply));
838
839
    // 6. If trap is undefined, then
840
0
    if (!trap) {
841
        // a. Return ? Call(target, thisArgument, argumentsList).
842
0
        return call(vm, m_target, this_argument, arguments_list);
843
0
    }
844
845
    // 7. Let argArray be CreateArrayFromList(argumentsList).
846
0
    auto arguments_array = Array::create_from(realm, arguments_list);
847
848
    // 8. Return ? Call(trap, handler, « target, thisArgument, argArray »).
849
0
    return call(vm, trap, m_handler, m_target, this_argument, arguments_array);
850
0
}
851
852
bool ProxyObject::has_constructor() const
853
0
{
854
    // Note: A Proxy exotic object only has a [[Construct]] internal method if the initial value of
855
    //       its [[ProxyTarget]] internal slot is an object that has a [[Construct]] internal method.
856
0
    if (!is_function())
857
0
        return false;
858
859
0
    return static_cast<FunctionObject&>(*m_target).has_constructor();
860
0
}
861
862
// 10.5.13 [[Construct]] ( argumentsList, newTarget ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget
863
ThrowCompletionOr<NonnullGCPtr<Object>> ProxyObject::internal_construct(ReadonlySpan<Value> arguments_list, FunctionObject& new_target)
864
0
{
865
0
    LIMIT_PROXY_RECURSION_DEPTH();
866
867
0
    auto& vm = this->vm();
868
0
    auto& realm = *vm.current_realm();
869
870
    // A Proxy exotic object only has a [[Construct]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Construct]] internal method.
871
0
    VERIFY(is_function());
872
873
    // 1. Let handler be O.[[ProxyHandler]].
874
875
    // 2. If handler is null, throw a TypeError exception.
876
0
    if (m_is_revoked)
877
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
878
879
    // 3. Assert: Type(handler) is Object.
880
    // 4. Let target be O.[[ProxyTarget]].
881
    // 5. Assert: IsConstructor(target) is true.
882
883
    // 6. Let trap be ? GetMethod(handler, "construct").
884
0
    auto trap = TRY(Value(m_handler).get_method(vm, vm.names.construct));
885
886
    // 7. If trap is undefined, then
887
0
    if (!trap) {
888
        // a. Return ? Construct(target, argumentsList, newTarget).
889
0
        return construct(vm, static_cast<FunctionObject&>(*m_target), arguments_list, &new_target);
890
0
    }
891
892
    // 8. Let argArray be CreateArrayFromList(argumentsList).
893
0
    auto arguments_array = Array::create_from(realm, arguments_list);
894
895
    // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
896
0
    auto new_object = TRY(call(vm, trap, m_handler, m_target, arguments_array, &new_target));
897
898
    // 10. If Type(newObj) is not Object, throw a TypeError exception.
899
0
    if (!new_object.is_object())
900
0
        return vm.throw_completion<TypeError>(ErrorType::ProxyConstructBadReturnType);
901
902
    // 11. Return newObj.
903
0
    return new_object.as_object();
904
0
}
905
906
void ProxyObject::visit_edges(Cell::Visitor& visitor)
907
0
{
908
0
    Base::visit_edges(visitor);
909
0
    visitor.visit(m_target);
910
0
    visitor.visit(m_handler);
911
0
}
912
913
DeprecatedFlyString const& ProxyObject::name() const
914
0
{
915
0
    VERIFY(is_function());
916
0
    return static_cast<FunctionObject&>(*m_target).name();
917
0
}
918
919
}