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