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