/src/serenity/Userland/Libraries/LibJS/Runtime/ProxyConstructor.cpp
Line | Count | Source (jump to first uncovered line) |
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/Array.h> |
9 | | #include <LibJS/Runtime/Error.h> |
10 | | #include <LibJS/Runtime/GlobalObject.h> |
11 | | #include <LibJS/Runtime/ProxyConstructor.h> |
12 | | #include <LibJS/Runtime/ProxyObject.h> |
13 | | |
14 | | namespace JS { |
15 | | |
16 | | JS_DEFINE_ALLOCATOR(ProxyConstructor); |
17 | | |
18 | | // 10.5.14 ProxyCreate ( target, handler ), https://tc39.es/ecma262/#sec-proxycreate |
19 | | static ThrowCompletionOr<ProxyObject*> proxy_create(VM& vm, Value target, Value handler) |
20 | 0 | { |
21 | 0 | auto& realm = *vm.current_realm(); |
22 | | |
23 | | // 1. If target is not an Object, throw a TypeError exception. |
24 | 0 | if (!target.is_object()) |
25 | 0 | return vm.throw_completion<TypeError>(ErrorType::ProxyConstructorBadType, "target", target.to_string_without_side_effects()); |
26 | | |
27 | | // 2. If handler is not an Object, throw a TypeError exception. |
28 | 0 | if (!handler.is_object()) |
29 | 0 | return vm.throw_completion<TypeError>(ErrorType::ProxyConstructorBadType, "handler", handler.to_string_without_side_effects()); |
30 | | |
31 | | // 3. Let P be MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »). |
32 | | // 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 10.5. |
33 | | // 5. IsCallable(target) is true, then |
34 | | // a. Set P.[[Call]] as specified in 10.5.12. |
35 | | // b. If IsConstructor(target) is true, then |
36 | | // i. Set P.[[Construct]] as specified in 10.5.13. |
37 | | // 6. Set P.[[ProxyTarget]] to target. |
38 | | // 7. Set P.[[ProxyHandler]] to handler. |
39 | | // 8. Return P. |
40 | 0 | return ProxyObject::create(realm, target.as_object(), handler.as_object()).ptr(); |
41 | 0 | } |
42 | | |
43 | | ProxyConstructor::ProxyConstructor(Realm& realm) |
44 | 62 | : NativeFunction(realm.vm().names.Proxy.as_string(), realm.intrinsics().function_prototype()) |
45 | 62 | { |
46 | 62 | } |
47 | | |
48 | | void ProxyConstructor::initialize(Realm& realm) |
49 | 62 | { |
50 | 62 | auto& vm = this->vm(); |
51 | 62 | Base::initialize(realm); |
52 | 62 | u8 attr = Attribute::Writable | Attribute::Configurable; |
53 | 62 | define_native_function(realm, vm.names.revocable, revocable, 2, attr); |
54 | | |
55 | 62 | define_direct_property(vm.names.length, Value(2), Attribute::Configurable); |
56 | 62 | } |
57 | | |
58 | | // 28.2.1.1 Proxy ( target, handler ), https://tc39.es/ecma262/#sec-proxy-target-handler |
59 | | ThrowCompletionOr<Value> ProxyConstructor::call() |
60 | 0 | { |
61 | 0 | auto& vm = this->vm(); |
62 | | |
63 | | // 1. If NewTarget is undefined, throw a TypeError exception. |
64 | 0 | return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, vm.names.Proxy); |
65 | 0 | } |
66 | | |
67 | | // 28.2.1.1 Proxy ( target, handler ), https://tc39.es/ecma262/#sec-proxy-target-handler |
68 | | ThrowCompletionOr<NonnullGCPtr<Object>> ProxyConstructor::construct(FunctionObject&) |
69 | 0 | { |
70 | 0 | auto& vm = this->vm(); |
71 | 0 | auto target = vm.argument(0); |
72 | 0 | auto handler = vm.argument(1); |
73 | | |
74 | | // 2. Return ? ProxyCreate(target, handler). |
75 | 0 | return *TRY(proxy_create(vm, target, handler)); |
76 | 0 | } |
77 | | |
78 | | // 28.2.2.1 Proxy.revocable ( target, handler ), https://tc39.es/ecma262/#sec-proxy.revocable |
79 | | JS_DEFINE_NATIVE_FUNCTION(ProxyConstructor::revocable) |
80 | 0 | { |
81 | 0 | auto& realm = *vm.current_realm(); |
82 | 0 | auto target = vm.argument(0); |
83 | 0 | auto handler = vm.argument(1); |
84 | | |
85 | | // 1. Let p be ? ProxyCreate(target, handler). |
86 | 0 | auto* proxy = TRY(proxy_create(vm, target, handler)); |
87 | | |
88 | | // 2. Let revokerClosure be a new Abstract Closure with no parameters that captures nothing and performs the following steps when called: |
89 | 0 | auto revoker_closure = [proxy_handle = make_handle(proxy)](auto&) -> ThrowCompletionOr<Value> { |
90 | | // a. Let F be the active function object. |
91 | | |
92 | | // b. Let p be F.[[RevocableProxy]]. |
93 | 0 | auto& proxy = const_cast<ProxyObject&>(*proxy_handle.cell()); |
94 | | |
95 | | // c. If p is null, return undefined. |
96 | 0 | if (proxy.is_revoked()) |
97 | 0 | return js_undefined(); |
98 | | |
99 | | // d. Set F.[[RevocableProxy]] to null. |
100 | | // e. Assert: p is a Proxy object. |
101 | | // f. Set p.[[ProxyTarget]] to null. |
102 | | // g. Set p.[[ProxyHandler]] to null. |
103 | 0 | proxy.revoke(); |
104 | | |
105 | | // h. Return undefined. |
106 | 0 | return js_undefined(); |
107 | 0 | }; |
108 | | |
109 | | // 3. Let revoker be CreateBuiltinFunction(revokerClosure, 0, "", « [[RevocableProxy]] »). |
110 | | // 4. Set revoker.[[RevocableProxy]] to p. |
111 | 0 | auto revoker = NativeFunction::create(realm, move(revoker_closure), 0, ""); |
112 | | |
113 | | // 5. Let result be OrdinaryObjectCreate(%Object.prototype%). |
114 | 0 | auto result = Object::create(realm, realm.intrinsics().object_prototype()); |
115 | | |
116 | | // 6. Perform ! CreateDataPropertyOrThrow(result, "proxy", p). |
117 | 0 | MUST(result->create_data_property_or_throw(vm.names.proxy, proxy)); |
118 | | |
119 | | // 7. Perform ! CreateDataPropertyOrThrow(result, "revoke", revoker). |
120 | 0 | MUST(result->create_data_property_or_throw(vm.names.revoke, revoker)); |
121 | | |
122 | | // 8. Return result. |
123 | 0 | return result; |
124 | 0 | } |
125 | | |
126 | | } |