/src/serenity/Userland/Libraries/LibJS/Runtime/PromiseCapability.cpp
Line | Count | Source (jump to first uncovered line) |
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/Error.h> |
9 | | #include <LibJS/Runtime/NativeFunction.h> |
10 | | #include <LibJS/Runtime/PromiseCapability.h> |
11 | | |
12 | | namespace JS { |
13 | | |
14 | | JS_DEFINE_ALLOCATOR(PromiseCapability); |
15 | | |
16 | | NonnullGCPtr<PromiseCapability> PromiseCapability::create(VM& vm, NonnullGCPtr<Object> promise, NonnullGCPtr<FunctionObject> resolve, NonnullGCPtr<FunctionObject> reject) |
17 | 0 | { |
18 | 0 | return vm.heap().allocate_without_realm<PromiseCapability>(promise, resolve, reject); |
19 | 0 | } |
20 | | |
21 | | PromiseCapability::PromiseCapability(NonnullGCPtr<Object> promise, NonnullGCPtr<FunctionObject> resolve, NonnullGCPtr<FunctionObject> reject) |
22 | 0 | : m_promise(promise) |
23 | 0 | , m_resolve(resolve) |
24 | 0 | , m_reject(reject) |
25 | 0 | { |
26 | 0 | } |
27 | | |
28 | | void PromiseCapability::visit_edges(Cell::Visitor& visitor) |
29 | 0 | { |
30 | 0 | Base::visit_edges(visitor); |
31 | 0 | visitor.visit(m_promise); |
32 | 0 | visitor.visit(m_resolve); |
33 | 0 | visitor.visit(m_reject); |
34 | 0 | } |
35 | | |
36 | | namespace { |
37 | | struct ResolvingFunctions final : public Cell { |
38 | | JS_CELL(ResolvingFunctions, Cell); |
39 | | JS_DECLARE_ALLOCATOR(ResolvingFunctions); |
40 | | |
41 | | Value resolve { js_undefined() }; |
42 | | Value reject { js_undefined() }; |
43 | | |
44 | | virtual void visit_edges(Cell::Visitor& visitor) override |
45 | 0 | { |
46 | 0 | Base::visit_edges(visitor); |
47 | 0 | visitor.visit(resolve); |
48 | 0 | visitor.visit(reject); |
49 | 0 | } |
50 | | }; |
51 | | JS_DEFINE_ALLOCATOR(ResolvingFunctions); |
52 | | } |
53 | | |
54 | | // 27.2.1.5 NewPromiseCapability ( C ), https://tc39.es/ecma262/#sec-newpromisecapability |
55 | | ThrowCompletionOr<NonnullGCPtr<PromiseCapability>> new_promise_capability(VM& vm, Value constructor) |
56 | 0 | { |
57 | 0 | auto& realm = *vm.current_realm(); |
58 | | |
59 | | // 1. If IsConstructor(C) is false, throw a TypeError exception. |
60 | 0 | if (!constructor.is_constructor()) |
61 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, constructor.to_string_without_side_effects()); |
62 | | |
63 | | // 2. NOTE: C is assumed to be a constructor function that supports the parameter conventions of the Promise constructor (see 27.2.3.1). |
64 | | |
65 | | // 3. Let resolvingFunctions be the Record { [[Resolve]]: undefined, [[Reject]]: undefined }. |
66 | 0 | auto resolving_functions = vm.heap().allocate<ResolvingFunctions>(realm); |
67 | | |
68 | | // 4. Let executorClosure be a new Abstract Closure with parameters (resolve, reject) that captures resolvingFunctions and performs the following steps when called: |
69 | 0 | auto executor_closure = [resolving_functions](auto& vm) -> ThrowCompletionOr<Value> { |
70 | 0 | auto resolve = vm.argument(0); |
71 | 0 | auto reject = vm.argument(1); |
72 | | |
73 | | // No idea what other engines say here. |
74 | | // a. If promiseCapability.[[Resolve]] is not undefined, throw a TypeError exception. |
75 | 0 | if (!resolving_functions->resolve.is_undefined()) |
76 | 0 | return vm.template throw_completion<TypeError>(ErrorType::GetCapabilitiesExecutorCalledMultipleTimes); |
77 | | |
78 | | // b. If promiseCapability.[[Reject]] is not undefined, throw a TypeError exception. |
79 | 0 | if (!resolving_functions->reject.is_undefined()) |
80 | 0 | return vm.template throw_completion<TypeError>(ErrorType::GetCapabilitiesExecutorCalledMultipleTimes); |
81 | | |
82 | | // c. Set promiseCapability.[[Resolve]] to resolve. |
83 | 0 | resolving_functions->resolve = resolve; |
84 | | |
85 | | // d. Set promiseCapability.[[Reject]] to reject. |
86 | 0 | resolving_functions->reject = reject; |
87 | | |
88 | | // e. Return undefined. |
89 | 0 | return js_undefined(); |
90 | 0 | }; |
91 | | |
92 | | // 5. Let executor be CreateBuiltinFunction(executorClosure, 2, "", « »). |
93 | 0 | auto executor = NativeFunction::create(realm, move(executor_closure), 2, ""); |
94 | | |
95 | | // 6. Let promise be ? Construct(C, « executor »). |
96 | 0 | auto promise = TRY(construct(vm, constructor.as_function(), executor)); |
97 | | |
98 | | // 7. If IsCallable(resolvingFunctions.[[Resolve]]) is false, throw a TypeError exception. |
99 | 0 | if (!resolving_functions->resolve.is_function()) |
100 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "Promise capability resolve value"); |
101 | | |
102 | | // 8. If IsCallable(resolvingFunctions.[[Reject]]) is false, throw a TypeError exception. |
103 | 0 | if (!resolving_functions->reject.is_function()) |
104 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "Promise capability reject value"); |
105 | | |
106 | | // 9. Return the PromiseCapability Record { [[Promise]]: promise, [[Resolve]]: resolvingFunctions.[[Resolve]], [[Reject]]: resolvingFunctions.[[Reject]] }. |
107 | 0 | return PromiseCapability::create(vm, promise, resolving_functions->resolve.as_function(), resolving_functions->reject.as_function()); |
108 | 0 | } |
109 | | |
110 | | } |