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