/src/serenity/Userland/Libraries/LibJS/Runtime/WrappedFunction.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Runtime/AbstractOperations.h> |
8 | | #include <LibJS/Runtime/ShadowRealm.h> |
9 | | #include <LibJS/Runtime/WrappedFunction.h> |
10 | | |
11 | | namespace JS { |
12 | | |
13 | | JS_DEFINE_ALLOCATOR(WrappedFunction); |
14 | | |
15 | | // 3.1.1 WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ), https://tc39.es/proposal-shadowrealm/#sec-wrappedfunctioncreate |
16 | | ThrowCompletionOr<NonnullGCPtr<WrappedFunction>> WrappedFunction::create(Realm& realm, Realm& caller_realm, FunctionObject& target) |
17 | 0 | { |
18 | 0 | auto& vm = realm.vm(); |
19 | | |
20 | | // 1. Let internalSlotsList be the internal slots listed in Table 2, plus [[Prototype]] and [[Extensible]]. |
21 | | // 2. Let wrapped be MakeBasicObject(internalSlotsList). |
22 | | // 3. Set wrapped.[[Prototype]] to callerRealm.[[Intrinsics]].[[%Function.prototype%]]. |
23 | | // 4. Set wrapped.[[Call]] as described in 2.1. |
24 | | // 5. Set wrapped.[[WrappedTargetFunction]] to Target. |
25 | | // 6. Set wrapped.[[Realm]] to callerRealm. |
26 | 0 | auto& prototype = *caller_realm.intrinsics().function_prototype(); |
27 | 0 | auto wrapped = vm.heap().allocate<WrappedFunction>(realm, caller_realm, target, prototype); |
28 | | |
29 | | // 7. Let result be CopyNameAndLength(wrapped, Target). |
30 | 0 | auto result = copy_name_and_length(vm, *wrapped, target); |
31 | | |
32 | | // 8. If result is an Abrupt Completion, throw a TypeError exception. |
33 | 0 | if (result.is_throw_completion()) |
34 | 0 | return vm.throw_completion<TypeError>(ErrorType::WrappedFunctionCopyNameAndLengthThrowCompletion); |
35 | | |
36 | | // 9. Return wrapped. |
37 | 0 | return wrapped; |
38 | 0 | } |
39 | | |
40 | | // 2 Wrapped Function Exotic Objects, https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects |
41 | | WrappedFunction::WrappedFunction(Realm& realm, FunctionObject& wrapped_target_function, Object& prototype) |
42 | 0 | : FunctionObject(prototype) |
43 | 0 | , m_wrapped_target_function(wrapped_target_function) |
44 | 0 | , m_realm(realm) |
45 | 0 | { |
46 | 0 | } |
47 | | |
48 | | void WrappedFunction::visit_edges(Visitor& visitor) |
49 | 0 | { |
50 | 0 | Base::visit_edges(visitor); |
51 | |
|
52 | 0 | visitor.visit(m_wrapped_target_function); |
53 | 0 | visitor.visit(m_realm); |
54 | 0 | } |
55 | | |
56 | | // 2.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist |
57 | | ThrowCompletionOr<Value> WrappedFunction::internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) |
58 | 0 | { |
59 | 0 | auto& vm = this->vm(); |
60 | | |
61 | | // 1. Let callerContext be the running execution context. |
62 | | // NOTE: No-op, kept by the VM in its execution context stack. |
63 | | |
64 | | // 2. Let calleeContext be PrepareForWrappedFunctionCall(F). |
65 | 0 | auto callee_context = ExecutionContext::create(); |
66 | 0 | prepare_for_wrapped_function_call(*this, *callee_context); |
67 | | |
68 | | // 3. Assert: calleeContext is now the running execution context. |
69 | 0 | VERIFY(&vm.running_execution_context() == callee_context); |
70 | | |
71 | | // 4. Let result be Completion(OrdinaryWrappedFunctionCall(F, thisArgument, argumentsList)). |
72 | 0 | auto result = ordinary_wrapped_function_call(*this, this_argument, arguments_list); |
73 | | |
74 | | // 5. Remove calleeContext from the execution context stack and restore callerContext as the running execution context. |
75 | 0 | vm.pop_execution_context(); |
76 | | |
77 | | // 6. Return ? result. |
78 | 0 | return result; |
79 | 0 | } |
80 | | |
81 | | // 2.2 OrdinaryWrappedFunctionCall ( F: a wrapped function exotic object, thisArgument: an ECMAScript language value, argumentsList: a List of ECMAScript language values, ), https://tc39.es/proposal-shadowrealm/#sec-ordinary-wrapped-function-call |
82 | | ThrowCompletionOr<Value> ordinary_wrapped_function_call(WrappedFunction const& function, Value this_argument, ReadonlySpan<Value> arguments_list) |
83 | 0 | { |
84 | 0 | auto& vm = function.vm(); |
85 | | |
86 | | // 1. Let target be F.[[WrappedTargetFunction]]. |
87 | 0 | auto const& target = function.wrapped_target_function(); |
88 | | |
89 | | // 2. Assert: IsCallable(target) is true. |
90 | 0 | VERIFY(Value(&target).is_function()); |
91 | | |
92 | | // 3. Let callerRealm be F.[[Realm]]. |
93 | 0 | auto* caller_realm = function.realm(); |
94 | | |
95 | | // 4. NOTE: Any exception objects produced after this point are associated with callerRealm. |
96 | 0 | VERIFY(vm.current_realm() == caller_realm); |
97 | | |
98 | | // 5. Let targetRealm be ? GetFunctionRealm(target). |
99 | 0 | auto* target_realm = TRY(get_function_realm(vm, target)); |
100 | | |
101 | | // 6. Let wrappedArgs be a new empty List. |
102 | 0 | auto wrapped_args = MarkedVector<Value> { vm.heap() }; |
103 | 0 | wrapped_args.ensure_capacity(arguments_list.size()); |
104 | | |
105 | | // 7. For each element arg of argumentsList, do |
106 | 0 | for (auto const& arg : arguments_list) { |
107 | | // a. Let wrappedValue be ? GetWrappedValue(targetRealm, arg). |
108 | 0 | auto wrapped_value = TRY(get_wrapped_value(vm, *target_realm, arg)); |
109 | | |
110 | | // b. Append wrappedValue to wrappedArgs. |
111 | 0 | wrapped_args.append(wrapped_value); |
112 | 0 | } |
113 | | |
114 | | // 8. Let wrappedThisArgument to ? GetWrappedValue(targetRealm, thisArgument). |
115 | 0 | auto wrapped_this_argument = TRY(get_wrapped_value(vm, *target_realm, this_argument)); |
116 | | |
117 | | // 9. Let result be the Completion Record of Call(target, wrappedThisArgument, wrappedArgs). |
118 | 0 | auto result = call(vm, &target, wrapped_this_argument, wrapped_args.span()); |
119 | | |
120 | | // 10. If result.[[Type]] is normal or result.[[Type]] is return, then |
121 | 0 | if (!result.is_throw_completion()) { |
122 | | // a. Return ? GetWrappedValue(callerRealm, result.[[Value]]). |
123 | 0 | return get_wrapped_value(vm, *caller_realm, result.value()); |
124 | 0 | } |
125 | | // 11. Else, |
126 | 0 | else { |
127 | | // a. Throw a TypeError exception. |
128 | 0 | return vm.throw_completion<TypeError>(ErrorType::WrappedFunctionCallThrowCompletion); |
129 | 0 | } |
130 | | |
131 | | // NOTE: Also see "Editor's Note" in the spec regarding the TypeError above. |
132 | 0 | } |
133 | | |
134 | | // 2.3 PrepareForWrappedFunctionCall ( F: a wrapped function exotic object, ), https://tc39.es/proposal-shadowrealm/#sec-prepare-for-wrapped-function-call |
135 | | void prepare_for_wrapped_function_call(WrappedFunction const& function, ExecutionContext& callee_context) |
136 | 0 | { |
137 | 0 | auto& vm = function.vm(); |
138 | | |
139 | | // 1. Let callerContext be the running execution context. |
140 | 0 | auto const& caller_context = vm.running_execution_context(); |
141 | | |
142 | | // 2. Let calleeContext be a new execution context. |
143 | | |
144 | | // NOTE: In the specification, PrepareForWrappedFunctionCall "returns" a new callee execution context. |
145 | | // To avoid heap allocations, we put our ExecutionContext objects on the C++ stack instead. |
146 | | // Whoever calls us should put an ExecutionContext on their stack and pass that as the `callee_context`. |
147 | | |
148 | | // 3. Set the Function of calleeContext to F. |
149 | 0 | callee_context.function = &const_cast<WrappedFunction&>(function); |
150 | | |
151 | | // 4. Let calleeRealm be F.[[Realm]]. |
152 | 0 | auto* callee_realm = function.realm(); |
153 | | |
154 | | // 5. Set the Realm of calleeContext to calleeRealm. |
155 | 0 | callee_context.realm = callee_realm; |
156 | | |
157 | | // 6. Set the ScriptOrModule of calleeContext to null. |
158 | 0 | callee_context.script_or_module = {}; |
159 | | |
160 | | // 7. If callerContext is not already suspended, suspend callerContext. |
161 | | // NOTE: We don't support this concept yet. |
162 | 0 | (void)caller_context; |
163 | | |
164 | | // 8. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. |
165 | 0 | vm.push_execution_context(callee_context); |
166 | | |
167 | | // 9. NOTE: Any exception objects produced after this point are associated with calleeRealm. |
168 | | |
169 | | // 10. Return calleeContext. |
170 | | // NOTE: No-op, see NOTE after step 2. |
171 | 0 | } |
172 | | |
173 | | } |