summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS/Runtime/WrappedFunction.cpp
blob: 8c220ba39d976382654a905ce4b53a1bb5e4f049 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ShadowRealm.h>
#include <LibJS/Runtime/WrappedFunction.h>

namespace JS {

// 3.1.1 WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ), https://tc39.es/proposal-shadowrealm/#sec-wrappedfunctioncreate
ThrowCompletionOr<NonnullGCPtr<WrappedFunction>> WrappedFunction::create(Realm& realm, Realm& caller_realm, FunctionObject& target)
{
    auto& vm = realm.vm();

    // 1. Let internalSlotsList be the internal slots listed in Table 2, plus [[Prototype]] and [[Extensible]].
    // 2. Let wrapped be MakeBasicObject(internalSlotsList).
    // 3. Set wrapped.[[Prototype]] to callerRealm.[[Intrinsics]].[[%Function.prototype%]].
    // 4. Set wrapped.[[Call]] as described in 2.1.
    // 5. Set wrapped.[[WrappedTargetFunction]] to Target.
    // 6. Set wrapped.[[Realm]] to callerRealm.
    auto& prototype = *caller_realm.intrinsics().function_prototype();
    auto wrapped = vm.heap().allocate<WrappedFunction>(realm, caller_realm, target, prototype);

    // 7. Let result be CopyNameAndLength(wrapped, Target).
    auto result = copy_name_and_length(vm, *wrapped, target);

    // 8. If result is an Abrupt Completion, throw a TypeError exception.
    if (result.is_throw_completion())
        return vm.throw_completion<TypeError>(ErrorType::WrappedFunctionCopyNameAndLengthThrowCompletion);

    // 9. Return wrapped.
    return wrapped;
}

// 2 Wrapped Function Exotic Objects, https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects
WrappedFunction::WrappedFunction(Realm& realm, FunctionObject& wrapped_target_function, Object& prototype)
    : FunctionObject(prototype)
    , m_wrapped_target_function(wrapped_target_function)
    , m_realm(realm)
{
}

void WrappedFunction::visit_edges(Visitor& visitor)
{
    Base::visit_edges(visitor);

    visitor.visit(&m_wrapped_target_function);
    visitor.visit(&m_realm);
}

// 2.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist
ThrowCompletionOr<Value> WrappedFunction::internal_call(Value this_argument, MarkedVector<Value> arguments_list)
{
    auto& vm = this->vm();

    // 1. Let callerContext be the running execution context.
    // NOTE: No-op, kept by the VM in its execution context stack.

    // 2. Let calleeContext be PrepareForWrappedFunctionCall(F).
    ExecutionContext callee_context { vm.heap() };
    prepare_for_wrapped_function_call(*this, callee_context);

    // 3. Assert: calleeContext is now the running execution context.
    VERIFY(&vm.running_execution_context() == &callee_context);

    // 4. Let result be OrdinaryWrappedFunctionCall(F, thisArgument, argumentsList).
    auto result = ordinary_wrapped_function_call(*this, this_argument, arguments_list);

    // 5. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
    vm.pop_execution_context();

    // NOTE: I think the spec isn't fully correct here, see https://github.com/tc39/proposal-shadowrealm/issues/371.
    // 6. If result.[[Type]] is return, return result.[[Value]].
    // 7. ReturnIfAbrupt(result).
    // 8. Return undefined.
    return result;
}

// 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
ThrowCompletionOr<Value> ordinary_wrapped_function_call(WrappedFunction const& function, Value this_argument, MarkedVector<Value> const& arguments_list)
{
    auto& vm = function.vm();

    // 1. Let target be F.[[WrappedTargetFunction]].
    auto const& target = function.wrapped_target_function();

    // 2. Assert: IsCallable(target) is true.
    VERIFY(Value(&target).is_function());

    // 3. Let callerRealm be F.[[Realm]].
    auto* caller_realm = function.realm();

    // 4. NOTE: Any exception objects produced after this point are associated with callerRealm.
    VERIFY(vm.current_realm() == caller_realm);

    // 5. Let targetRealm be ? GetFunctionRealm(target).
    auto* target_realm = TRY(get_function_realm(vm, target));

    // 6. Let wrappedArgs be a new empty List.
    auto wrapped_args = MarkedVector<Value> { vm.heap() };
    wrapped_args.ensure_capacity(arguments_list.size());

    // 7. For each element arg of argumentsList, do
    for (auto const& arg : arguments_list) {
        // a. Let wrappedValue be ? GetWrappedValue(targetRealm, arg).
        auto wrapped_value = TRY(get_wrapped_value(vm, *target_realm, arg));

        // b. Append wrappedValue to wrappedArgs.
        wrapped_args.append(wrapped_value);
    }

    // 8. Let wrappedThisArgument to ? GetWrappedValue(targetRealm, thisArgument).
    auto wrapped_this_argument = TRY(get_wrapped_value(vm, *target_realm, this_argument));

    // 9. Let result be the Completion Record of Call(target, wrappedThisArgument, wrappedArgs).
    auto result = call(vm, &target, wrapped_this_argument, move(wrapped_args));

    // 10. If result.[[Type]] is normal or result.[[Type]] is return, then
    if (!result.is_throw_completion()) {
        // a. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
        return get_wrapped_value(vm, *caller_realm, result.value());
    }
    // 11. Else,
    else {
        // a. Throw a TypeError exception.
        return vm.throw_completion<TypeError>(ErrorType::WrappedFunctionCallThrowCompletion);
    }

    // NOTE: Also see "Editor's Note" in the spec regarding the TypeError above.
}

// 2.3 PrepareForWrappedFunctionCall ( F: a wrapped function exotic object, ), https://tc39.es/proposal-shadowrealm/#sec-prepare-for-wrapped-function-call
void prepare_for_wrapped_function_call(WrappedFunction const& function, ExecutionContext& callee_context)
{
    auto& vm = function.vm();

    // 1. Let callerContext be the running execution context.
    auto const& caller_context = vm.running_execution_context();

    // 2. Let calleeContext be a new execution context.

    // NOTE: In the specification, PrepareForWrappedFunctionCall "returns" a new callee execution context.
    // To avoid heap allocations, we put our ExecutionContext objects on the C++ stack instead.
    // Whoever calls us should put an ExecutionContext on their stack and pass that as the `callee_context`.

    // 3. Set the Function of calleeContext to F.
    callee_context.function = &const_cast<WrappedFunction&>(function);

    // 4. Let calleeRealm be F.[[Realm]].
    auto* callee_realm = function.realm();

    // 5. Set the Realm of calleeContext to calleeRealm.
    callee_context.realm = callee_realm;

    // 6. Set the ScriptOrModule of calleeContext to null.
    callee_context.script_or_module = {};

    // 7. If callerContext is not already suspended, suspend callerContext.
    // NOTE: We don't support this concept yet.
    (void)caller_context;

    // 8. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
    vm.push_execution_context(callee_context);

    // 9. NOTE: Any exception objects produced after this point are associated with calleeRealm.

    // 10. Return calleeContext.
    // NOTE: No-op, see NOTE after step 2.
}

}