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

source code of qtdeclarative/src/qml/jsruntime/qv4promiseobject.cpp