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
45using namespace QV4;
46using namespace QV4::Promise;
47
48DEFINE_OBJECT_VTABLE(PromiseReaction);
49DEFINE_OBJECT_VTABLE(PromiseCtor);
50DEFINE_OBJECT_VTABLE(PromiseObject);
51DEFINE_OBJECT_VTABLE(PromiseCapability);
52DEFINE_OBJECT_VTABLE(PromiseExecutionState);
53
54DEFINE_OBJECT_VTABLE(CapabilitiesExecutorWrapper);
55DEFINE_OBJECT_VTABLE(ResolveElementWrapper);
56DEFINE_OBJECT_VTABLE(ResolveWrapper);
57DEFINE_OBJECT_VTABLE(RejectWrapper);
58
59
60namespace {
61
62bool isPromise(const Value &object)
63{
64 return object.as<PromiseObject>() != nullptr;
65}
66
67bool isCallable(const Value &object)
68{
69 return object.as<FunctionObject>() != nullptr;
70}
71
72void 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
78void dropException(QV4::ExecutionEngine* e)
79{
80 e->hasException = false;
81}
82}
83
84QT_BEGIN_NAMESPACE
85namespace QV4 {
86namespace Promise {
87
88const int PROMISE_REACTION_EVENT = QEvent::registerEventType();
89const int PROMISE_RESOLVE_THENABLE_EVENT = QEvent::registerEventType();
90
91struct 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
103struct 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
117QT_END_NAMESPACE
118
119ReactionHandler::ReactionHandler(QObject *parent)
120 : QObject(parent)
121{}
122
123ReactionHandler::~ReactionHandler()
124{}
125
126void ReactionHandler::addReaction(ExecutionEngine *e, const Value *reaction, const Value *value)
127{
128 QCoreApplication::postEvent(receiver: this, event: new ReactionEvent(e, reaction, value));
129}
130
131void 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
136void 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
149void 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
185namespace {
186
187class FunctionBuilder {
188public:
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
227void 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
246void Heap::PromiseObject::setState(PromiseObject::State state)
247{
248 this->state = state;
249}
250
251bool Heap::PromiseObject::isSettled() const
252{
253 return (state != Pending);
254}
255
256bool Heap::PromiseObject::isPending() const
257{
258 return (state == Pending);
259}
260
261bool Heap::PromiseObject::isFulfilled() const
262{
263 return (state == Fulfilled);
264}
265
266bool Heap::PromiseObject::isRejected() const
267{
268 return (state == Rejected);
269}
270
271void 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
286void 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
301Heap::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
323Heap::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
345void 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
353void Heap::PromiseCtor::init(QV4::ExecutionContext *scope)
354{
355 Heap::FunctionObject::init(scope, QStringLiteral("Promise"));
356}
357
358void 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
373void Heap::CapabilitiesExecutorWrapper::init()
374{
375 Heap::FunctionObject::init();
376}
377
378void Heap::CapabilitiesExecutorWrapper::destroy()
379{
380 Heap::FunctionObject::destroy();
381}
382
383void Heap::PromiseExecutionState::init()
384{
385 index = 0;
386 remainingElementCount = 0;
387}
388
389void Heap::ResolveElementWrapper::init()
390{
391 index = 0;
392 alreadyResolved = false;
393
394 Heap::FunctionObject::init();
395}
396
397void Heap::ResolveWrapper::init()
398{
399 alreadyResolved = false;
400 Heap::FunctionObject::init();
401}
402
403void Heap::RejectWrapper::init()
404{
405 alreadyResolved = false;
406 Heap::FunctionObject::init();
407}
408
409ReturnedValue 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
417ReturnedValue 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
464ReturnedValue 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
503ReturnedValue 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
534ReturnedValue 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
690ReturnedValue 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
817void 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
840ReturnedValue 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
906ReturnedValue 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
943ReturnedValue 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
963ReturnedValue 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
997ReturnedValue 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
1057ReturnedValue 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

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