Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qv4promiseobject.cpp
Go to the documentation of this file.
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
10
11using namespace QV4;
12using namespace QV4::Promise;
13
19
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(scope.engine->id_length(), Primitive::fromInt32(1), Attr_NotWritable|Attr_NotEnumerable);
42}
43
44void dropException(QV4::ExecutionEngine* e)
45{
46 e->hasException = false;
47}
48}
49
50namespace QV4 {
51namespace Promise {
52
55
56struct ReactionEvent : public QEvent
57{
58 ReactionEvent(ExecutionEngine *e, const Value *reaction_, const Value *resolution_)
60 reaction{e, *reaction_},
61 resolution{e, *resolution_}
62 {}
63
66};
67
78
79
80} // namespace Promise
81} // namespace QV4
82
86
89
91{
92 QCoreApplication::postEvent(this, 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(this, new ResolveThenableEvent(e, promise, thenable, then));
98}
99
101{
102 if (event)
103 {
104 const int type = event->type();
106 executeReaction(static_cast<ReactionEvent*>(event));
107
110 }
111}
112
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(promise, resolution, 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(promise, result, 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(promise, resolution, 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, resolveWrapper->d());
158 resolveWrapper->d()->promise.set(e, 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, rejectWrapper->d());
168 rejectWrapper->d()->promise.set(e, 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, executionState);
181
182 insertIdLengthTag(scope, resolveElementWrapper->d());
183
184 return resolveElementWrapper->d();
185 }
186};
187
188}
189
190
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(scope.engine, promise->d())};
197 ScopedFunctionObject reject {scope, FunctionBuilder::makeRejectFunction(scope.engine, 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(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(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(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(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, 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, 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, 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, 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(this));
314 handler->addReaction(e, reaction, value);
315}
316
318{
319 Heap::FunctionObject::init(engine, QStringLiteral("Promise"));
320}
321
322void Heap::PromiseObject::init(ExecutionEngine *e)
323{
324 Heap::Object::init();
325
326 {
328 fulfillReactions.set(e, a);
329 }
330
331 {
333 rejectReactions.set(e, 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
374{
375 // 25.4.3.1 Promise ( executor )
376 // 1. If NewTarget is undefined, throw a TypeError exception.
377 Scope scope(f);
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
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(scope.engine, a->d()));
405 ScopedFunctionObject reject(scope, FunctionBuilder::makeRejectFunction(scope.engine, 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(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(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
435
436 ScopedValue x(scope);
437 if (argc < 1) {
439 } else {
440 x = argv[0];
441 }
442
443 // 3. If IsPromise(x) is true, then
444 if (isPromise(x) && x->isObject()) {
445 ScopedObject so(scope, thisObject);
446 // Let xConstructor be ? Get(x, "constructor").
447 ScopedObject constructor(scope, x->objectValue()->get(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->as<const FunctionObject>(), capability));
456 if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
458
459 // Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
460 ScopedValue undefined(scope, Value::undefinedValue());
461 ScopedFunctionObject resolve(scope, capability->d()->resolve);
462 resolve->call(undefined, x, 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())
475
476 ScopedValue r(scope);
477 if (argc < 1) {
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->as<const FunctionObject>(), capability));
487 if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
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(undefined, r, 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())
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->as<FunctionObject>(), capability));
513 if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject)) {
514 if (scope.hasException()) {
515 return e->exceptionValue->asReturnedValue();
516 } else {
518 }
519 }
520 capability->d()->promise.set(e, 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()) {
530 dropException(e);
531 } else {
532 error = e->newTypeErrorObject(QStringLiteral("Type error"));
533 }
534 reject->call(newPromise, error, 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, capability);
541
542 Scoped<QV4::ArrayObject> results(scope, e->newArrayObject(0));
543 executionState->d()->values.set(e, results);
544
545 ScopedValue doneValue(scope);
546 uint index = 0;
547 for (;;) {
548 Scope scope(e);
549 ScopedValue nextValue(scope);
550 doneValue = Value::fromReturnedValue(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(nextValue->toBoolean());
560 } else if (nextValue->isInteger() || nextValue->isDouble()) {
561 nextObject = e->newNumberObject(nextValue->toInteger());
562 } else if (nextValue->isString()) {
563 ScopedString scopedString(scope, nextValue->toString(scope.engine));
564 nextObject = e->newStringObject(scopedString);
565 }
566
567 ScopedFunctionObject resolve(scope, thisObject->as<Object>()->get(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);
579
580 reject->call(newPromise, completion, 1);
581 return newPromise.asReturnedValue();
582 }
583
584 ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
585 if (scope.hasException() || !nextPromise) {
586 ScopedValue completion(scope, doneValue->toBoolean()
588 : Runtime::IteratorClose::call(e, iteratorObject));
589 if (scope.hasException()) {
590 completion = e->exceptionValue->asReturnedValue();
591 dropException(e);
592 }
593 reject->call(newPromise, completion, 1);
594 return newPromise.asReturnedValue();
595 }
596
597 executionState->d()->remainingElementCount++;
598
599 ScopedFunctionObject then(scope, nextPromise->get(thenName));
600 if (!then || scope.hasException()) {
601 ScopedValue completion(scope);
602 if (!scope.hasException()) {
603 completion = e->newTypeErrorObject(QStringLiteral("Type error"));
604 } else {
605 completion = e->exceptionValue->asReturnedValue();
606 dropException(e);
607 }
608
609 if (!doneValue->toBoolean())
610 completion = Runtime::IteratorClose::call(scope.engine, iteratorObject);
611
612 reject->call(newPromise, completion, 1);
613 return newPromise.asReturnedValue();
614 }
615
616 ScopedFunctionObject resolveElement(scope, FunctionBuilder::makeResolveElementFunction(e, index, executionState->d()));
617
618 JSCallArguments jsCallData(scope, 2);
619 jsCallData.args[0] = resolveElement;
620 jsCallData.args[1] = reject;
621 jsCallData.thisObject = nextPromise;
622
623 then->call(jsCallData);
624 if (scope.hasException()) {
625 ScopedValue completion(scope, e->exceptionValue->asReturnedValue());
626 dropException(e);
627
628 if (!doneValue->toBoolean())
629 completion = Runtime::IteratorClose::call(scope.engine, iteratorObject);
630
631 reject->call(newPromise, completion, 1);
632 return newPromise.asReturnedValue();
633 }
634
635 index++;
636 }
637
638 // empty list
639 executionState->d()->remainingElementCount--;
640 if (executionState->d()->remainingElementCount == 0) {
641 const FunctionObject *resolve = capability->d()->resolve.as<FunctionObject>();
642 if (!resolve)
644
645 ScopedValue values(scope, executionState->d()->values);
646 resolve->call(newPromise, values, 1);
647 if (scope.hasException()) {
648 dropException(e);
649 reject->call(newPromise, scope.engine->exceptionValue, 1);
650 }
651 }
652
653 return newPromise.asReturnedValue();
654}
655
656ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thisObject, const Value *argv, int)
657{
658 Scope scope(f);
659 ExecutionEngine* e = scope.engine;
660
661 if (!thisObject || !thisObject->isObject())
663
664 ScopedString resolveName(scope, e->newIdentifier(QStringLiteral("resolve")));
665 ScopedString thenName(scope, e->newIdentifier(QStringLiteral("then")));
666
667 Scoped<PromiseCapability> capability(scope, scope.engine->memoryManager->allocate<QV4::PromiseCapability>());
668
669 ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<FunctionObject>(), capability));
670 if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
672 capability->d()->promise.set(scope.engine, newPromise);
673
674 ScopedFunctionObject reject(scope, capability->d()->reject);
675
676 ScopedObject itemsObject(scope, argv);
677 ScopedObject iteratorObject(scope, Runtime::GetIterator::call(e, itemsObject, true));
678 if (!iteratorObject) {
679 ScopedObject error(scope, e->newTypeErrorObject(QStringLiteral("Type error")));
680 reject->call(newPromise, error, 1);
681 return newPromise.asReturnedValue();
682 }
683
684 ScopedValue doneValue(scope);
685 for (;;) {
686 Scope scope(e);
687 ScopedValue nextValue(scope);
688 doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue));
689
690 if (scope.hasException()) {
691 ScopedValue completion(scope, doneValue->toBoolean()
693 : Runtime::IteratorClose::call(e, iteratorObject));
694 if (scope.hasException()) {
695 completion = e->exceptionValue->asReturnedValue();
696 dropException(e);
697 }
698 reject->call(newPromise, completion, 1);
699 return newPromise.asReturnedValue();
700 }
701
702 if (doneValue->toBoolean())
703 break;
704
705 ScopedObject nextObject(scope);
706 if (nextValue->isObject()) {
707 nextObject = *nextValue;
708 } else if (nextValue->isBoolean()) {
709 nextObject = scope.engine->newBooleanObject(nextValue->toBoolean());
710 } else if (nextValue->isInteger() || nextValue->isDouble()) {
711 nextObject = scope.engine->newNumberObject(nextValue->toInteger());
712 } else if (nextValue->isString()) {
713 ScopedString scopedString(scope, nextValue->toString(scope.engine));
714 nextObject = scope.engine->newStringObject(scopedString);
715 }
716
717 ScopedFunctionObject resolve(scope, thisObject->as<FunctionObject>()->get(resolveName));
718 if (!resolve || scope.hasException()) {
719 ScopedValue completion(scope);
720 if (!scope.hasException()) {
721 completion = e->newTypeErrorObject(QStringLiteral("Type error"));
722 } else {
723 completion = e->exceptionValue->asReturnedValue();
724 dropException(e);
725 }
726
727 if (!doneValue->toBoolean())
728 completion = Runtime::IteratorClose::call(e, iteratorObject);
729
730 reject->call(newPromise, completion, 1);
731 return newPromise.asReturnedValue();
732 }
733
734 ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
735 if (scope.hasException() || !nextPromise) {
736 ScopedValue completion(scope, doneValue->toBoolean()
738 : Runtime::IteratorClose::call(e, iteratorObject));
739 if (scope.hasException()) {
740 completion = e->exceptionValue->asReturnedValue();
741 dropException(e);
742 }
743 reject->call(newPromise, completion, 1);
744 return newPromise.asReturnedValue();
745 }
746
747 ScopedFunctionObject then(scope, nextPromise->get(thenName));
748 if (!then || scope.hasException()) {
749 ScopedValue completion(scope);
750 if (!scope.hasException()) {
751 completion = e->newTypeErrorObject(QStringLiteral("Type error"));
752 } else {
753 completion = e->exceptionValue->asReturnedValue();
754 dropException(e);
755 }
756
757 if (!doneValue->toBoolean())
758 completion = Runtime::IteratorClose::call(e, iteratorObject);
759
760 reject->call(newPromise, completion, 1);
761 return newPromise.asReturnedValue();
762 }
763
764 ScopedFunctionObject resolveOriginalPromise(scope, capability->d()->resolve);
765
766 JSCallArguments jsCallData(scope, 2);
767 jsCallData.args[0] = resolveOriginalPromise;
768 jsCallData.args[1] = reject;
769 jsCallData.thisObject = nextPromise;
770
771 then->call(jsCallData);
772 if (scope.hasException()) {
773 ScopedValue completion(scope, e->exceptionValue->asReturnedValue());
774 dropException(e);
775
776 if (!doneValue->toBoolean())
777 completion = Runtime::IteratorClose::call(e, iteratorObject);
778
779 reject->call(newPromise, completion, 1);
780 return newPromise.asReturnedValue();
781 }
782 }
783
784 return newPromise.asReturnedValue();
785}
786
788{
789 Scope scope(engine);
790 ScopedObject o(scope);
791
792 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
793 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
794
795 ctor->defineDefaultProperty(QStringLiteral("resolve"), PromiseCtor::method_resolve, 1);
796 ctor->defineDefaultProperty(QStringLiteral("reject"), PromiseCtor::method_reject, 1);
797 ctor->defineDefaultProperty(QStringLiteral("all"), PromiseCtor::method_all, 1);
798 ctor->defineDefaultProperty(QStringLiteral("race"), PromiseCtor::method_race, 1);
799 ctor->addSymbolSpecies();
800
801 defineDefaultProperty(engine->id_constructor(), (o = ctor));
802
803 ScopedString val(scope, engine->newString(QLatin1String("Promise")));
804 defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
805
806 defineDefaultProperty(QStringLiteral("then"), method_then, 2);
807 defineDefaultProperty(QStringLiteral("catch"), method_catch, 1);
808}
809
810ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
811{
812 // 25.4.5.3 Promise.prototype.then
813 Scope scope(f);
814 ExecutionEngine* e = scope.engine;
815
816 Scoped<QV4::PromiseObject> promise(scope, thisObject);
817 if (!promise)
819
820 ScopedFunctionObject onFulfilled(scope);
821 if (argc >= 1) {
822 onFulfilled = argv[0];
823 } else {
824 onFulfilled = Encode::undefined();
825 }
826
827 ScopedFunctionObject onRejected(scope);
828 if (argc >= 2) {
829 onRejected = argv[1];
830 } else {
831 onRejected = Encode::undefined();
832 }
833
834 Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<PromiseCapability>());
835
836 ScopedFunctionObject constructor(scope, promise->get(e->id_constructor()));
837 if (!constructor || scope.hasException())
839
840 // 4. Let resultCapability be ? NewPromiseCapability(C).
841 ScopedObject nextPromise(scope, e->newPromiseObject(constructor, capability));
842 capability->d()->promise.set(scope.engine, nextPromise);
843
844 Scoped<PromiseReaction> fulfillReaction(scope, Heap::PromiseReaction::createFulfillReaction(scope.engine, capability, onFulfilled));
845 Scoped<PromiseReaction> rejectReaction(scope, Heap::PromiseReaction::createRejectReaction(scope.engine, capability, onRejected));
846
847 ScopedValue resolution(scope, promise->d()->resolution);
848 if (promise->d()->isPending()) { // 7. If promise.[[PromiseState]] is "pending"
849 {
850 // Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
851 ScopedArrayObject a(scope, promise->d()->fulfillReactions);
852 ScopedValue newValue(scope, fulfillReaction->d());
853 a->push_back(newValue);
854 }
855
856 {
857 // Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
858 ScopedArrayObject a(scope, promise->d()->rejectReactions);
859 ScopedValue newValue(scope, rejectReaction->d());
860 a->push_back(newValue);
861 }
862 } else if (promise->d()->isFulfilled()) { // 8. Else if promise.[[PromiseState]] is "fulfilled", then
863 // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
864 fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution); // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
865 } else if (promise->d()->isRejected()) { // 9. Else
866 // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « rejectReaction, reason »).
867 rejectReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
868 } else {
869 Q_ASSERT(false);
870 THROW_GENERIC_ERROR("Should never be thrown. Unknown promise state");
871 }
872
873 return nextPromise->asReturnedValue();
874}
875
876ReturnedValue PromisePrototype::method_catch(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
877{
878 Scope scope(f);
879 Scoped<Object> promise(scope);
880 if (thisObject->isObject()) {
881 promise.setPointer(thisObject->as<Object>());
882 } else if (thisObject->isBoolean()) {
883 promise = scope.engine->newBooleanObject(thisObject->toBoolean());
884 } else if (thisObject->isInteger() || thisObject->isDouble()) {
885 promise = scope.engine->newNumberObject(thisObject->toInteger());
886 } else if (thisObject->isString()) {
887 ScopedString scopedString(scope, thisObject->toString(scope.engine));
888 promise = scope.engine->newStringObject(scopedString);
889 } else {
891 }
892
893 ScopedValue onRejected(scope);
894 if (argc < 1) {
895 onRejected = Encode::undefined();
896 } else {
897 onRejected = argv[0];
898 }
899
900 JSCallArguments jsCallData(scope, 2);
901 jsCallData.args[0] = Encode::undefined();
902 jsCallData.args[1] = onRejected;
903 jsCallData.thisObject = promise;
904
905 ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then")));
906 ScopedFunctionObject then(scope, promise->get(thenName));
907 if (!then || scope.hasException())
909
910 return then->call(jsCallData);
911}
912
913ReturnedValue CapabilitiesExecutorWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
914{
915 Q_UNUSED(thisObject);
916
917 Scope scope(f);
918 const CapabilitiesExecutorWrapper* self = static_cast<const CapabilitiesExecutorWrapper*>(f);
919 Heap::PromiseCapability *capabilities = self->d()->capabilities;
920
921 if (!capabilities->resolve.isUndefined() || !capabilities->reject.isUndefined())
923
924 if (argc >= 1 && !argv[0].isUndefined())
925 capabilities->resolve.set(scope.engine, argv[0]);
926
927 if (argc >= 2 && !argv[1].isUndefined())
928 capabilities->reject.set(scope.engine, argv[1]);
929
930 return Encode::undefined();
931}
932
933ReturnedValue ResolveElementWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
934{
935 Q_UNUSED(thisObject);
936
937 Scope scope(f);
938 const ResolveElementWrapper* self = static_cast<const ResolveElementWrapper*>(f);
939
940 if (self->d()->alreadyResolved)
941 return Encode::undefined();
942
943 ScopedValue value(scope);
944 if (argc == 1) {
945 value = argv[0];
946 } else {
948 }
949
950 Scoped<PromiseExecutionState> so(scope, self->d()->state);
951 self->d()->alreadyResolved = true;
952
953 ScopedObject values(scope, so->d()->values);
954 values->arraySet(self->d()->index, value);
955
956 so->d()->remainingElementCount--;
957 if (so->d()->remainingElementCount == 0) {
958 Scoped<PromiseCapability> capability(scope, so->d()->capability);
959 ScopedValue promise(scope, capability->d()->promise);
960 ScopedFunctionObject resolve(scope, capability->d()->resolve.as<QV4::FunctionObject>());
961 resolve->call(promise, values, 1);
962 }
963
964 return Encode::undefined();
965}
966
967ReturnedValue ResolveWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
968{
969 // 25.4.1.3.2 (ecmase-262/8.0)
970
971 Q_UNUSED(thisObject);
972
973 Scope scope(f);
974 const ResolveWrapper *self = static_cast<const ResolveWrapper*>(f);
975
976 Scoped<PromiseObject> promise(scope, self->d()->promise);
977 // 4. If alreadyRseolved.[[Value]] is true, return undefined
978 if (self->d()->alreadyResolved || !promise->d()->isPending()) // Why check for pending?
979 return Encode::undefined();
980
981 // 5. Set alreadyResolved.[[Value]] to true
982 self->d()->alreadyResolved = true;
983
984 ScopedValue resolution(scope);
985 if (argc == 1) {
986 resolution = argv[0];
987 } else {
988 resolution = Encode::undefined();
989 }
990
991 if (!resolution->isObject()) { // 7 If Type(resolution) is not Object
992 // then Return FullFillPromise(promise, resolution)
993 // (FullFillPromise will return undefined, so we share the return with the other path which also returns undefined
994 promise->d()->setState(Heap::PromiseObject::Fulfilled);
995 promise->d()->resolution.set(scope.engine, resolution);
996 promise->d()->triggerFullfillReactions(scope.engine);
997 } else {
998 //PromiseObject *promise = resolution->as<PromiseObject>();
999 auto resolutionObject = resolution->as<Object>();
1000 ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then")));
1001
1002 // 8. Let then be Get(resolution, then)
1003 ScopedFunctionObject thenAction { scope, resolutionObject->get(thenName)};
1004 // 9. If then is an abrupt completion, then
1005 if (scope.hasException()) {
1006 // Return RecjectPromise(promise, then.[[Value]]
1007 ScopedValue thenValue {scope, scope.engine->catchException()};
1008 promise->d()->setState(Heap::PromiseObject::Rejected);
1009 promise->d()->resolution.set(scope.engine, thenValue);
1010 promise->d()->triggerRejectReactions(scope.engine);
1011 } else {
1012 // 10. Let thenAction be then.[[Value]]
1013 if (!thenAction) { // 11. If IsCallable(thenAction) is false
1014 promise->d()->setState(Heap::PromiseObject::Fulfilled);
1015 promise->d()->resolution.set(scope.engine, resolution);
1016 promise->d()->triggerFullfillReactions(scope.engine);
1017 } else {
1018 // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, « promise, resolution, thenAction »).
1019 scope.engine->getPromiseReactionHandler()->addResolveThenable(scope.engine, promise.getPointer(), resolutionObject, thenAction);
1020 }
1021 }
1022 }
1023
1024 return Encode::undefined();
1025}
1026
1027ReturnedValue RejectWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
1028{
1029 Q_UNUSED(thisObject);
1030
1031 Scope scope(f);
1032 const RejectWrapper *self = static_cast<const RejectWrapper*>(f);
1033
1034 Scoped<PromiseObject> promise(scope, self->d()->promise);
1035 if (self->d()->alreadyResolved || !promise->d()->isPending())
1036 return Encode::undefined();
1037
1038 ScopedValue value(scope);
1039 if (argc == 1) {
1040 value = argv[0];
1041 } else {
1043 }
1044
1045 if (!isPromise(value)) {
1046 self->d()->alreadyResolved = true;
1047 promise->d()->setState(Heap::PromiseObject::Rejected);
1048 promise->d()->resolution.set(scope.engine, value);
1049
1050 promise->d()->triggerRejectReactions(scope.engine);
1051
1052 } else {
1053 PromiseObject *promise = value->as<PromiseObject>();
1054 ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("catch")));
1055
1056 ScopedFunctionObject then(scope, promise->get(thenName));
1057 JSCallArguments jsCallData(scope, 2);
1058 jsCallData.args[0] = *f;
1059 jsCallData.args[1] = Encode::undefined();
1060 jsCallData.thisObject = value;
1061
1062 then->call(jsCallData);
1063 }
1064
1065 return Encode::undefined();
1066}
1067
1069
1070#include "moc_qv4promiseobject_p.cpp"
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
\inmodule QtCore
Definition qcoreevent.h:45
static int registerEventType(int hint=-1) noexcept
\inmodule QtCore
Definition qobject.h:103
ObjectType::Data * allocate(Args &&... args)
Definition qv4mm_p.h:298
ReactionHandler(QObject *parent=nullptr)
void addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then)
void customEvent(QEvent *event) override
This event handler can be reimplemented in a subclass to receive custom events.
void addReaction(ExecutionEngine *e, const Value *reaction, const Value *value)
void executeResolveThenable(ResolveThenableEvent *event)
void executeReaction(ReactionEvent *event)
else opt state
[0]
Combined button and popup list for selecting options.
const int PROMISE_REACTION_EVENT
const int PROMISE_RESOLVE_THENABLE_EVENT
Scoped< FunctionObject > ScopedFunctionObject
quint64 ReturnedValue
Scoped< String > ScopedString
@ Attr_NotEnumerable
@ Attr_NotWritable
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
GLenum GLsizei GLsizei GLint * values
[15]
GLint GLint GLint GLint GLint x
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLboolean r
[2]
GLfloat GLfloat f
GLenum type
struct _cl_event * event
GLuint GLfloat * val
GLuint64EXT * result
[6]
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
#define THROW_TYPE_ERROR()
#define THROW_GENERIC_ERROR(str)
#define DEFINE_OBJECT_VTABLE(classname)
QJSEngine engine
[0]
static constexpr ReturnedValue undefined()
MemoryManager * memoryManager
Heap::PromiseObject * newPromiseObject()
String * id_length() const
String * id_constructor() const
Heap::Object * newNumberObject(double value)
Heap::String * newIdentifier(const QString &text)
Heap::Object * newBooleanObject(bool b)
Promise::ReactionHandler * getPromiseReactionHandler()
Heap::ArrayObject * newArrayObject(int count=0)
Heap::Object * newTypeErrorObject(const QString &message)
Heap::Object * newStringObject(const String *string)
ReturnedValue catchException(StackTrace *trace=nullptr)
ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const
void init(ExecutionEngine *engine)
bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow)
ReturnedValue get(StringOrSymbol *name, bool *hasProperty=nullptr, const Value *receiver=nullptr) const
static ReturnedValue method_all(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_race(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_reject(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_resolve(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_catch(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
void init(ExecutionEngine *engine, Object *ctor)
QV4::PersistentValue resolution
ReactionEvent(ExecutionEngine *e, const Value *reaction_, const Value *resolution_)
QV4::PersistentValue reaction
ResolveThenableEvent(ExecutionEngine *e, const PromiseObject *promise_, const Object *thenable_, const FunctionObject *then_)
static ReturnedValue call(ExecutionEngine *, const Value &, int)
static ReturnedValue call(ExecutionEngine *, const Value &)
static ReturnedValue call(ExecutionEngine *, const Value &, Value *)
bool hasException() const
ExecutionEngine * engine
bool isInteger() const
constexpr ReturnedValue asReturnedValue() const
bool isDouble() const
bool isBoolean() const
static constexpr VTable::CallAsConstructor virtualCallAsConstructor
static constexpr VTable::Call virtualCall
static constexpr Value fromInt32(int i)
Definition qv4value_p.h:187
Heap::String * toString(ExecutionEngine *e) const
Definition qv4value_p.h:114
bool isString() const
Definition qv4value_p.h:284
bool toBoolean() const
Definition qv4value_p.h:97
static constexpr Value undefinedValue()
Definition qv4value_p.h:191
static Value fromHeapObject(HeapBasePtr m)
Definition qv4value_p.h:84
static constexpr Value fromReturnedValue(ReturnedValue val)
Definition qv4value_p.h:165
const T * as() const
Definition qv4value_p.h:132
double toInteger() const
Definition qv4value_p.h:394
bool isObject() const
Definition qv4value_p.h:302
Definition moc.h:23