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