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