aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorValery Kotov <kotov.valery@gmail.com>2015-07-16 19:35:55 +0300
committerSimon Hausmann <simon.hausmann@qt.io>2018-10-11 08:27:48 +0000
commitf47c87e75fb94f1b322157fa663ac8b87a4dbd22 (patch)
treeccd08e635759641095544f3a9ce3d41a516f5b84 /src
parent54fdc4de7963437642704c2bcf339979aa4f8734 (diff)
QML Engine: Support for JavaScript Promises
Support for JavaScript Promises. Change-Id: I90ce328b35f3bdf3fd666a8829f22b5d56b6f861 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/jsruntime/jsruntime.pri6
-rw-r--r--src/qml/jsruntime/qv4engine.cpp42
-rw-r--r--src/qml/jsruntime/qv4engine_p.h14
-rw-r--r--src/qml/jsruntime/qv4global_p.h6
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp979
-rw-r--r--src/qml/jsruntime/qv4promiseobject_p.h263
6 files changed, 1308 insertions, 2 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 2a338c6792..5ec55b960b 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -56,7 +56,8 @@ SOURCES += \
$$PWD/qv4mapobject.cpp \
$$PWD/qv4mapiterator.cpp \
$$PWD/qv4estable.cpp \
- $$PWD/qv4module.cpp
+ $$PWD/qv4module.cpp \
+ $$PWD/qv4promiseobject.cpp
qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp
@@ -126,7 +127,8 @@ HEADERS += \
$$PWD/qv4mapiterator_p.h \
$$PWD/qv4estable_p.h \
$$PWD/qv4vtable_p.h \
- $$PWD/qv4module_p.h
+ $$PWD/qv4module_p.h \
+ $$PWD/qv4promiseobject_p.h
qtConfig(qml-sequence-object) {
HEADERS += \
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 8f64c84eae..129bb20019 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -101,6 +101,7 @@
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4dataview_p.h"
+#include "qv4promiseobject_p.h"
#include "qv4typedarray_p.h"
#include <private/qv8engine_p.h>
#include <private/qjsvalue_p.h>
@@ -520,6 +521,14 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
+ //
+ // promises
+ //
+
+ jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(global);
+ jsObjects[PromiseProto] = memoryManager->allocate<PromisePrototype>();
+ static_cast<PromisePrototype *>(promisePrototype())->init(this, promiseCtor());
+
// typed arrays
jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(global);
@@ -570,6 +579,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
globalObject->defineDefaultProperty(QStringLiteral("SyntaxError"), *syntaxErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Promise"), *promiseCtor());
globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
@@ -876,6 +886,38 @@ Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message)
return ErrorObject::create<URIErrorObject>(this, message, uRIErrorCtor());
}
+Heap::PromiseObject *ExecutionEngine::newPromiseObject()
+{
+ if (!m_reactionHandler) {
+ m_reactionHandler.reset(new Promise::ReactionHandler);
+ }
+
+ Scope scope(this);
+ Scoped<PromiseObject> object(scope, memoryManager->allocate<PromiseObject>(this));
+ return object->d();
+}
+
+Heap::Object *ExecutionEngine::newPromiseObject(const QV4::FunctionObject *thisObject, const QV4::PromiseCapability *capability)
+{
+ if (!m_reactionHandler) {
+ m_reactionHandler.reset(new Promise::ReactionHandler);
+ }
+
+ Scope scope(this);
+ Scoped<CapabilitiesExecutorWrapper> executor(scope, memoryManager->allocate<CapabilitiesExecutorWrapper>());
+ executor->d()->capabilities.set(this, capability->d());
+ executor->insertMember(id_length(), Primitive::fromInt32(2), Attr_NotWritable|Attr_NotEnumerable);
+
+ ScopedObject object(scope, thisObject->callAsConstructor(executor, 1));
+ return object->d();
+}
+
+Promise::ReactionHandler *ExecutionEngine::getPromiseReactionHandler()
+{
+ Q_ASSERT(m_reactionHandler);
+ return m_reactionHandler.data();
+}
+
Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v)
{
return memoryManager->allocate<VariantObject>(v);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 276efe6e13..928c9f4947 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -94,6 +94,9 @@ struct Module;
struct Function;
+namespace Promise {
+class ReactionHandler;
+};
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
{
@@ -158,6 +161,7 @@ public:
SyntaxErrorProto,
TypeErrorProto,
URIErrorProto,
+ PromiseProto,
VariantProto,
#if QT_CONFIG(qml_sequence_object)
SequenceProto,
@@ -197,6 +201,7 @@ public:
TypeError_Ctor,
URIError_Ctor,
SharedArrayBuffer_Ctor,
+ Promise_Ctor,
ArrayBuffer_Ctor,
DataView_Ctor,
WeakSet_Ctor,
@@ -236,6 +241,7 @@ public:
FunctionObject *typeErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + TypeError_Ctor); }
FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); }
FunctionObject *sharedArrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + SharedArrayBuffer_Ctor); }
+ FunctionObject *promiseCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Promise_Ctor); }
FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); }
FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); }
FunctionObject *weakSetCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakSet_Ctor); }
@@ -266,6 +272,7 @@ public:
Object *syntaxErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SyntaxErrorProto); }
Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); }
Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); }
+ Object *promisePrototype() const { return reinterpret_cast<Object *>(jsObjects + PromiseProto); }
Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); }
#if QT_CONFIG(qml_sequence_object)
Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); }
@@ -523,6 +530,10 @@ public:
Heap::Object *newRangeErrorObject(const QString &message);
Heap::Object *newURIErrorObject(const Value &message);
+ Heap::PromiseObject *newPromiseObject();
+ Heap::Object *newPromiseObject(const QV4::FunctionObject *thisObject, const QV4::PromiseCapability *capability);
+ Promise::ReactionHandler *getPromiseReactionHandler();
+
Heap::Object *newVariantObject(const QVariant &v);
Heap::Object *newForInIteratorObject(Object *o);
@@ -611,6 +622,9 @@ private:
QScopedPointer<QV4::Profiling::Profiler> m_profiler;
#endif
int jitCallCountThreshold;
+
+ // used by generated Promise objects to handle 'then' events
+ QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler;
};
// This is a trick to tell the code generators that functions taking a NoThrowContext won't
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index eab519720f..95a87820fb 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -198,6 +198,9 @@ namespace Heap {
struct MapObject;
struct SetObject;
+ struct PromiseObject;
+ struct PromiseCapability;
+
template <typename T, size_t> struct Pointer;
}
@@ -248,6 +251,9 @@ struct TypedArray;
struct MapObject;
struct SetMapObject;
+struct PromiseObject;
+struct PromiseCapability;
+
// ReturnedValue is used to return values from runtime methods
// the type has to be a primitive type (no struct or union), so that the compiler
// will return it in a register on all platforms.
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
new file mode 100644
index 0000000000..a955e5eb6a
--- /dev/null
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -0,0 +1,979 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QCoreApplication>
+
+#include <private/qv4promiseobject_p.h>
+#include <private/qv4symbol_p.h>
+#include "qv4jscall_p.h"
+
+using namespace QV4;
+using namespace QV4::Promise;
+
+DEFINE_OBJECT_VTABLE(PromiseReaction);
+DEFINE_OBJECT_VTABLE(PromiseCtor);
+DEFINE_OBJECT_VTABLE(PromiseObject);
+DEFINE_OBJECT_VTABLE(PromiseCapability);
+DEFINE_OBJECT_VTABLE(PromiseExecutionState);
+
+DEFINE_OBJECT_VTABLE(CapabilitiesExecutorWrapper);
+DEFINE_OBJECT_VTABLE(ResolveElementWrapper);
+DEFINE_OBJECT_VTABLE(ResolveWrapper);
+DEFINE_OBJECT_VTABLE(RejectWrapper);
+
+
+namespace {
+
+bool isPromise(const Value &object)
+{
+ return object.as<PromiseObject>() != nullptr;
+}
+
+bool isCallable(const Value &object)
+{
+ return object.as<FunctionObject>() != nullptr;
+}
+
+void insertIdLengthTag(Scope& scope, Heap::FunctionObject* function)
+{
+ ScopedFunctionObject scopedFunction(scope, function);
+ scopedFunction->insertMember(scope.engine->id_length(), Primitive::fromInt32(1), Attr_NotWritable|Attr_NotEnumerable);
+}
+
+void dropException(QV4::ExecutionEngine* e)
+{
+ e->hasException = false;
+}
+}
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace Promise {
+
+const int PROMISE_REACTION_EVENT = QEvent::registerEventType();
+
+struct ReactionEvent : public QEvent
+{
+ ReactionEvent(ExecutionEngine *e, const Value *reaction_, const Value *resolution_)
+ : QEvent(QEvent::Type(PROMISE_REACTION_EVENT)),
+ reaction{e, *reaction_},
+ resolution{e, *resolution_}
+ {}
+
+ QV4::PersistentValue reaction;
+ QV4::PersistentValue resolution;
+};
+
+} // namespace Promise
+} // namespace QV4
+QT_END_NAMESPACE
+
+ReactionHandler::ReactionHandler(QObject *parent)
+ : QObject(parent)
+{}
+
+ReactionHandler::~ReactionHandler()
+{}
+
+void ReactionHandler::addReaction(ExecutionEngine *e, const Value *reaction, const Value *value)
+{
+ QCoreApplication::postEvent(this, new ReactionEvent(e, reaction, value));
+}
+
+void ReactionHandler::customEvent(QEvent *event)
+{
+ if (event)
+ {
+ const int type = event->type();
+ if (type == PROMISE_REACTION_EVENT)
+ executeReaction(static_cast<ReactionEvent*>(event));
+ }
+}
+
+void ReactionHandler::executeReaction(ReactionEvent *event)
+{
+ Scope scope(event->reaction.engine());
+
+ Scoped<QV4::PromiseReaction> ro(scope, event->reaction.as<QV4::PromiseReaction>());
+ Scoped<QV4::PromiseCapability> capability(scope, ro->d()->capability);
+
+ ScopedValue resolution(scope, event->resolution.value());
+ ScopedValue promise(scope, capability->d()->promise);
+
+ if (ro->d()->type == Heap::PromiseReaction::Function) {
+ ScopedFunctionObject handler(scope, ro->d()->handler.as<QV4::FunctionObject>());
+ ScopedValue result(scope, handler->call(promise, resolution, 1));
+
+ ScopedFunctionObject reaction(scope);
+ if (scope.hasException()) {
+ reaction = capability->d()->reject.as<QV4::FunctionObject>();
+ } else {
+ reaction = capability->d()->resolve.as<QV4::FunctionObject>();
+ }
+
+ reaction->call(promise, result, 1);
+ } else {
+ ScopedFunctionObject reaction(scope);
+ if (ro->d()->type == Heap::PromiseReaction::Identity) {
+ reaction = capability->d()->resolve.as<QV4::FunctionObject>();
+ } else {
+ reaction = capability->d()->reject.as<QV4::FunctionObject>();
+ }
+
+ reaction->call(promise, resolution, 1);
+ }
+}
+
+namespace {
+
+class FunctionBuilder {
+public:
+ static Heap::FunctionObject *makeResolveFunction(ExecutionEngine* e, QV4::Heap::PromiseObject *promise) {
+ Scope scope(e);
+ Scoped<QV4::ResolveWrapper> resolveWrapper(scope, e->memoryManager->allocate<QV4::ResolveWrapper>());
+
+ insertIdLengthTag(scope, resolveWrapper->d());
+ resolveWrapper->d()->promise.set(e, promise);
+
+ return resolveWrapper->d();
+ }
+
+ static Heap::FunctionObject *makeRejectFunction(ExecutionEngine* e, QV4::Heap::PromiseObject *promise) {
+ Scope scope(e);
+ Scoped<QV4::RejectWrapper> rejectWrapper(scope, e->memoryManager->allocate<QV4::RejectWrapper>());
+
+ insertIdLengthTag(scope, rejectWrapper->d());
+ rejectWrapper->d()->promise.set(e, promise);
+
+ return rejectWrapper->d();
+ }
+
+ static Heap::FunctionObject *makeResolveElementFunction(ExecutionEngine* e, uint index, Heap::PromiseExecutionState *executionState)
+ {
+ Scope scope(e);
+ Scoped<QV4::ResolveElementWrapper> resolveElementWrapper(scope, e->memoryManager->allocate<QV4::ResolveElementWrapper>());
+
+ resolveElementWrapper->d()->index = index;
+ resolveElementWrapper->d()->alreadyResolved = false;
+ resolveElementWrapper->d()->state.set(e, executionState);
+
+ insertIdLengthTag(scope, resolveElementWrapper->d());
+
+ return resolveElementWrapper->d();
+ }
+};
+
+}
+
+void Heap::PromiseObject::setState(PromiseObject::State state)
+{
+ this->state = state;
+}
+
+bool Heap::PromiseObject::isSettled() const
+{
+ return (state != Pending);
+}
+
+bool Heap::PromiseObject::isPending() const
+{
+ return (state == Pending);
+}
+
+bool Heap::PromiseObject::isFulfilled() const
+{
+ return (state == Fulfilled);
+}
+
+bool Heap::PromiseObject::isRejected() const
+{
+ return (state == Rejected);
+}
+
+void Heap::PromiseObject::triggerFullfillReactions(ExecutionEngine *e)
+{
+ Scope scope(e);
+ ScopedArrayObject a(scope, fulfillReactions);
+ if (a->arrayData()) {
+ Scoped<QV4::ArrayData> ad(scope, a->arrayData());
+ const uint sz = ad->length();
+ ScopedValue value(scope, resolution);
+ for (uint i = 0; i < sz; i++) {
+ Scoped<QV4::PromiseReaction> r(scope, ad->get(i));
+ r->d()->triggerWithValue(scope.engine, value);
+ }
+ }
+}
+
+void Heap::PromiseObject::triggerRejectReactions(ExecutionEngine *e)
+{
+ Scope scope(e);
+ ScopedArrayObject a(scope, rejectReactions);
+ if (a->arrayData()) {
+ Scoped<QV4::ArrayData> ad(scope, a->arrayData());
+ const uint sz = ad->d()->length();
+ ScopedValue value(scope, resolution);
+ for (uint i = 0; i < sz; i++) {
+ Scoped<QV4::PromiseReaction> r(scope, ad->d()->get(i));
+ r->d()->triggerWithValue(scope.engine, value);
+ }
+ }
+}
+
+Heap::PromiseReaction *Heap::PromiseReaction::createFulfillReaction(ExecutionEngine* e,
+ const QV4::PromiseCapability *capability, const QV4::FunctionObject *onFulfilled)
+{
+ Scope scope(e);
+ Scoped<QV4::PromiseReaction> fulfillReaction(scope, e->memoryManager->allocate<QV4::PromiseReaction>());
+ fulfillReaction->d()->capability.set(e, capability->d());
+
+ if (onFulfilled) {
+ QV4::ScopedFunctionObject scopedFullfillReaction(scope, onFulfilled);
+ if (!scopedFullfillReaction) {
+ fulfillReaction->d()->type = PromiseReaction::Identity;
+ } else {
+ fulfillReaction->d()->type = PromiseReaction::Function;
+ fulfillReaction->d()->handler.set(e, scopedFullfillReaction);
+ }
+ } else {
+ fulfillReaction->d()->type = PromiseReaction::Identity;
+ }
+
+ return fulfillReaction->d();
+}
+
+Heap::PromiseReaction *Heap::PromiseReaction::createRejectReaction(ExecutionEngine* e,
+ const QV4::PromiseCapability *capability, const QV4::FunctionObject *onRejected)
+{
+ Scope scope(e);
+ Scoped<QV4::PromiseReaction> rejectReaction(scope, e->memoryManager->allocate<QV4::PromiseReaction>());
+ rejectReaction->d()->capability.set(e, capability->d());
+
+ if (onRejected) {
+ ScopedFunctionObject scopedRejectReaction(scope, onRejected);
+ if (!scopedRejectReaction) {
+ rejectReaction->d()->type = PromiseReaction::Thrower;
+ } else {
+ rejectReaction->d()->type = PromiseReaction::Function;
+ rejectReaction->d()->handler.set(e, scopedRejectReaction);
+ }
+ } else {
+ rejectReaction->d()->type = PromiseReaction::Thrower;
+ }
+
+ return rejectReaction->d();
+}
+
+void Heap::PromiseReaction::triggerWithValue(ExecutionEngine *e, const Value *value)
+{
+ Scope scope(e);
+ auto handler = e->getPromiseReactionHandler();
+ ScopedValue reaction(scope, Value::fromHeapObject(this));
+ handler->addReaction(e, reaction, value);
+}
+
+void Heap::PromiseCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Promise"));
+}
+
+void Heap::PromiseObject::init(ExecutionEngine *e)
+{
+ Heap::Object::init();
+
+ {
+ Heap::ArrayObject* a = e->newArrayObject();
+ fulfillReactions.set(e, a);
+ }
+
+ {
+ Heap::ArrayObject* a = e->newArrayObject();
+ rejectReactions.set(e, a);
+ }
+}
+
+void Heap::CapabilitiesExecutorWrapper::init()
+{
+ Heap::FunctionObject::init();
+}
+
+void Heap::CapabilitiesExecutorWrapper::destroy()
+{
+ Heap::FunctionObject::destroy();
+}
+
+void Heap::PromiseExecutionState::init()
+{
+ index = 0;
+ remainingElementCount = 0;
+}
+
+void Heap::ResolveElementWrapper::init()
+{
+ index = 0;
+ alreadyResolved = false;
+
+ Heap::FunctionObject::init();
+}
+
+void Heap::ResolveWrapper::init()
+{
+ alreadyResolved = false;
+ Heap::FunctionObject::init();
+}
+
+void Heap::RejectWrapper::init()
+{
+ alreadyResolved = false;
+ Heap::FunctionObject::init();
+}
+
+
+ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ Scope scope(f);
+
+ if (argc != 1)
+ THROW_TYPE_ERROR();
+
+ ScopedFunctionObject executor(scope, argv[0].as<const FunctionObject>());
+ if (!executor)
+ THROW_TYPE_ERROR();
+
+ Scoped<PromiseObject> a(scope, scope.engine->newPromiseObject());
+ if (scope.engine->hasException)
+ return Encode::undefined();
+
+ a->d()->state = Heap::PromiseObject::Pending;
+
+ ScopedFunctionObject resolve(scope, FunctionBuilder::makeResolveFunction(scope.engine, a->d()));
+ ScopedFunctionObject reject(scope, FunctionBuilder::makeRejectFunction(scope.engine, a->d()));
+
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = resolve;
+ jsCallData->args[1] = reject;
+ jsCallData->thisObject = a;
+
+ executor->call(jsCallData);
+
+ if (scope.engine->hasException) {
+ a->d()->state = Heap::PromiseObject::Rejected;
+ a->d()->resolution.set(scope.engine, Value::fromReturnedValue(scope.engine->catchException()));
+ }
+
+ return a->asReturnedValue();
+}
+
+ReturnedValue PromiseCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ THROW_TYPE_ERROR();
+}
+
+ReturnedValue PromiseCtor::method_resolve(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ExecutionEngine* e = scope.engine;
+ if (!thisObject || !thisObject->isObject())
+ THROW_TYPE_ERROR();
+
+ ScopedValue argument(scope);
+ if (argc < 1) {
+ argument = Encode::undefined();
+ } else {
+ argument = argv[0];
+ }
+
+ if (isPromise(argument) && argument->isObject()) {
+ ScopedObject so(scope, thisObject);
+ ScopedObject constructor(scope, argument->objectValue()->get(e->id_constructor()));
+ if (so->d() == constructor->d())
+ return argument->asReturnedValue();
+ }
+
+ Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
+
+ ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<const FunctionObject>(), capability));
+ if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
+ THROW_TYPE_ERROR();
+
+ ScopedValue undefined(scope, Value::undefinedValue());
+ ScopedFunctionObject resolve(scope, capability->d()->resolve);
+ resolve->call(undefined, argument, 1);
+
+ return newPromise.asReturnedValue();
+}
+
+ReturnedValue PromiseCtor::method_reject(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ExecutionEngine *e = scope.engine;
+
+ if (!thisObject || !thisObject->isObject())
+ THROW_TYPE_ERROR();
+
+ ScopedValue argument(scope);
+ if (argc < 1) {
+ argument = Encode::undefined();
+ } else {
+ argument = argv[0];
+ }
+
+ Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
+
+ ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<const FunctionObject>(), capability));
+ if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
+ THROW_TYPE_ERROR();
+
+ ScopedValue undefined(scope, Value::undefinedValue());
+ ScopedFunctionObject reject(scope, capability->d()->reject.as<const FunctionObject>());
+ reject->call(undefined, argument, 1);
+
+ return newPromise.asReturnedValue();
+}
+
+ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(f);
+ ExecutionEngine* e = scope.engine;
+
+ if (!thisObject || !thisObject->isObject())
+ THROW_TYPE_ERROR();
+
+ ScopedString resolveName(scope, e->newIdentifier(QStringLiteral("resolve")));
+ ScopedString thenName(scope, e->newIdentifier(QStringLiteral("then")));
+
+ Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
+
+ ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<FunctionObject>(), capability));
+ if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject)) {
+ if (scope.hasException()) {
+ return e->exceptionValue->asReturnedValue();
+ } else {
+ THROW_TYPE_ERROR();
+ }
+ }
+ capability->d()->promise.set(e, newPromise);
+
+ ScopedFunctionObject reject(scope, capability->d()->reject);
+
+ ScopedObject itemsObject(scope, argv);
+ ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true));
+ if (!iteratorObject || scope.hasException()) {
+ ScopedObject error(scope);
+ if (scope.hasException()) {
+ error = e->exceptionValue;
+ dropException(e);
+ } else {
+ error = e->newTypeErrorObject(QStringLiteral("Type error"));
+ }
+ reject->call(newPromise, error, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ Scoped<QV4::PromiseExecutionState> executionState(scope, e->memoryManager->allocate<QV4::PromiseExecutionState>());
+ executionState->d()->remainingElementCount = 1;
+ executionState->d()->capability.set(e, capability);
+
+ Scoped<QV4::ArrayObject> results(scope, e->newArrayObject(0));
+ executionState->d()->values.set(e, results);
+
+ ScopedValue doneValue(scope);
+ uint index = 0;
+ for (;;) {
+ Scope scope(e);
+ ScopedValue nextValue(scope);
+ doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue));
+
+ if (doneValue->toBoolean())
+ break;
+
+ ScopedObject nextObject(scope);
+ if (nextValue->isObject()) {
+ nextObject = *nextValue;
+ } else if (nextValue->isBoolean()) {
+ nextObject = e->newBooleanObject(nextValue->toBoolean());
+ } else if (nextValue->isInteger() || nextValue->isDouble()) {
+ nextObject = e->newNumberObject(nextValue->toInteger());
+ } else if (nextValue->isString()) {
+ ScopedString scopedString(scope, nextValue->toString(scope.engine));
+ nextObject = e->newStringObject(scopedString);
+ }
+
+ ScopedFunctionObject resolve(scope, thisObject->as<Object>()->get(resolveName));
+ if (!resolve || scope.hasException()) {
+ ScopedValue completion(scope);
+ if (!scope.hasException()) {
+ completion = e->newTypeErrorObject(QStringLiteral("Type error"));
+ } else {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+
+ if (!doneValue->toBoolean())
+ completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
+ if (!nextPromise || scope.hasException()) {
+ ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue));
+ if (scope.hasException()) {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ executionState->d()->remainingElementCount++;
+
+ ScopedFunctionObject then(scope, nextPromise->get(thenName));
+ if (!then || scope.hasException()) {
+ ScopedValue completion(scope);
+ if (!scope.hasException()) {
+ completion = e->newTypeErrorObject(QStringLiteral("Type error"));
+ } else {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+
+ if (!doneValue->toBoolean())
+ completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue);
+
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ ScopedFunctionObject resolveElement(scope, FunctionBuilder::makeResolveElementFunction(e, index, executionState->d()));
+
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = resolveElement;
+ jsCallData->args[1] = reject;
+ jsCallData->thisObject = nextPromise;
+
+ then->call(jsCallData);
+ if (scope.hasException()) {
+ ScopedValue completion(scope, e->exceptionValue->asReturnedValue());
+ dropException(e);
+
+ if (!doneValue->toBoolean())
+ completion = Runtime::method_iteratorClose(scope.engine, iteratorObject, doneValue);
+
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ index++;
+ }
+
+ // empty list
+ executionState->d()->remainingElementCount--;
+ if (executionState->d()->remainingElementCount == 0) {
+ const FunctionObject *resolve = capability->d()->resolve.as<FunctionObject>();
+ if (!resolve)
+ THROW_TYPE_ERROR();
+
+ ScopedValue values(scope, executionState->d()->values);
+ resolve->call(newPromise, values, 1);
+ if (scope.hasException()) {
+ dropException(e);
+ reject->call(newPromise, scope.engine->exceptionValue, 1);
+ }
+ }
+
+ return newPromise.asReturnedValue();
+}
+
+ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(f);
+ ExecutionEngine* e = scope.engine;
+
+ if (!thisObject || !thisObject->isObject())
+ THROW_TYPE_ERROR();
+
+ ScopedString resolveName(scope, e->newIdentifier(QStringLiteral("resolve")));
+ ScopedString thenName(scope, e->newIdentifier(QStringLiteral("then")));
+
+ Scoped<PromiseCapability> capability(scope, scope.engine->memoryManager->allocate<QV4::PromiseCapability>());
+
+ ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<FunctionObject>(), capability));
+ if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
+ THROW_TYPE_ERROR();
+ capability->d()->promise.set(scope.engine, newPromise);
+
+ ScopedFunctionObject reject(scope, capability->d()->reject);
+
+ ScopedObject itemsObject(scope, argv);
+ ScopedObject iteratorObject(scope, Runtime::method_getIterator(e, itemsObject, true));
+ if (!iteratorObject) {
+ ScopedObject error(scope, e->newTypeErrorObject(QStringLiteral("Type error")));
+ reject->call(newPromise, error, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ ScopedValue doneValue(scope);
+ for (;;) {
+ Scope scope(e);
+ ScopedValue nextValue(scope);
+ doneValue = Value::fromReturnedValue(Runtime::method_iteratorNext(e, iteratorObject, nextValue));
+
+ if (scope.hasException()) {
+ ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue));
+ if (scope.hasException()) {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ if (doneValue->toBoolean())
+ break;
+
+ ScopedObject nextObject(scope);
+ if (nextValue->isObject()) {
+ nextObject = *nextValue;
+ } else if (nextValue->isBoolean()) {
+ nextObject = scope.engine->newBooleanObject(nextValue->toBoolean());
+ } else if (nextValue->isInteger() || nextValue->isDouble()) {
+ nextObject = scope.engine->newNumberObject(nextValue->toInteger());
+ } else if (nextValue->isString()) {
+ ScopedString scopedString(scope, nextValue->toString(scope.engine));
+ nextObject = scope.engine->newStringObject(scopedString);
+ }
+
+ ScopedFunctionObject resolve(scope, thisObject->as<FunctionObject>()->get(resolveName));
+ if (!resolve || scope.hasException()) {
+ ScopedValue completion(scope);
+ if (!scope.hasException()) {
+ completion = e->newTypeErrorObject(QStringLiteral("Type error"));
+ } else {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+
+ if (!doneValue->toBoolean())
+ completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1)));
+ if (!nextPromise || scope.hasException()) {
+ ScopedValue completion(scope, Runtime::method_iteratorClose(e, iteratorObject, doneValue));
+ if (scope.hasException()) {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ ScopedFunctionObject then(scope, nextPromise->get(thenName));
+ if (!then || scope.hasException()) {
+ ScopedValue completion(scope);
+ if (!scope.hasException()) {
+ completion = e->newTypeErrorObject(QStringLiteral("Type error"));
+ } else {
+ completion = e->exceptionValue->asReturnedValue();
+ dropException(e);
+ }
+
+ if (!doneValue->toBoolean())
+ completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+
+ ScopedFunctionObject resolveOriginalPromise(scope, capability->d()->resolve);
+
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = resolveOriginalPromise;
+ jsCallData->args[1] = reject;
+ jsCallData->thisObject = nextPromise;
+
+ then->call(jsCallData);
+ if (scope.hasException()) {
+ ScopedValue completion(scope, e->exceptionValue->asReturnedValue());
+ dropException(e);
+
+ if (!doneValue->toBoolean())
+ completion = Runtime::method_iteratorClose(e, iteratorObject, doneValue);
+
+ reject->call(newPromise, completion, 1);
+ return newPromise.asReturnedValue();
+ }
+ }
+
+ return newPromise.asReturnedValue();
+}
+
+void PromisePrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+
+ ctor->defineDefaultProperty(QStringLiteral("resolve"), PromiseCtor::method_resolve, 1);
+ ctor->defineDefaultProperty(QStringLiteral("reject"), PromiseCtor::method_reject, 1);
+ ctor->defineDefaultProperty(QStringLiteral("all"), PromiseCtor::method_all, 1);
+ ctor->defineDefaultProperty(QStringLiteral("race"), PromiseCtor::method_race, 1);
+ ctor->addSymbolSpecies();
+
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ ScopedString val(scope, engine->newString(QLatin1String("Promise")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+
+ defineDefaultProperty(QStringLiteral("then"), method_then, 2);
+ defineDefaultProperty(QStringLiteral("catch"), method_catch, 1);
+}
+
+ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ExecutionEngine* e = scope.engine;
+
+ Scoped<QV4::PromiseObject> promise(scope, thisObject);
+ if (!promise)
+ THROW_TYPE_ERROR();
+
+ ScopedFunctionObject onFulfilled(scope);
+ if (argc >= 1) {
+ onFulfilled = argv[0];
+ } else {
+ onFulfilled = Encode::undefined();
+ }
+
+ ScopedFunctionObject onRejected(scope);
+ if (argc >= 2) {
+ onRejected = argv[1];
+ } else {
+ onRejected = Encode::undefined();
+ }
+
+ Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<PromiseCapability>());
+
+ ScopedFunctionObject constructor(scope, promise->get(e->id_constructor()));
+ if (!constructor || scope.hasException())
+ THROW_TYPE_ERROR();
+
+ ScopedObject nextPromise(scope, e->newPromiseObject(constructor, capability));
+ capability->d()->promise.set(scope.engine, nextPromise);
+
+ Scoped<PromiseReaction> fulfillReaction(scope, Heap::PromiseReaction::createFulfillReaction(scope.engine, capability, onFulfilled));
+ Scoped<PromiseReaction> rejectReaction(scope, Heap::PromiseReaction::createRejectReaction(scope.engine, capability, onRejected));
+
+ ScopedValue resolution(scope, promise->d()->resolution);
+ if (promise->d()->isPending()) {
+ {
+ ScopedArrayObject a(scope, promise->d()->fulfillReactions);
+ ScopedValue newValue(scope, fulfillReaction->d());
+ a->push_back(newValue);
+ }
+
+ {
+ ScopedArrayObject a(scope, promise->d()->rejectReactions);
+ ScopedValue newValue(scope, rejectReaction->d());
+ a->push_back(newValue);
+ }
+ } else if (promise->d()->isFulfilled()) {
+ fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
+ } else if (promise->d()->isRejected()) {
+ rejectReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
+ } else {
+ Q_ASSERT(false);
+ THROW_GENERIC_ERROR("Should never be thrown. Unknown promise state");
+ }
+
+ return nextPromise->asReturnedValue();
+}
+
+ReturnedValue PromisePrototype::method_catch(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ Scoped<Object> promise(scope);
+ if (thisObject->isObject()) {
+ promise.setPointer(thisObject->as<Object>());
+ } else if (thisObject->isBoolean()) {
+ promise = scope.engine->newBooleanObject(thisObject->toBoolean());
+ } else if (thisObject->isInteger() || thisObject->isDouble()) {
+ promise = scope.engine->newNumberObject(thisObject->toInteger());
+ } else if (thisObject->isString()) {
+ ScopedString scopedString(scope, thisObject->toString(scope.engine));
+ promise = scope.engine->newStringObject(scopedString);
+ } else {
+ THROW_TYPE_ERROR();
+ }
+
+ ScopedValue onRejected(scope);
+ if (argc < 1) {
+ onRejected = Encode::undefined();
+ } else {
+ onRejected = argv[0];
+ }
+
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = Encode::undefined();
+ jsCallData->args[1] = onRejected;
+ jsCallData->thisObject = promise;
+
+ ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then")));
+ ScopedFunctionObject then(scope, promise->get(thenName));
+ if (!then || scope.hasException())
+ THROW_TYPE_ERROR();
+
+ return then->call(jsCallData);
+}
+
+ReturnedValue CapabilitiesExecutorWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Q_UNUSED(thisObject);
+
+ Scope scope(f);
+ const CapabilitiesExecutorWrapper* self = static_cast<const CapabilitiesExecutorWrapper*>(f);
+ Heap::PromiseCapability *capabilities = self->d()->capabilities;
+
+ if (!capabilities->resolve.isUndefined() || !capabilities->reject.isUndefined())
+ THROW_TYPE_ERROR();
+
+ if (argc >= 1 && !argv[0].isUndefined())
+ capabilities->resolve.set(scope.engine, argv[0]);
+
+ if (argc >= 2 && !argv[1].isUndefined())
+ capabilities->reject.set(scope.engine, argv[1]);
+
+ // TODO: return?
+ return Encode::undefined();
+}
+
+ReturnedValue ResolveElementWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Q_UNUSED(thisObject);
+
+ Scope scope(f);
+ const ResolveElementWrapper* self = static_cast<const ResolveElementWrapper*>(f);
+
+ if (self->d()->alreadyResolved)
+ return Encode::undefined();
+
+ ScopedValue value(scope);
+ if (argc == 1) {
+ value = argv[0];
+ } else {
+ value = Encode::undefined();
+ }
+
+ Scoped<PromiseExecutionState> so(scope, self->d()->state);
+ self->d()->alreadyResolved = true;
+
+ ScopedObject values(scope, so->d()->values);
+ values->arraySet(self->d()->index, value);
+
+ so->d()->remainingElementCount--;
+ if (so->d()->remainingElementCount == 0) {
+ Scoped<PromiseCapability> capability(scope, so->d()->capability);
+ ScopedValue promise(scope, capability->d()->promise);
+ ScopedFunctionObject resolve(scope, capability->d()->resolve.as<QV4::FunctionObject>());
+ resolve->call(promise, values, 1);
+ }
+
+ return Encode::undefined();
+}
+
+ReturnedValue ResolveWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Q_UNUSED(thisObject);
+
+ Scope scope(f);
+ const ResolveWrapper *self = static_cast<const ResolveWrapper*>(f);
+
+ Scoped<PromiseObject> promise(scope, self->d()->promise);
+ if (self->d()->alreadyResolved || !promise->d()->isPending())
+ return Encode::undefined();
+
+ ScopedValue value(scope);
+ if (argc == 1) {
+ value = argv[0];
+ } else {
+ value = Encode::undefined();
+ }
+
+ self->d()->alreadyResolved = true;
+ promise->d()->setState(Heap::PromiseObject::Fulfilled);
+ promise->d()->resolution.set(scope.engine, value);
+
+ promise->d()->triggerFullfillReactions(scope.engine);
+
+ return Encode::undefined();
+}
+
+ReturnedValue RejectWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Q_UNUSED(thisObject);
+
+ Scope scope(f);
+ const RejectWrapper *self = static_cast<const RejectWrapper*>(f);
+
+ Scoped<PromiseObject> promise(scope, self->d()->promise);
+ if (self->d()->alreadyResolved || !promise->d()->isPending())
+ return Encode::undefined();
+
+ ScopedValue value(scope);
+ if (argc == 1) {
+ value = argv[0];
+ } else {
+ value = Encode::undefined();
+ }
+
+ self->d()->alreadyResolved = true;
+ promise->d()->setState(Heap::PromiseObject::Rejected);
+ promise->d()->resolution.set(scope.engine, value);
+
+ promise->d()->triggerRejectReactions(scope.engine);
+
+ return Encode::undefined();
+}
diff --git a/src/qml/jsruntime/qv4promiseobject_p.h b/src/qml/jsruntime/qv4promiseobject_p.h
new file mode 100644
index 0000000000..80f7183074
--- /dev/null
+++ b/src/qml/jsruntime/qv4promiseobject_p.h
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4PROMISEOBJECT_H
+#define QV4PROMISEOBJECT_H
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct PromiseCapability;
+
+namespace Promise {
+
+struct ReactionEvent;
+
+class ReactionHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ ReactionHandler(QObject *parent = nullptr);
+ virtual ~ReactionHandler();
+
+ void addReaction(ExecutionEngine *e, const Value *reaction, const Value *value);
+
+protected:
+ void customEvent(QEvent *event);
+ void executeReaction(ReactionEvent *event);
+};
+
+} // Promise
+
+namespace Heap {
+
+struct PromiseCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+#define PromiseObjectMembers(class, Member) \
+ Member(class, HeapValue, HeapValue, resolution) \
+ Member(class, HeapValue, HeapValue, fulfillReactions) \
+ Member(class, HeapValue, HeapValue, rejectReactions)
+
+DECLARE_HEAP_OBJECT(PromiseObject, Object) {
+ DECLARE_MARKOBJECTS(PromiseObject)
+ void init(ExecutionEngine *e);
+
+ enum State {
+ Pending,
+ Fulfilled,
+ Rejected
+ };
+
+ void setState(State);
+ bool isSettled() const;
+ bool isPending() const;
+ bool isFulfilled() const;
+ bool isRejected() const;
+
+ State state;
+
+ void triggerFullfillReactions(ExecutionEngine *e);
+ void triggerRejectReactions(ExecutionEngine *e);
+};
+
+#define PromiseCapabilityMembers(class, Member) \
+ Member(class, HeapValue, HeapValue, promise) \
+ Member(class, HeapValue, HeapValue, resolve) \
+ Member(class, HeapValue, HeapValue, reject)
+
+DECLARE_HEAP_OBJECT(PromiseCapability, Object) {
+ DECLARE_MARKOBJECTS(PromiseCapability)
+};
+
+#define PromiseReactionMembers(class, Member) \
+ Member(class, HeapValue, HeapValue, handler) \
+ Member(class, Pointer, PromiseCapability*, capability)
+
+DECLARE_HEAP_OBJECT(PromiseReaction, Object) {
+ DECLARE_MARKOBJECTS(PromiseReaction)
+
+ static Heap::PromiseReaction *createFulfillReaction(ExecutionEngine* e, const QV4::PromiseCapability *capability, const QV4::FunctionObject *onFulfilled);
+ static Heap::PromiseReaction *createRejectReaction(ExecutionEngine* e, const QV4::PromiseCapability *capability, const QV4::FunctionObject *onRejected);
+
+ void triggerWithValue(ExecutionEngine *e, const Value *value);
+
+ enum Type {
+ Function,
+ Identity,
+ Thrower
+ };
+
+ Type type;
+
+ friend class ReactionHandler;
+};
+
+#define CapabilitiesExecutorWrapperMembers(class, Member) \
+ Member(class, Pointer, PromiseCapability*, capabilities)
+
+DECLARE_HEAP_OBJECT(CapabilitiesExecutorWrapper, FunctionObject) {
+ DECLARE_MARKOBJECTS(CapabilitiesExecutorWrapper)
+ void init();
+ void destroy();
+};
+
+#define PromiseExecutionStateMembers(class, Member) \
+ Member(class, HeapValue, HeapValue, values) \
+ Member(class, HeapValue, HeapValue, capability)
+
+DECLARE_HEAP_OBJECT(PromiseExecutionState, FunctionObject) {
+ DECLARE_MARKOBJECTS(PromiseExecutionState)
+ void init();
+
+ uint index;
+ uint remainingElementCount;
+};
+
+#define ResolveElementWrapperMembers(class, Member) \
+ Member(class, HeapValue, HeapValue, state)
+
+DECLARE_HEAP_OBJECT(ResolveElementWrapper, FunctionObject) {
+ DECLARE_MARKOBJECTS(ResolveElementWrapper)
+ void init();
+
+ uint index;
+ bool alreadyResolved;
+};
+
+#define ResolveWrapperMembers(class, Member) \
+ Member(class, Pointer, PromiseObject*, promise)
+
+DECLARE_HEAP_OBJECT(ResolveWrapper, FunctionObject) {
+ DECLARE_MARKOBJECTS(ResolveWrapper)
+ void init();
+
+ bool alreadyResolved;
+};
+
+#define RejectWrapperMembers(class, Member) \
+ Member(class, Pointer, PromiseObject*, promise)
+
+DECLARE_HEAP_OBJECT(RejectWrapper, FunctionObject) {
+ DECLARE_MARKOBJECTS(RejectWrapper)
+ void init();
+
+ bool alreadyResolved;
+};
+
+} // Heap
+
+struct PromiseReaction : Object
+{
+ V4_OBJECT2(PromiseReaction, Object)
+};
+
+struct PromiseCapability : Object
+{
+ V4_OBJECT2(PromiseCapability, Object)
+};
+
+struct PromiseExecutionState : Object
+{
+ V4_OBJECT2(PromiseExecutionState, Object)
+};
+
+struct Q_QML_PRIVATE_EXPORT PromiseObject : Object
+{
+ V4_OBJECT2(PromiseObject, Object)
+ V4_NEEDS_DESTROY
+ V4_PROTOTYPE(promisePrototype)
+};
+
+struct PromiseCtor: FunctionObject
+{
+ V4_OBJECT2(PromiseCtor, FunctionObject)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_resolve(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reject(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_all(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_race(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct PromisePrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_catch(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct CapabilitiesExecutorWrapper: FunctionObject {
+ V4_OBJECT2(CapabilitiesExecutorWrapper, FunctionObject)
+
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ResolveElementWrapper : FunctionObject {
+ V4_OBJECT2(ResolveElementWrapper, FunctionObject)
+
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ResolveWrapper : FunctionObject {
+ V4_OBJECT2(ResolveWrapper, FunctionObject)
+
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct RejectWrapper : FunctionObject {
+ V4_OBJECT2(RejectWrapper, FunctionObject)
+
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+} // QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4PROMISEOBJECT_H