1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | #include <QCoreApplication> |
40 | |
41 | #include <private/qv4promiseobject_p.h> |
42 | #include <private/qv4symbol_p.h> |
43 | #include "qv4jscall_p.h" |
44 | |
45 | using namespace QV4; |
46 | using namespace QV4::Promise; |
47 | |
48 | DEFINE_OBJECT_VTABLE(PromiseReaction); |
49 | DEFINE_OBJECT_VTABLE(PromiseCtor); |
50 | DEFINE_OBJECT_VTABLE(PromiseObject); |
51 | DEFINE_OBJECT_VTABLE(PromiseCapability); |
52 | DEFINE_OBJECT_VTABLE(PromiseExecutionState); |
53 | |
54 | DEFINE_OBJECT_VTABLE(CapabilitiesExecutorWrapper); |
55 | DEFINE_OBJECT_VTABLE(ResolveElementWrapper); |
56 | DEFINE_OBJECT_VTABLE(ResolveWrapper); |
57 | DEFINE_OBJECT_VTABLE(RejectWrapper); |
58 | |
59 | |
60 | namespace { |
61 | |
62 | bool isPromise(const Value &object) |
63 | { |
64 | return object.as<PromiseObject>() != nullptr; |
65 | } |
66 | |
67 | bool isCallable(const Value &object) |
68 | { |
69 | return object.as<FunctionObject>() != nullptr; |
70 | } |
71 | |
72 | void insertIdLengthTag(Scope& scope, Heap::FunctionObject* function) |
73 | { |
74 | ScopedFunctionObject scopedFunction(scope, function); |
75 | scopedFunction->insertMember(s: scope.engine->id_length(), v: Primitive::fromInt32(i: 1), attributes: Attr_NotWritable|Attr_NotEnumerable); |
76 | } |
77 | |
78 | void dropException(QV4::ExecutionEngine* e) |
79 | { |
80 | e->hasException = false; |
81 | } |
82 | } |
83 | |
84 | QT_BEGIN_NAMESPACE |
85 | namespace QV4 { |
86 | namespace Promise { |
87 | |
88 | const int PROMISE_REACTION_EVENT = QEvent::registerEventType(); |
89 | const int PROMISE_RESOLVE_THENABLE_EVENT = QEvent::registerEventType(); |
90 | |
91 | struct ReactionEvent : public QEvent |
92 | { |
93 | ReactionEvent(ExecutionEngine *e, const Value *reaction_, const Value *resolution_) |
94 | : QEvent(QEvent::Type(PROMISE_REACTION_EVENT)), |
95 | reaction{e, *reaction_}, |
96 | resolution{e, *resolution_} |
97 | {} |
98 | |
99 | QV4::PersistentValue reaction; |
100 | QV4::PersistentValue resolution; |
101 | }; |
102 | |
103 | struct ResolveThenableEvent : public QEvent |
104 | { |
105 | ResolveThenableEvent(ExecutionEngine *e, const PromiseObject *promise_, const Object *thenable_, const FunctionObject *then_) |
106 | : QEvent(QEvent::Type(PROMISE_RESOLVE_THENABLE_EVENT)), promise(e, *promise_), thenable(e, *thenable_), then(e, *then_) |
107 | {} |
108 | |
109 | QV4::PersistentValue promise; |
110 | QV4::PersistentValue thenable; |
111 | QV4::PersistentValue then; |
112 | }; |
113 | |
114 | |
115 | } // namespace Promise |
116 | } // namespace QV4 |
117 | QT_END_NAMESPACE |
118 | |
119 | ReactionHandler::ReactionHandler(QObject *parent) |
120 | : QObject(parent) |
121 | {} |
122 | |
123 | ReactionHandler::~ReactionHandler() |
124 | {} |
125 | |
126 | void ReactionHandler::addReaction(ExecutionEngine *e, const Value *reaction, const Value *value) |
127 | { |
128 | QCoreApplication::postEvent(receiver: this, event: new ReactionEvent(e, reaction, value)); |
129 | } |
130 | |
131 | void ReactionHandler::addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then) |
132 | { |
133 | QCoreApplication::postEvent(receiver: this, event: new ResolveThenableEvent(e, promise, thenable, then)); |
134 | } |
135 | |
136 | void ReactionHandler::customEvent(QEvent *event) |
137 | { |
138 | if (event) |
139 | { |
140 | const int type = event->type(); |
141 | if (type == PROMISE_REACTION_EVENT) |
142 | executeReaction(event: static_cast<ReactionEvent*>(event)); |
143 | |
144 | if (type == PROMISE_RESOLVE_THENABLE_EVENT) |
145 | executeResolveThenable(event: static_cast<ResolveThenableEvent*>(event)); |
146 | } |
147 | } |
148 | |
149 | void ReactionHandler::executeReaction(ReactionEvent *event) |
150 | { |
151 | Scope scope(event->reaction.engine()); |
152 | |
153 | Scoped<QV4::PromiseReaction> ro(scope, event->reaction.as<QV4::PromiseReaction>()); |
154 | Scoped<QV4::PromiseCapability> capability(scope, ro->d()->capability); |
155 | |
156 | ScopedValue resolution(scope, event->resolution.value()); |
157 | ScopedValue promise(scope, capability->d()->promise); |
158 | |
159 | if (ro->d()->type == Heap::PromiseReaction::Function) { |
160 | ScopedFunctionObject handler(scope, ro->d()->handler.as<QV4::FunctionObject>()); |
161 | ScopedValue result(scope, handler->call(thisObject: promise, argv: resolution, argc: 1)); |
162 | |
163 | ScopedFunctionObject reaction(scope); |
164 | if (scope.hasException()) { |
165 | reaction = capability->d()->reject.as<QV4::FunctionObject>(); |
166 | result = scope.engine->catchException(); |
167 | } else { |
168 | reaction = capability->d()->resolve.as<QV4::FunctionObject>(); |
169 | } |
170 | |
171 | reaction->call(thisObject: promise, argv: result, argc: 1); |
172 | } else { |
173 | ScopedFunctionObject reaction(scope); |
174 | if (ro->d()->type == Heap::PromiseReaction::Identity) { |
175 | reaction = capability->d()->resolve.as<QV4::FunctionObject>(); |
176 | } else { |
177 | reaction = capability->d()->reject.as<QV4::FunctionObject>(); |
178 | } |
179 | |
180 | reaction->call(thisObject: promise, argv: resolution, argc: 1); |
181 | } |
182 | } |
183 | |
184 | |
185 | namespace { |
186 | |
187 | class FunctionBuilder { |
188 | public: |
189 | static Heap::FunctionObject *makeResolveFunction(ExecutionEngine* e, QV4::Heap::PromiseObject *promise) { |
190 | Scope scope(e); |
191 | Scoped<QV4::ResolveWrapper> resolveWrapper(scope, e->memoryManager->allocate<QV4::ResolveWrapper>()); |
192 | |
193 | insertIdLengthTag(scope, function: resolveWrapper->d()); |
194 | resolveWrapper->d()->promise.set(e, newVal: promise); |
195 | |
196 | return resolveWrapper->d(); |
197 | } |
198 | |
199 | static Heap::FunctionObject *makeRejectFunction(ExecutionEngine* e, QV4::Heap::PromiseObject *promise) { |
200 | Scope scope(e); |
201 | Scoped<QV4::RejectWrapper> rejectWrapper(scope, e->memoryManager->allocate<QV4::RejectWrapper>()); |
202 | |
203 | insertIdLengthTag(scope, function: rejectWrapper->d()); |
204 | rejectWrapper->d()->promise.set(e, newVal: promise); |
205 | |
206 | return rejectWrapper->d(); |
207 | } |
208 | |
209 | static Heap::FunctionObject *makeResolveElementFunction(ExecutionEngine* e, uint index, Heap::PromiseExecutionState *executionState) |
210 | { |
211 | Scope scope(e); |
212 | Scoped<QV4::ResolveElementWrapper> resolveElementWrapper(scope, e->memoryManager->allocate<QV4::ResolveElementWrapper>()); |
213 | |
214 | resolveElementWrapper->d()->index = index; |
215 | resolveElementWrapper->d()->alreadyResolved = false; |
216 | resolveElementWrapper->d()->state.set(e, b: executionState); |
217 | |
218 | insertIdLengthTag(scope, function: resolveElementWrapper->d()); |
219 | |
220 | return resolveElementWrapper->d(); |
221 | } |
222 | }; |
223 | |
224 | } |
225 | |
226 | |
227 | void ReactionHandler::executeResolveThenable(ResolveThenableEvent *event) |
228 | { |
229 | Scope scope(event->then.engine()); |
230 | JSCallData jsCallData(scope, 2); |
231 | PromiseObject *promise = event->promise.as<PromiseObject>(); |
232 | ScopedFunctionObject resolve {scope, FunctionBuilder::makeResolveFunction(e: scope.engine, promise: promise->d())}; |
233 | ScopedFunctionObject reject {scope, FunctionBuilder::makeRejectFunction(e: scope.engine, promise: promise->d())}; |
234 | jsCallData->args[0] = resolve; |
235 | jsCallData.args[1] = reject; |
236 | jsCallData->thisObject = event->thenable.as<QV4::Object>(); |
237 | event->then.as<const FunctionObject>()->call(data: jsCallData); |
238 | if (scope.engine->hasException) { |
239 | JSCallData rejectCallData(scope, 1); |
240 | rejectCallData->args[0] = scope.engine->catchException(); |
241 | Scoped<RejectWrapper> reject {scope, scope.engine->memoryManager->allocate<QV4::RejectWrapper>()}; |
242 | reject->call(data: rejectCallData); |
243 | } |
244 | } |
245 | |
246 | void Heap::PromiseObject::setState(PromiseObject::State state) |
247 | { |
248 | this->state = state; |
249 | } |
250 | |
251 | bool Heap::PromiseObject::isSettled() const |
252 | { |
253 | return (state != Pending); |
254 | } |
255 | |
256 | bool Heap::PromiseObject::isPending() const |
257 | { |
258 | return (state == Pending); |
259 | } |
260 | |
261 | bool Heap::PromiseObject::isFulfilled() const |
262 | { |
263 | return (state == Fulfilled); |
264 | } |
265 | |
266 | bool Heap::PromiseObject::isRejected() const |
267 | { |
268 | return (state == Rejected); |
269 | } |
270 | |
271 | void Heap::PromiseObject::triggerFullfillReactions(ExecutionEngine *e) |
272 | { |
273 | Scope scope(e); |
274 | ScopedArrayObject a(scope, fulfillReactions); |
275 | if (a->arrayData()) { |
276 | Scoped<QV4::ArrayData> ad(scope, a->arrayData()); |
277 | const uint sz = ad->length(); |
278 | ScopedValue value(scope, resolution); |
279 | for (uint i = 0; i < sz; i++) { |
280 | Scoped<QV4::PromiseReaction> r(scope, ad->get(i)); |
281 | r->d()->triggerWithValue(e: scope.engine, value); |
282 | } |
283 | } |
284 | } |
285 | |
286 | void Heap::PromiseObject::triggerRejectReactions(ExecutionEngine *e) |
287 | { |
288 | Scope scope(e); |
289 | ScopedArrayObject a(scope, rejectReactions); |
290 | if (a->arrayData()) { |
291 | Scoped<QV4::ArrayData> ad(scope, a->arrayData()); |
292 | const uint sz = ad->d()->length(); |
293 | ScopedValue value(scope, resolution); |
294 | for (uint i = 0; i < sz; i++) { |
295 | Scoped<QV4::PromiseReaction> r(scope, ad->d()->get(i)); |
296 | r->d()->triggerWithValue(e: scope.engine, value); |
297 | } |
298 | } |
299 | } |
300 | |
301 | Heap::PromiseReaction *Heap::PromiseReaction::createFulfillReaction(ExecutionEngine* e, |
302 | const QV4::PromiseCapability *capability, const QV4::FunctionObject *onFulfilled) |
303 | { |
304 | Scope scope(e); |
305 | Scoped<QV4::PromiseReaction> fulfillReaction(scope, e->memoryManager->allocate<QV4::PromiseReaction>()); |
306 | fulfillReaction->d()->capability.set(e, newVal: capability->d()); |
307 | |
308 | if (onFulfilled) { |
309 | QV4::ScopedFunctionObject scopedFullfillReaction(scope, onFulfilled); |
310 | if (!scopedFullfillReaction) { |
311 | fulfillReaction->d()->type = PromiseReaction::Identity; |
312 | } else { |
313 | fulfillReaction->d()->type = PromiseReaction::Function; |
314 | fulfillReaction->d()->handler.set(e, newVal: scopedFullfillReaction); |
315 | } |
316 | } else { |
317 | fulfillReaction->d()->type = PromiseReaction::Identity; |
318 | } |
319 | |
320 | return fulfillReaction->d(); |
321 | } |
322 | |
323 | Heap::PromiseReaction *Heap::PromiseReaction::createRejectReaction(ExecutionEngine* e, |
324 | const QV4::PromiseCapability *capability, const QV4::FunctionObject *onRejected) |
325 | { |
326 | Scope scope(e); |
327 | Scoped<QV4::PromiseReaction> rejectReaction(scope, e->memoryManager->allocate<QV4::PromiseReaction>()); |
328 | rejectReaction->d()->capability.set(e, newVal: capability->d()); |
329 | |
330 | if (onRejected) { |
331 | ScopedFunctionObject scopedRejectReaction(scope, onRejected); |
332 | if (!scopedRejectReaction) { |
333 | rejectReaction->d()->type = PromiseReaction::Thrower; |
334 | } else { |
335 | rejectReaction->d()->type = PromiseReaction::Function; |
336 | rejectReaction->d()->handler.set(e, newVal: scopedRejectReaction); |
337 | } |
338 | } else { |
339 | rejectReaction->d()->type = PromiseReaction::Thrower; |
340 | } |
341 | |
342 | return rejectReaction->d(); |
343 | } |
344 | |
345 | void Heap::PromiseReaction::triggerWithValue(ExecutionEngine *e, const Value *value) |
346 | { |
347 | Scope scope(e); |
348 | auto handler = e->getPromiseReactionHandler(); |
349 | ScopedValue reaction(scope, Value::fromHeapObject(m: this)); |
350 | handler->addReaction(e, reaction, value); |
351 | } |
352 | |
353 | void Heap::PromiseCtor::init(QV4::ExecutionContext *scope) |
354 | { |
355 | Heap::FunctionObject::init(scope, QStringLiteral("Promise" )); |
356 | } |
357 | |
358 | void Heap::PromiseObject::init(ExecutionEngine *e) |
359 | { |
360 | Heap::Object::init(); |
361 | |
362 | { |
363 | Heap::ArrayObject* a = e->newArrayObject(); |
364 | fulfillReactions.set(e, b: a); |
365 | } |
366 | |
367 | { |
368 | Heap::ArrayObject* a = e->newArrayObject(); |
369 | rejectReactions.set(e, b: a); |
370 | } |
371 | } |
372 | |
373 | void Heap::CapabilitiesExecutorWrapper::init() |
374 | { |
375 | Heap::FunctionObject::init(); |
376 | } |
377 | |
378 | void Heap::CapabilitiesExecutorWrapper::destroy() |
379 | { |
380 | Heap::FunctionObject::destroy(); |
381 | } |
382 | |
383 | void Heap::PromiseExecutionState::init() |
384 | { |
385 | index = 0; |
386 | remainingElementCount = 0; |
387 | } |
388 | |
389 | void Heap::ResolveElementWrapper::init() |
390 | { |
391 | index = 0; |
392 | alreadyResolved = false; |
393 | |
394 | Heap::FunctionObject::init(); |
395 | } |
396 | |
397 | void Heap::ResolveWrapper::init() |
398 | { |
399 | alreadyResolved = false; |
400 | Heap::FunctionObject::init(); |
401 | } |
402 | |
403 | void Heap::RejectWrapper::init() |
404 | { |
405 | alreadyResolved = false; |
406 | Heap::FunctionObject::init(); |
407 | } |
408 | |
409 | ReturnedValue PromiseCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) |
410 | { |
411 | // 25.4.3.1 Promise ( executor ) |
412 | // 1. If NewTarget is undefined, throw a TypeError exception. |
413 | Scope scope(f); |
414 | THROW_TYPE_ERROR(); |
415 | } |
416 | |
417 | ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
418 | { |
419 | // 25.4.3.1 Promise ( executor ) |
420 | |
421 | Scope scope(f); |
422 | |
423 | if (argc == 0) // If there are no arguments, argument 1 will be undefined ==> thus not callable ==> type error |
424 | THROW_TYPE_ERROR(); |
425 | |
426 | ScopedFunctionObject executor(scope, argv[0].as<const FunctionObject>()); |
427 | if (!executor) //2. If IsCallable(executor) is false |
428 | THROW_TYPE_ERROR(); // throw a TypeError exception |
429 | |
430 | Scoped<PromiseObject> a(scope, scope.engine->newPromiseObject()); |
431 | if (scope.engine->hasException) |
432 | return Encode::undefined(); |
433 | |
434 | a->d()->state = Heap::PromiseObject::Pending; //4. Set promise.[[PromiseState]] to "pending" |
435 | // 5. Set promise.[[PromiseFulfillReactions]] to a new empty List. |
436 | // 6. Set promise.[[PromiseRejectReactions]] to a new empty List. |
437 | // 7. Set promise.[[PromiseIsHandled]] to false. |
438 | // happens in constructor of a |
439 | |
440 | ScopedFunctionObject resolve(scope, FunctionBuilder::makeResolveFunction(e: scope.engine, promise: a->d())); |
441 | ScopedFunctionObject reject(scope, FunctionBuilder::makeRejectFunction(e: scope.engine, promise: a->d())); |
442 | |
443 | JSCallData jsCallData(scope, 2); |
444 | jsCallData->args[0] = resolve; |
445 | jsCallData->args[1] = reject; |
446 | //jsCallData->thisObject = a; VERIFY corretness, but this should be undefined (see below) |
447 | |
448 | executor->call(data: jsCallData); // 9. Let completion be Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »). |
449 | |
450 | if (scope.engine->hasException) { |
451 | ScopedValue exception {scope, scope.engine->catchException()}; |
452 | JSCallData callData {scope, 1}; |
453 | callData.args[0] = exception; |
454 | reject->call(data: callData); |
455 | } |
456 | |
457 | if (newTarget) |
458 | a->setProtoFromNewTarget(newTarget); |
459 | |
460 | return a->asReturnedValue(); |
461 | } |
462 | |
463 | |
464 | ReturnedValue PromiseCtor::method_resolve(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
465 | { |
466 | // 25.4.4.5Promise.resolve ( x ) |
467 | Scope scope(f); |
468 | ExecutionEngine* e = scope.engine; |
469 | if (!thisObject || !thisObject->isObject()) // 2. If Type(C) is not Object, throw a TypeError exception |
470 | THROW_TYPE_ERROR(); |
471 | |
472 | ScopedValue x(scope); |
473 | if (argc < 1) { |
474 | x = Encode::undefined(); |
475 | } else { |
476 | x = argv[0]; |
477 | } |
478 | |
479 | // 3. If IsPromise(x) is true, then |
480 | if (isPromise(object: x) && x->isObject()) { |
481 | ScopedObject so(scope, thisObject); |
482 | // Let xConstructor be ? Get(x, "constructor"). |
483 | ScopedObject constructor(scope, x->objectValue()->get(name: e->id_constructor())); |
484 | if (so->d() == constructor->d()) // If SameValue(xConstructor, C) is true, return x. |
485 | return x->asReturnedValue(); |
486 | } |
487 | |
488 | // Let promiseCapability be ? NewPromiseCapability(C). |
489 | Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>()); |
490 | |
491 | ScopedObject newPromise(scope, e->newPromiseObject(thisObject: thisObject->as<const FunctionObject>(), capability)); |
492 | if (!newPromise || !isCallable(object: capability->d()->resolve) || !isCallable(object: capability->d()->reject)) |
493 | THROW_TYPE_ERROR(); |
494 | |
495 | // Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). |
496 | ScopedValue undefined(scope, Value::undefinedValue()); |
497 | ScopedFunctionObject resolve(scope, capability->d()->resolve); |
498 | resolve->call(thisObject: undefined, argv: x, argc: 1); |
499 | |
500 | return newPromise.asReturnedValue(); |
501 | } |
502 | |
503 | ReturnedValue PromiseCtor::method_reject(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
504 | { |
505 | Scope scope(f); |
506 | ExecutionEngine *e = scope.engine; |
507 | |
508 | // 2. If Type(C) is not Object, throw a TypeError exception. |
509 | if (!thisObject || !thisObject->isObject()) |
510 | THROW_TYPE_ERROR(); |
511 | |
512 | ScopedValue r(scope); |
513 | if (argc < 1) { |
514 | r = Encode::undefined(); |
515 | } else { |
516 | r = argv[0]; |
517 | } |
518 | |
519 | // 3. Let promiseCapability be ? NewPromiseCapability(C). |
520 | Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>()); |
521 | |
522 | ScopedObject newPromise(scope, e->newPromiseObject(thisObject: thisObject->as<const FunctionObject>(), capability)); |
523 | if (!newPromise || !isCallable(object: capability->d()->resolve) || !isCallable(object: capability->d()->reject)) |
524 | THROW_TYPE_ERROR(); |
525 | |
526 | ScopedValue undefined(scope, Value::undefinedValue()); |
527 | ScopedFunctionObject reject(scope, capability->d()->reject.as<const FunctionObject>()); |
528 | // Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). |
529 | reject->call(thisObject: undefined, argv: r, argc: 1); |
530 | |
531 | return newPromise.asReturnedValue(); |
532 | } |
533 | |
534 | ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *thisObject, const Value *argv, int) |
535 | { |
536 | Scope scope(f); |
537 | ExecutionEngine* e = scope.engine; |
538 | |
539 | // 2. If Type(C) is not Object, throw a TypeError exception. |
540 | if (!thisObject || !thisObject->isObject()) |
541 | THROW_TYPE_ERROR(); |
542 | |
543 | ScopedString resolveName(scope, e->newIdentifier(QStringLiteral("resolve" ))); |
544 | ScopedString thenName(scope, e->newIdentifier(QStringLiteral("then" ))); |
545 | |
546 | Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>()); |
547 | |
548 | ScopedObject newPromise(scope, e->newPromiseObject(thisObject: thisObject->as<FunctionObject>(), capability)); |
549 | if (!newPromise || !isCallable(object: capability->d()->resolve) || !isCallable(object: capability->d()->reject)) { |
550 | if (scope.hasException()) { |
551 | return e->exceptionValue->asReturnedValue(); |
552 | } else { |
553 | THROW_TYPE_ERROR(); |
554 | } |
555 | } |
556 | capability->d()->promise.set(e, newVal: newPromise); |
557 | |
558 | ScopedFunctionObject reject(scope, capability->d()->reject); |
559 | |
560 | ScopedObject itemsObject(scope, argv); |
561 | ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true)); |
562 | if (!iteratorObject || scope.hasException()) { |
563 | ScopedObject error(scope); |
564 | if (scope.hasException()) { |
565 | error = e->exceptionValue; |
566 | dropException(e); |
567 | } else { |
568 | error = e->newTypeErrorObject(QStringLiteral("Type error" )); |
569 | } |
570 | reject->call(thisObject: newPromise, argv: error, argc: 1); |
571 | return newPromise.asReturnedValue(); |
572 | } |
573 | |
574 | Scoped<QV4::PromiseExecutionState> executionState(scope, e->memoryManager->allocate<QV4::PromiseExecutionState>()); |
575 | executionState->d()->remainingElementCount = 1; |
576 | executionState->d()->capability.set(e, newVal: capability); |
577 | |
578 | Scoped<QV4::ArrayObject> results(scope, e->newArrayObject(count: 0)); |
579 | executionState->d()->values.set(e, newVal: results); |
580 | |
581 | ScopedValue doneValue(scope); |
582 | uint index = 0; |
583 | for (;;) { |
584 | Scope scope(e); |
585 | ScopedValue nextValue(scope); |
586 | doneValue = Value::fromReturnedValue(val: Runtime::IteratorNext::call(e, iteratorObject, nextValue)); |
587 | |
588 | if (doneValue->toBoolean()) |
589 | break; |
590 | |
591 | ScopedObject nextObject(scope); |
592 | if (nextValue->isObject()) { |
593 | nextObject = *nextValue; |
594 | } else if (nextValue->isBoolean()) { |
595 | nextObject = e->newBooleanObject(b: nextValue->toBoolean()); |
596 | } else if (nextValue->isInteger() || nextValue->isDouble()) { |
597 | nextObject = e->newNumberObject(value: nextValue->toInteger()); |
598 | } else if (nextValue->isString()) { |
599 | ScopedString scopedString(scope, nextValue->toString(e: scope.engine)); |
600 | nextObject = e->newStringObject(string: scopedString); |
601 | } |
602 | |
603 | ScopedFunctionObject resolve(scope, thisObject->as<Object>()->get(name: resolveName)); |
604 | if (!resolve || scope.hasException()) { |
605 | ScopedValue completion(scope); |
606 | if (!scope.hasException()) { |
607 | completion = e->newTypeErrorObject(QStringLiteral("Type error" )); |
608 | } else { |
609 | completion = e->exceptionValue->asReturnedValue(); |
610 | dropException(e); |
611 | } |
612 | |
613 | if (!doneValue->toBoolean()) |
614 | completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); |
615 | |
616 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
617 | return newPromise.asReturnedValue(); |
618 | } |
619 | |
620 | ScopedObject nextPromise(scope, Value::fromReturnedValue(val: resolve->call(thisObject, argv: nextValue, argc: 1))); |
621 | if (scope.hasException() || !nextPromise) { |
622 | ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); |
623 | if (scope.hasException()) { |
624 | completion = e->exceptionValue->asReturnedValue(); |
625 | dropException(e); |
626 | } |
627 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
628 | return newPromise.asReturnedValue(); |
629 | } |
630 | |
631 | executionState->d()->remainingElementCount++; |
632 | |
633 | ScopedFunctionObject then(scope, nextPromise->get(name: thenName)); |
634 | if (!then || scope.hasException()) { |
635 | ScopedValue completion(scope); |
636 | if (!scope.hasException()) { |
637 | completion = e->newTypeErrorObject(QStringLiteral("Type error" )); |
638 | } else { |
639 | completion = e->exceptionValue->asReturnedValue(); |
640 | dropException(e); |
641 | } |
642 | |
643 | if (!doneValue->toBoolean()) |
644 | completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); |
645 | |
646 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
647 | return newPromise.asReturnedValue(); |
648 | } |
649 | |
650 | ScopedFunctionObject resolveElement(scope, FunctionBuilder::makeResolveElementFunction(e, index, executionState: executionState->d())); |
651 | |
652 | JSCallData jsCallData(scope, 2); |
653 | jsCallData->args[0] = resolveElement; |
654 | jsCallData->args[1] = reject; |
655 | jsCallData->thisObject = nextPromise; |
656 | |
657 | then->call(data: jsCallData); |
658 | if (scope.hasException()) { |
659 | ScopedValue completion(scope, e->exceptionValue->asReturnedValue()); |
660 | dropException(e); |
661 | |
662 | if (!doneValue->toBoolean()) |
663 | completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); |
664 | |
665 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
666 | return newPromise.asReturnedValue(); |
667 | } |
668 | |
669 | index++; |
670 | } |
671 | |
672 | // empty list |
673 | executionState->d()->remainingElementCount--; |
674 | if (executionState->d()->remainingElementCount == 0) { |
675 | const FunctionObject *resolve = capability->d()->resolve.as<FunctionObject>(); |
676 | if (!resolve) |
677 | THROW_TYPE_ERROR(); |
678 | |
679 | ScopedValue values(scope, executionState->d()->values); |
680 | resolve->call(thisObject: newPromise, argv: values, argc: 1); |
681 | if (scope.hasException()) { |
682 | dropException(e); |
683 | reject->call(thisObject: newPromise, argv: scope.engine->exceptionValue, argc: 1); |
684 | } |
685 | } |
686 | |
687 | return newPromise.asReturnedValue(); |
688 | } |
689 | |
690 | ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thisObject, const Value *argv, int) |
691 | { |
692 | Scope scope(f); |
693 | ExecutionEngine* e = scope.engine; |
694 | |
695 | if (!thisObject || !thisObject->isObject()) |
696 | THROW_TYPE_ERROR(); |
697 | |
698 | ScopedString resolveName(scope, e->newIdentifier(QStringLiteral("resolve" ))); |
699 | ScopedString thenName(scope, e->newIdentifier(QStringLiteral("then" ))); |
700 | |
701 | Scoped<PromiseCapability> capability(scope, scope.engine->memoryManager->allocate<QV4::PromiseCapability>()); |
702 | |
703 | ScopedObject newPromise(scope, e->newPromiseObject(thisObject: thisObject->as<FunctionObject>(), capability)); |
704 | if (!newPromise || !isCallable(object: capability->d()->resolve) || !isCallable(object: capability->d()->reject)) |
705 | THROW_TYPE_ERROR(); |
706 | capability->d()->promise.set(e: scope.engine, newVal: newPromise); |
707 | |
708 | ScopedFunctionObject reject(scope, capability->d()->reject); |
709 | |
710 | ScopedObject itemsObject(scope, argv); |
711 | ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true)); |
712 | if (!iteratorObject) { |
713 | ScopedObject error(scope, e->newTypeErrorObject(QStringLiteral("Type error" ))); |
714 | reject->call(thisObject: newPromise, argv: error, argc: 1); |
715 | return newPromise.asReturnedValue(); |
716 | } |
717 | |
718 | ScopedValue doneValue(scope); |
719 | for (;;) { |
720 | Scope scope(e); |
721 | ScopedValue nextValue(scope); |
722 | doneValue = Value::fromReturnedValue(val: Runtime::IteratorNext::call(e, iteratorObject, nextValue)); |
723 | |
724 | if (scope.hasException()) { |
725 | ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); |
726 | if (scope.hasException()) { |
727 | completion = e->exceptionValue->asReturnedValue(); |
728 | dropException(e); |
729 | } |
730 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
731 | return newPromise.asReturnedValue(); |
732 | } |
733 | |
734 | if (doneValue->toBoolean()) |
735 | break; |
736 | |
737 | ScopedObject nextObject(scope); |
738 | if (nextValue->isObject()) { |
739 | nextObject = *nextValue; |
740 | } else if (nextValue->isBoolean()) { |
741 | nextObject = scope.engine->newBooleanObject(b: nextValue->toBoolean()); |
742 | } else if (nextValue->isInteger() || nextValue->isDouble()) { |
743 | nextObject = scope.engine->newNumberObject(value: nextValue->toInteger()); |
744 | } else if (nextValue->isString()) { |
745 | ScopedString scopedString(scope, nextValue->toString(e: scope.engine)); |
746 | nextObject = scope.engine->newStringObject(string: scopedString); |
747 | } |
748 | |
749 | ScopedFunctionObject resolve(scope, thisObject->as<FunctionObject>()->get(name: resolveName)); |
750 | if (!resolve || scope.hasException()) { |
751 | ScopedValue completion(scope); |
752 | if (!scope.hasException()) { |
753 | completion = e->newTypeErrorObject(QStringLiteral("Type error" )); |
754 | } else { |
755 | completion = e->exceptionValue->asReturnedValue(); |
756 | dropException(e); |
757 | } |
758 | |
759 | if (!doneValue->toBoolean()) |
760 | completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); |
761 | |
762 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
763 | return newPromise.asReturnedValue(); |
764 | } |
765 | |
766 | ScopedObject nextPromise(scope, Value::fromReturnedValue(val: resolve->call(thisObject, argv: nextValue, argc: 1))); |
767 | if (scope.hasException() || !nextPromise) { |
768 | ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); |
769 | if (scope.hasException()) { |
770 | completion = e->exceptionValue->asReturnedValue(); |
771 | dropException(e); |
772 | } |
773 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
774 | return newPromise.asReturnedValue(); |
775 | } |
776 | |
777 | ScopedFunctionObject then(scope, nextPromise->get(name: thenName)); |
778 | if (!then || scope.hasException()) { |
779 | ScopedValue completion(scope); |
780 | if (!scope.hasException()) { |
781 | completion = e->newTypeErrorObject(QStringLiteral("Type error" )); |
782 | } else { |
783 | completion = e->exceptionValue->asReturnedValue(); |
784 | dropException(e); |
785 | } |
786 | |
787 | if (!doneValue->toBoolean()) |
788 | completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); |
789 | |
790 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
791 | return newPromise.asReturnedValue(); |
792 | } |
793 | |
794 | ScopedFunctionObject resolveOriginalPromise(scope, capability->d()->resolve); |
795 | |
796 | JSCallData jsCallData(scope, 2); |
797 | jsCallData->args[0] = resolveOriginalPromise; |
798 | jsCallData->args[1] = reject; |
799 | jsCallData->thisObject = nextPromise; |
800 | |
801 | then->call(data: jsCallData); |
802 | if (scope.hasException()) { |
803 | ScopedValue completion(scope, e->exceptionValue->asReturnedValue()); |
804 | dropException(e); |
805 | |
806 | if (!doneValue->toBoolean()) |
807 | completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); |
808 | |
809 | reject->call(thisObject: newPromise, argv: completion, argc: 1); |
810 | return newPromise.asReturnedValue(); |
811 | } |
812 | } |
813 | |
814 | return newPromise.asReturnedValue(); |
815 | } |
816 | |
817 | void PromisePrototype::init(ExecutionEngine *engine, Object *ctor) |
818 | { |
819 | Scope scope(engine); |
820 | ScopedObject o(scope); |
821 | |
822 | ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Primitive::fromInt32(i: 1)); |
823 | ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this)); |
824 | |
825 | ctor->defineDefaultProperty(QStringLiteral("resolve" ), code: PromiseCtor::method_resolve, argumentCount: 1); |
826 | ctor->defineDefaultProperty(QStringLiteral("reject" ), code: PromiseCtor::method_reject, argumentCount: 1); |
827 | ctor->defineDefaultProperty(QStringLiteral("all" ), code: PromiseCtor::method_all, argumentCount: 1); |
828 | ctor->defineDefaultProperty(QStringLiteral("race" ), code: PromiseCtor::method_race, argumentCount: 1); |
829 | ctor->addSymbolSpecies(); |
830 | |
831 | defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor)); |
832 | |
833 | ScopedString val(scope, engine->newString(s: QLatin1String("Promise" ))); |
834 | defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val); |
835 | |
836 | defineDefaultProperty(QStringLiteral("then" ), code: method_then, argumentCount: 2); |
837 | defineDefaultProperty(QStringLiteral("catch" ), code: method_catch, argumentCount: 1); |
838 | } |
839 | |
840 | ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
841 | { |
842 | // 25.4.5.3 Promise.prototype.then |
843 | Scope scope(f); |
844 | ExecutionEngine* e = scope.engine; |
845 | |
846 | Scoped<QV4::PromiseObject> promise(scope, thisObject); |
847 | if (!promise) |
848 | THROW_TYPE_ERROR(); |
849 | |
850 | ScopedFunctionObject onFulfilled(scope); |
851 | if (argc >= 1) { |
852 | onFulfilled = argv[0]; |
853 | } else { |
854 | onFulfilled = Encode::undefined(); |
855 | } |
856 | |
857 | ScopedFunctionObject onRejected(scope); |
858 | if (argc >= 2) { |
859 | onRejected = argv[1]; |
860 | } else { |
861 | onRejected = Encode::undefined(); |
862 | } |
863 | |
864 | Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<PromiseCapability>()); |
865 | |
866 | ScopedFunctionObject constructor(scope, promise->get(name: e->id_constructor())); |
867 | if (!constructor || scope.hasException()) |
868 | THROW_TYPE_ERROR(); |
869 | |
870 | // 4. Let resultCapability be ? NewPromiseCapability(C). |
871 | ScopedObject nextPromise(scope, e->newPromiseObject(thisObject: constructor, capability)); |
872 | capability->d()->promise.set(e: scope.engine, newVal: nextPromise); |
873 | |
874 | Scoped<PromiseReaction> fulfillReaction(scope, Heap::PromiseReaction::createFulfillReaction(e: scope.engine, capability, onFulfilled)); |
875 | Scoped<PromiseReaction> rejectReaction(scope, Heap::PromiseReaction::createRejectReaction(e: scope.engine, capability, onRejected)); |
876 | |
877 | ScopedValue resolution(scope, promise->d()->resolution); |
878 | if (promise->d()->isPending()) { // 7. If promise.[[PromiseState]] is "pending" |
879 | { |
880 | // Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]]. |
881 | ScopedArrayObject a(scope, promise->d()->fulfillReactions); |
882 | ScopedValue newValue(scope, fulfillReaction->d()); |
883 | a->push_back(v: newValue); |
884 | } |
885 | |
886 | { |
887 | // Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]]. |
888 | ScopedArrayObject a(scope, promise->d()->rejectReactions); |
889 | ScopedValue newValue(scope, rejectReaction->d()); |
890 | a->push_back(v: newValue); |
891 | } |
892 | } else if (promise->d()->isFulfilled()) { // 8. Else if promise.[[PromiseState]] is "fulfilled", then |
893 | // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »). |
894 | fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, value: resolution); // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »). |
895 | } else if (promise->d()->isRejected()) { // 9. Else |
896 | // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « rejectReaction, reason »). |
897 | rejectReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, value: resolution); |
898 | } else { |
899 | Q_ASSERT(false); |
900 | THROW_GENERIC_ERROR("Should never be thrown. Unknown promise state" ); |
901 | } |
902 | |
903 | return nextPromise->asReturnedValue(); |
904 | } |
905 | |
906 | ReturnedValue PromisePrototype::method_catch(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
907 | { |
908 | Scope scope(f); |
909 | Scoped<Object> promise(scope); |
910 | if (thisObject->isObject()) { |
911 | promise.setPointer(thisObject->as<Object>()); |
912 | } else if (thisObject->isBoolean()) { |
913 | promise = scope.engine->newBooleanObject(b: thisObject->toBoolean()); |
914 | } else if (thisObject->isInteger() || thisObject->isDouble()) { |
915 | promise = scope.engine->newNumberObject(value: thisObject->toInteger()); |
916 | } else if (thisObject->isString()) { |
917 | ScopedString scopedString(scope, thisObject->toString(e: scope.engine)); |
918 | promise = scope.engine->newStringObject(string: scopedString); |
919 | } else { |
920 | THROW_TYPE_ERROR(); |
921 | } |
922 | |
923 | ScopedValue onRejected(scope); |
924 | if (argc < 1) { |
925 | onRejected = Encode::undefined(); |
926 | } else { |
927 | onRejected = argv[0]; |
928 | } |
929 | |
930 | JSCallData jsCallData(scope, 2); |
931 | jsCallData->args[0] = Encode::undefined(); |
932 | jsCallData->args[1] = onRejected; |
933 | jsCallData->thisObject = promise; |
934 | |
935 | ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then" ))); |
936 | ScopedFunctionObject then(scope, promise->get(name: thenName)); |
937 | if (!then || scope.hasException()) |
938 | THROW_TYPE_ERROR(); |
939 | |
940 | return then->call(data: jsCallData); |
941 | } |
942 | |
943 | ReturnedValue CapabilitiesExecutorWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
944 | { |
945 | Q_UNUSED(thisObject); |
946 | |
947 | Scope scope(f); |
948 | const CapabilitiesExecutorWrapper* self = static_cast<const CapabilitiesExecutorWrapper*>(f); |
949 | Heap::PromiseCapability *capabilities = self->d()->capabilities; |
950 | |
951 | if (!capabilities->resolve.isUndefined() || !capabilities->reject.isUndefined()) |
952 | THROW_TYPE_ERROR(); |
953 | |
954 | if (argc >= 1 && !argv[0].isUndefined()) |
955 | capabilities->resolve.set(e: scope.engine, newVal: argv[0]); |
956 | |
957 | if (argc >= 2 && !argv[1].isUndefined()) |
958 | capabilities->reject.set(e: scope.engine, newVal: argv[1]); |
959 | |
960 | return Encode::undefined(); |
961 | } |
962 | |
963 | ReturnedValue ResolveElementWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
964 | { |
965 | Q_UNUSED(thisObject); |
966 | |
967 | Scope scope(f); |
968 | const ResolveElementWrapper* self = static_cast<const ResolveElementWrapper*>(f); |
969 | |
970 | if (self->d()->alreadyResolved) |
971 | return Encode::undefined(); |
972 | |
973 | ScopedValue value(scope); |
974 | if (argc == 1) { |
975 | value = argv[0]; |
976 | } else { |
977 | value = Encode::undefined(); |
978 | } |
979 | |
980 | Scoped<PromiseExecutionState> so(scope, self->d()->state); |
981 | self->d()->alreadyResolved = true; |
982 | |
983 | ScopedObject values(scope, so->d()->values); |
984 | values->arraySet(index: self->d()->index, value); |
985 | |
986 | so->d()->remainingElementCount--; |
987 | if (so->d()->remainingElementCount == 0) { |
988 | Scoped<PromiseCapability> capability(scope, so->d()->capability); |
989 | ScopedValue promise(scope, capability->d()->promise); |
990 | ScopedFunctionObject resolve(scope, capability->d()->resolve.as<QV4::FunctionObject>()); |
991 | resolve->call(thisObject: promise, argv: values, argc: 1); |
992 | } |
993 | |
994 | return Encode::undefined(); |
995 | } |
996 | |
997 | ReturnedValue ResolveWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
998 | { |
999 | // 25.4.1.3.2 (ecmase-262/8.0) |
1000 | |
1001 | Q_UNUSED(thisObject); |
1002 | |
1003 | Scope scope(f); |
1004 | const ResolveWrapper *self = static_cast<const ResolveWrapper*>(f); |
1005 | |
1006 | Scoped<PromiseObject> promise(scope, self->d()->promise); |
1007 | // 4. If alreadyRseolved.[[Value]] is true, return undefined |
1008 | if (self->d()->alreadyResolved || !promise->d()->isPending()) // Why check for pending? |
1009 | return Encode::undefined(); |
1010 | |
1011 | // 5. Set alreadyResolved.[[Value]] to true |
1012 | self->d()->alreadyResolved = true; |
1013 | |
1014 | ScopedValue resolution(scope); |
1015 | if (argc == 1) { |
1016 | resolution = argv[0]; |
1017 | } else { |
1018 | resolution = Encode::undefined(); |
1019 | } |
1020 | |
1021 | if (!resolution->isObject()) { // 7 If Type(resolution) is not Object |
1022 | // then Return FullFillPromise(promise, resolution) |
1023 | // (FullFillPromise will return undefined, so we share the return with the other path which also returns undefined |
1024 | promise->d()->setState(Heap::PromiseObject::Fulfilled); |
1025 | promise->d()->resolution.set(e: scope.engine, newVal: resolution); |
1026 | promise->d()->triggerFullfillReactions(e: scope.engine); |
1027 | } else { |
1028 | //PromiseObject *promise = resolution->as<PromiseObject>(); |
1029 | auto resolutionObject = resolution->as<Object>(); |
1030 | ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then" ))); |
1031 | |
1032 | // 8. Let then be Get(resolution, then) |
1033 | ScopedFunctionObject thenAction { scope, resolutionObject->get(name: thenName)}; |
1034 | // 9. If then is an abrupt completion, then |
1035 | if (scope.engine->hasException) { |
1036 | // Return RecjectPromise(promise, then.[[Value]] |
1037 | ScopedValue thenValue {scope, scope.engine->catchException()}; |
1038 | promise->d()->setState(Heap::PromiseObject::Rejected); |
1039 | promise->d()->resolution.set(e: scope.engine, newVal: thenValue); |
1040 | promise->d()->triggerRejectReactions(e: scope.engine); |
1041 | } else { |
1042 | // 10. Let thenAction be then.[[Value]] |
1043 | if (!thenAction) { // 11. If IsCallable(thenAction) is false |
1044 | promise->d()->setState(Heap::PromiseObject::Fulfilled); |
1045 | promise->d()->resolution.set(e: scope.engine, newVal: resolution); |
1046 | promise->d()->triggerFullfillReactions(e: scope.engine); |
1047 | } else { |
1048 | // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, « promise, resolution, thenAction »). |
1049 | scope.engine->getPromiseReactionHandler()->addResolveThenable(e: scope.engine, promise: promise.getPointer(), thenable: resolutionObject, then: thenAction); |
1050 | } |
1051 | } |
1052 | } |
1053 | |
1054 | return Encode::undefined(); |
1055 | } |
1056 | |
1057 | ReturnedValue RejectWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) |
1058 | { |
1059 | Q_UNUSED(thisObject); |
1060 | |
1061 | Scope scope(f); |
1062 | const RejectWrapper *self = static_cast<const RejectWrapper*>(f); |
1063 | |
1064 | Scoped<PromiseObject> promise(scope, self->d()->promise); |
1065 | if (self->d()->alreadyResolved || !promise->d()->isPending()) |
1066 | return Encode::undefined(); |
1067 | |
1068 | ScopedValue value(scope); |
1069 | if (argc == 1) { |
1070 | value = argv[0]; |
1071 | } else { |
1072 | value = Encode::undefined(); |
1073 | } |
1074 | |
1075 | if (!isPromise(object: value)) { |
1076 | self->d()->alreadyResolved = true; |
1077 | promise->d()->setState(Heap::PromiseObject::Rejected); |
1078 | promise->d()->resolution.set(e: scope.engine, newVal: value); |
1079 | |
1080 | promise->d()->triggerRejectReactions(e: scope.engine); |
1081 | |
1082 | } else { |
1083 | PromiseObject *promise = value->as<PromiseObject>(); |
1084 | ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("catch" ))); |
1085 | |
1086 | ScopedFunctionObject then(scope, promise->get(name: thenName)); |
1087 | JSCallData jsCallData(scope, 2); |
1088 | jsCallData->args[0] = *f; |
1089 | jsCallData->args[1] = Encode::undefined(); |
1090 | jsCallData->thisObject = value; |
1091 | |
1092 | then->call(data: jsCallData); |
1093 | } |
1094 | |
1095 | return Encode::undefined(); |
1096 | } |
1097 | |