/src/serenity/Userland/Libraries/LibJS/Runtime/PromiseResolvingElementFunctions.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Runtime/AbstractOperations.h> |
8 | | #include <LibJS/Runtime/AggregateError.h> |
9 | | #include <LibJS/Runtime/Array.h> |
10 | | #include <LibJS/Runtime/GlobalObject.h> |
11 | | #include <LibJS/Runtime/PromiseCapability.h> |
12 | | #include <LibJS/Runtime/PromiseResolvingElementFunctions.h> |
13 | | |
14 | | namespace JS { |
15 | | |
16 | | JS_DEFINE_ALLOCATOR(RemainingElements); |
17 | | JS_DEFINE_ALLOCATOR(PromiseValueList); |
18 | | JS_DEFINE_ALLOCATOR(PromiseResolvingElementFunction); |
19 | | JS_DEFINE_ALLOCATOR(PromiseAllResolveElementFunction); |
20 | | JS_DEFINE_ALLOCATOR(PromiseAllSettledResolveElementFunction); |
21 | | JS_DEFINE_ALLOCATOR(PromiseAllSettledRejectElementFunction); |
22 | | JS_DEFINE_ALLOCATOR(PromiseAnyRejectElementFunction); |
23 | | |
24 | | void PromiseValueList::visit_edges(Visitor& visitor) |
25 | 0 | { |
26 | 0 | Base::visit_edges(visitor); |
27 | 0 | visitor.visit(m_values); |
28 | 0 | } |
29 | | |
30 | | PromiseResolvingElementFunction::PromiseResolvingElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements, Object& prototype) |
31 | 0 | : NativeFunction(prototype) |
32 | 0 | , m_index(index) |
33 | 0 | , m_values(values) |
34 | 0 | , m_capability(capability) |
35 | 0 | , m_remaining_elements(remaining_elements) |
36 | 0 | { |
37 | 0 | } |
38 | | |
39 | | void PromiseResolvingElementFunction::initialize(Realm& realm) |
40 | 0 | { |
41 | 0 | Base::initialize(realm); |
42 | 0 | define_direct_property(vm().names.length, Value(1), Attribute::Configurable); |
43 | 0 | } |
44 | | |
45 | | ThrowCompletionOr<Value> PromiseResolvingElementFunction::call() |
46 | 0 | { |
47 | 0 | if (m_already_called) |
48 | 0 | return js_undefined(); |
49 | 0 | m_already_called = true; |
50 | |
|
51 | 0 | return resolve_element(); |
52 | 0 | } |
53 | | |
54 | | void PromiseResolvingElementFunction::visit_edges(Cell::Visitor& visitor) |
55 | 0 | { |
56 | 0 | Base::visit_edges(visitor); |
57 | |
|
58 | 0 | visitor.visit(m_values); |
59 | 0 | visitor.visit(m_capability); |
60 | 0 | visitor.visit(m_remaining_elements); |
61 | 0 | } |
62 | | |
63 | | NonnullGCPtr<PromiseAllResolveElementFunction> PromiseAllResolveElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements) |
64 | 0 | { |
65 | 0 | return realm.heap().allocate<PromiseAllResolveElementFunction>(realm, index, values, capability, remaining_elements, realm.intrinsics().function_prototype()); |
66 | 0 | } |
67 | | |
68 | | PromiseAllResolveElementFunction::PromiseAllResolveElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements, Object& prototype) |
69 | 0 | : PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype) |
70 | 0 | { |
71 | 0 | } |
72 | | |
73 | | ThrowCompletionOr<Value> PromiseAllResolveElementFunction::resolve_element() |
74 | 0 | { |
75 | 0 | auto& vm = this->vm(); |
76 | 0 | auto& realm = *vm.current_realm(); |
77 | | |
78 | | // 8. Set values[index] to x. |
79 | 0 | m_values->values()[m_index] = vm.argument(0); |
80 | | |
81 | | // 9. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. |
82 | | // 10. If remainingElementsCount.[[Value]] is 0, then |
83 | 0 | if (--m_remaining_elements->value == 0) { |
84 | | // a. Let valuesArray be CreateArrayFromList(values). |
85 | 0 | auto values_array = Array::create_from(realm, m_values->values()); |
86 | | |
87 | | // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »). |
88 | 0 | return JS::call(vm, *m_capability->resolve(), js_undefined(), values_array); |
89 | 0 | } |
90 | | |
91 | | // 11. Return undefined. |
92 | 0 | return js_undefined(); |
93 | 0 | } |
94 | | |
95 | | NonnullGCPtr<PromiseAllSettledResolveElementFunction> PromiseAllSettledResolveElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements) |
96 | 0 | { |
97 | 0 | return realm.heap().allocate<PromiseAllSettledResolveElementFunction>(realm, index, values, capability, remaining_elements, realm.intrinsics().function_prototype()); |
98 | 0 | } |
99 | | |
100 | | PromiseAllSettledResolveElementFunction::PromiseAllSettledResolveElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements, Object& prototype) |
101 | 0 | : PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype) |
102 | 0 | { |
103 | 0 | } |
104 | | |
105 | | ThrowCompletionOr<Value> PromiseAllSettledResolveElementFunction::resolve_element() |
106 | 0 | { |
107 | 0 | auto& vm = this->vm(); |
108 | 0 | auto& realm = *vm.current_realm(); |
109 | | |
110 | | // 9. Let obj be OrdinaryObjectCreate(%Object.prototype%). |
111 | 0 | auto object = Object::create(realm, realm.intrinsics().object_prototype()); |
112 | | |
113 | | // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled"). |
114 | 0 | MUST(object->create_data_property_or_throw(vm.names.status, PrimitiveString::create(vm, "fulfilled"_string))); |
115 | | |
116 | | // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). |
117 | 0 | MUST(object->create_data_property_or_throw(vm.names.value, vm.argument(0))); |
118 | | |
119 | | // 12. Set values[index] to obj. |
120 | 0 | m_values->values()[m_index] = object; |
121 | | |
122 | | // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. |
123 | | // 14. If remainingElementsCount.[[Value]] is 0, then |
124 | 0 | if (--m_remaining_elements->value == 0) { |
125 | | // a. Let valuesArray be CreateArrayFromList(values). |
126 | 0 | auto values_array = Array::create_from(realm, m_values->values()); |
127 | | |
128 | | // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »). |
129 | 0 | return JS::call(vm, *m_capability->resolve(), js_undefined(), values_array); |
130 | 0 | } |
131 | | |
132 | | // 15. Return undefined. |
133 | 0 | return js_undefined(); |
134 | 0 | } |
135 | | |
136 | | NonnullGCPtr<PromiseAllSettledRejectElementFunction> PromiseAllSettledRejectElementFunction::create(Realm& realm, size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements) |
137 | 0 | { |
138 | 0 | return realm.heap().allocate<PromiseAllSettledRejectElementFunction>(realm, index, values, capability, remaining_elements, realm.intrinsics().function_prototype()); |
139 | 0 | } |
140 | | |
141 | | PromiseAllSettledRejectElementFunction::PromiseAllSettledRejectElementFunction(size_t index, PromiseValueList& values, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements, Object& prototype) |
142 | 0 | : PromiseResolvingElementFunction(index, values, capability, remaining_elements, prototype) |
143 | 0 | { |
144 | 0 | } |
145 | | |
146 | | ThrowCompletionOr<Value> PromiseAllSettledRejectElementFunction::resolve_element() |
147 | 0 | { |
148 | 0 | auto& vm = this->vm(); |
149 | 0 | auto& realm = *vm.current_realm(); |
150 | | |
151 | | // 9. Let obj be OrdinaryObjectCreate(%Object.prototype%). |
152 | 0 | auto object = Object::create(realm, realm.intrinsics().object_prototype()); |
153 | | |
154 | | // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected"). |
155 | 0 | MUST(object->create_data_property_or_throw(vm.names.status, PrimitiveString::create(vm, "rejected"_string))); |
156 | | |
157 | | // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). |
158 | 0 | MUST(object->create_data_property_or_throw(vm.names.reason, vm.argument(0))); |
159 | | |
160 | | // 12. Set values[index] to obj. |
161 | 0 | m_values->values()[m_index] = object; |
162 | | |
163 | | // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. |
164 | | // 14. If remainingElementsCount.[[Value]] is 0, then |
165 | 0 | if (--m_remaining_elements->value == 0) { |
166 | | // a. Let valuesArray be CreateArrayFromList(values). |
167 | 0 | auto values_array = Array::create_from(realm, m_values->values()); |
168 | | |
169 | | // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »). |
170 | 0 | return JS::call(vm, *m_capability->resolve(), js_undefined(), values_array); |
171 | 0 | } |
172 | | |
173 | | // 15. Return undefined. |
174 | 0 | return js_undefined(); |
175 | 0 | } |
176 | | |
177 | | NonnullGCPtr<PromiseAnyRejectElementFunction> PromiseAnyRejectElementFunction::create(Realm& realm, size_t index, PromiseValueList& errors, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements) |
178 | 0 | { |
179 | 0 | return realm.heap().allocate<PromiseAnyRejectElementFunction>(realm, index, errors, capability, remaining_elements, realm.intrinsics().function_prototype()); |
180 | 0 | } |
181 | | |
182 | | PromiseAnyRejectElementFunction::PromiseAnyRejectElementFunction(size_t index, PromiseValueList& errors, NonnullGCPtr<PromiseCapability const> capability, RemainingElements& remaining_elements, Object& prototype) |
183 | 0 | : PromiseResolvingElementFunction(index, errors, capability, remaining_elements, prototype) |
184 | 0 | { |
185 | 0 | } |
186 | | |
187 | | ThrowCompletionOr<Value> PromiseAnyRejectElementFunction::resolve_element() |
188 | 0 | { |
189 | 0 | auto& vm = this->vm(); |
190 | 0 | auto& realm = *vm.current_realm(); |
191 | | |
192 | | // 8. Set errors[index] to x. |
193 | 0 | m_values->values()[m_index] = vm.argument(0); |
194 | | |
195 | | // 9. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. |
196 | | // 10. If remainingElementsCount.[[Value]] is 0, then |
197 | 0 | if (--m_remaining_elements->value == 0) { |
198 | | // a. Let error be a newly created AggregateError object. |
199 | 0 | auto error = AggregateError::create(realm); |
200 | | |
201 | | // b. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }). |
202 | 0 | auto errors_array = Array::create_from(realm, m_values->values()); |
203 | 0 | MUST(error->define_property_or_throw(vm.names.errors, { .value = errors_array, .writable = true, .enumerable = false, .configurable = true })); |
204 | | |
205 | | // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »). |
206 | 0 | return JS::call(vm, *m_capability->reject(), js_undefined(), error); |
207 | 0 | } |
208 | | |
209 | 0 | return js_undefined(); |
210 | 0 | } |
211 | | |
212 | | } |