aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsapi/qjsmanagedvalue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsapi/qjsmanagedvalue.cpp')
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp1028
1 files changed, 1028 insertions, 0 deletions
diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp
new file mode 100644
index 0000000000..fee2d9da10
--- /dev/null
+++ b/src/qml/jsapi/qjsmanagedvalue.cpp
@@ -0,0 +1,1028 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 <QtQml/qjsmanagedvalue.h>
+#include <QtQml/qjsengine.h>
+#include <QtQml/private/qv4persistent_p.h>
+#include <QtQml/private/qv4engine_p.h>
+#include <QtQml/private/qv4mm_p.h>
+#include <QtQml/private/qjsvalue_p.h>
+#include <QtQml/private/qv4runtime_p.h>
+#include <QtQml/private/qv4functionobject_p.h>
+#include <QtQml/private/qv4jscall_p.h>
+#include <QtQml/private/qv4urlobject_p.h>
+#include <QtQml/private/qv4variantobject_p.h>
+#include <QtQml/private/qv4qobjectwrapper_p.h>
+#include <QtQml/private/qv4regexpobject_p.h>
+#include <QtQml/private/qv4dateobject_p.h>
+#include <QtQml/private/qv4errorobject_p.h>
+
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \class QJSManagedValue
+ * \since 6.1
+ *
+ * \brief QJSManagedValue represents a value on the JavaScript heap belonging to a QJSEngine.
+ *
+ * The QJSManagedValue class allows interaction with JavaScript values in most
+ * ways you can interact with them from JavaScript itself. You can get and set
+ * properties and prototypes, and you can access arrays. Additionally, you can
+ * transform the value into the Qt counterparts of JavaScript objects. For
+ * example, a Url object may be transformed into a QUrl.
+ *
+ * A QJSManagedValue is always bound to a particular QJSEngine. You cannot use
+ * it independently. This means that you cannot have a QJSManagedValue from one
+ * engine be a property or a proptotype of a QJSManagedValue from a different
+ * engine.
+ *
+ * In contrast to QJSValue, almost all values held by QJSManagedValue live on
+ * the JavaScript heap. There is no inline or unmanaged storage. Therefore, you
+ * can get the prototype of a primitive value, and you can get the \c length
+ * property of a string.
+ *
+ * Only default-constructed or moved-from QJSManagedValues do not hold a value
+ * on the JavaScript heap. They represent \c undefined, which doesn't have any
+ * properties or prototypes.
+ *
+ * Also in contrast to QJSValue, QJSManagedValue does not catch any JavaScript
+ * exceptions. If an operation on a QJSManagedValue causes an error, it will
+ * generally return an \c undefined value and QJSEngine::hasError() will return
+ * \c true afterwards. You can then catch the exception using
+ * QJSEngine::catchError(), or pass it up the stack, at your own discretion.
+ *
+ * \note As the reference to the value on the JavaScript heap has to be freed
+ * on destruction, you cannot move a QJSManagedValue to a different thread.
+ * The destruction would take place in the new thread, which would create a race
+ * condition with the garbage collector on the original thread. This also means
+ * that you cannot hold a QJSManagedValue beyond the lifespan of its engine.
+ *
+ * The recommended way of working with a QJSManagedValue is creating it
+ * on the stack, possibly by moving a QJSValue and adding an engine, then
+ * performing the necessary operations on it, and finally moving it back into a
+ * QJSValue for storage. Moving between QJSManagedValue and QJSValue is fast.
+ */
+
+/*!
+ * \enum QJSManagedValue::Type
+ *
+ * This enum represents the JavaScript native types, as specified by
+ * \l{ECMA-26}.
+ *
+ * \value Undefined The \c undefined type
+ * \value Boolean The \c boolean type
+ * \value Number The \c number type
+ * \value String The \c string type
+ * \value Object The \c object type
+ * \value Symbol The \c symbol type
+ *
+ * Note that the \c null value is not a type of itself but rather a special kind
+ * of object. You can query a QJSManagedValue for this condition using the
+ * isNull() method. Furthermore, JavaScript has no integer type, but it knows a
+ * special treatment of numbers in preparation for integer only operations. You
+ * can query a QJSManagedValue to find out whether it holds the result of such a
+ * treatment by using the isInteger() method.
+ */
+
+/*!
+ * \fn QJSManagedValue::QJSManagedValue()
+ *
+ * Creates a QJSManagedValue that represents the JavaScript \c undefined value.
+ * This is the only value not stored on the JavaScript heap. Calling engine()
+ * on a default-constructed QJSManagedValue will return nullptr.
+ */
+
+static QV4::ExecutionEngine *v4Engine(QV4::Value *d)
+{
+ return d ? QV4::PersistentValueStorage::getEngine(d) : nullptr;
+}
+
+/*!
+ * Creates a QJSManagedValue from \a value, using the heap of \a engine. If
+ * \a value is itself managed and the engine it belongs to is not \a engine,
+ * the result is an \c undefined value, and a warning is generated.
+ */
+QJSManagedValue::QJSManagedValue(QJSValue value, QJSEngine *engine)
+{
+ QV4::ExecutionEngine *v4 = engine->handle();
+
+ if (QV4::Value *m = QJSValuePrivate::takeManagedValue(&value)) {
+ if (Q_UNLIKELY(v4Engine(m) != v4)) {
+ qWarning("QJSManagedValue(QJSValue, QJSEngine *) failed: "
+ "Value was created in different engine.");
+ QV4::PersistentValueStorage::free(m);
+ return;
+ }
+
+ d = m;
+ return;
+ }
+
+ d = v4->memoryManager->m_persistentValues->allocate();
+
+ if (const QString *string = QJSValuePrivate::asQString(&value))
+ *d = v4->newString(*string);
+ else
+ *d = QJSValuePrivate::asReturnedValue(&value);
+}
+
+/*!
+ * Creates a QJSManagedValue from \a value using the heap of \a engine.
+ */
+QJSManagedValue::QJSManagedValue(const QJSPrimitiveValue &value, QJSEngine *engine) :
+ QJSManagedValue(engine->handle())
+{
+ switch (value.type()) {
+ case QJSPrimitiveValue::Undefined:
+ *d = QV4::Encode::undefined();
+ return;
+ case QJSPrimitiveValue::Null:
+ *d = QV4::Encode::null();
+ return;
+ case QJSPrimitiveValue::Boolean:
+ *d = QV4::Encode(value.asBoolean());
+ return;
+ case QJSPrimitiveValue::Integer:
+ *d = QV4::Encode(value.asInteger());
+ return;
+ case QJSPrimitiveValue::Double:
+ *d = QV4::Encode(value.asDouble());
+ return;
+ case QJSPrimitiveValue::String:
+ *d = engine->handle()->newString(value.asString());
+ return;
+ }
+
+ Q_UNREACHABLE();
+}
+
+/*!
+ * Creates a QJSManagedValue from \a variant using the heap of \a engine.
+ */
+QJSManagedValue::QJSManagedValue(const QVariant &variant, QJSEngine *engine) :
+ QJSManagedValue(engine->handle())
+{
+ *d = engine->handle()->fromVariant(variant);
+}
+
+/*!
+ * Creates a QJSManagedValue from \a string using the heap of \a engine.
+ */
+QJSManagedValue::QJSManagedValue(const QString &string, QJSEngine *engine) :
+ QJSManagedValue(engine->handle())
+{
+ *d = engine->handle()->newString(string);
+}
+
+/*!
+ * Destroys the QJSManagedValue.
+ *
+ * \note This frees the memory slot it holds on the JavaScript heap. You must
+ * not destroy a QJSManagedValue from a different thread than the one
+ * where the QJSEngine it belongs to lives.
+ */
+QJSManagedValue::~QJSManagedValue()
+{
+ QV4::PersistentValueStorage::free(d);
+}
+
+/*!
+ * Move-constructs a QJSManagedValue from \a other. This leaves \a other in
+ * the default-constructed state where it represents undefined and does not
+ * belong to any engine.
+ */
+QJSManagedValue::QJSManagedValue(QJSManagedValue &&other)
+{
+ qSwap(d, other.d);
+}
+
+/*!
+ * Move-assigns a QJSManagedValue from \a other. This leaves \a other in
+ * the default-constructed state where it represents undefined and does not
+ * belong to any engine.
+ *
+ * \note This frees the memory slot this QJSManagedValue holds on the
+ * JavaScript heap. You must not move-assign a QJSManagedValue on a
+ * different thread than the one where the QJSEngine it belongs to lives.
+ */
+QJSManagedValue &QJSManagedValue::operator=(QJSManagedValue &&other)
+{
+ if (this != &other) {
+ QV4::PersistentValueStorage::free(d);
+ d = nullptr;
+ qSwap(d, other.d);
+ }
+ return *this;
+}
+
+/*!
+ * Invokes the JavaScript '==' operator on this QJSManagedValue and \a other,
+ * and returns the result.
+ *
+ * \sa strictlyEquals
+ */
+bool QJSManagedValue::equals(const QJSManagedValue &other) const
+{
+ if (!d)
+ return !other.d || other.d->isNullOrUndefined();
+ if (!other.d)
+ return d->isNullOrUndefined();
+
+ return QV4::Runtime::CompareEqual::call(*d, *other.d);
+}
+
+/*!
+ * Invokes the JavaScript '===' operator on this QJSManagedValue and \a other,
+ * and returns the result.
+ *
+ * \sa equals
+ */
+bool QJSManagedValue::strictlyEquals(const QJSManagedValue &other) const
+{
+ if (!d)
+ return !other.d || other.d->isUndefined();
+ if (!other.d)
+ return d->isUndefined();
+
+ return QV4::RuntimeHelpers::strictEqual(*d, *other.d);
+}
+
+/*!
+ * Returns the QJSEngine this QJSManagedValue belongs to. Mind that the engine
+ * is always valid, unless the QJSManagedValue is default-constructed or moved
+ * from. In the latter case a nullptr is returned.
+ */
+QJSEngine *QJSManagedValue::engine() const
+{
+ if (!d)
+ return nullptr;
+ if (QV4::ExecutionEngine *v4 = QV4::PersistentValueStorage::getEngine(d))
+ return v4->jsEngine();
+ return nullptr;
+}
+
+/*!
+ * Returns the prototype for this QJSManagedValue. This works on any value. You
+ * can, for example retrieve the JavaScript \c boolean prototype from a \c boolean
+ * value.
+ */
+QJSManagedValue QJSManagedValue::prototype() const
+{
+ if (!d)
+ return QJSManagedValue();
+
+ QV4::ExecutionEngine *v4 = v4Engine(d);
+ QJSManagedValue result(v4);
+
+ if (auto object = d->as<QV4::Object>())
+ *result.d = object->getPrototypeOf();
+ else if (auto managed = d->as<QV4::Managed>())
+ *result.d = managed->internalClass()->prototype;
+ else if (d->isBoolean())
+ *result.d = v4->booleanPrototype();
+ else if (d->isNumber())
+ *result.d = v4->numberPrototype();
+
+ // If the prototype appears to be undefined, then it's actually null in JS terms.
+ if (result.d->isUndefined())
+ *result.d = QV4::Encode::null();
+
+ return result;
+}
+
+/*!
+ * Sets the prototype of this QJSManagedValue to \a prototype. A precondition
+ * is that \a prototype belongs to the same QJSEngine as this QJSManagedValue
+ * and is an object (including null). Furthermore, this QJSManagedValue has to
+ * be an object (excluding null), too, and you cannot create prototype cycles.
+ */
+void QJSManagedValue::setPrototype(const QJSManagedValue &prototype)
+{
+ auto object = d ? d->as<QV4::Object>() : nullptr;
+ if (!object) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Can only set a prototype on an object (excluding null).");
+ return;
+ }
+
+ // Object includes null ...
+ if (prototype.type() != QJSManagedValue::Object) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Can only set objects (including null) as prototypes.");
+ return;
+ }
+
+ if (Q_UNLIKELY(object->engine() != v4Engine(prototype.d))) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Prototype was created in differen engine.");
+ return;
+ }
+
+ // ... Null becomes nullptr here. That is why it appears as undefined later.
+ if (!object->setPrototypeOf(prototype.d->as<QV4::Object>())) {
+ qWarning("QJSManagedValue::setPrototype() failed: "
+ "Prototype cycle detected.");
+ }
+}
+
+/*!
+ * Returns the JavaScript type of this QJSManagedValue.
+ */
+QJSManagedValue::Type QJSManagedValue::type() const
+{
+ if (!d || d->isUndefined())
+ return Undefined;
+ if (d->isBoolean())
+ return Boolean;
+ if (d->isNumber())
+ return Number;
+ if (d->isString())
+ return String;
+ if (d->isSymbol())
+ return Symbol;
+ return Object;
+}
+
+/*!
+ * \fn QJSManagedValue::isUndefined() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c undefined,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isBoolean() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c boolean,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isNumber() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c number,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isString() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c string,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isSymbol() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c symbol,
+ * or \c false otherwise.
+ */
+
+/*!
+ * \fn QJSManagedValue::isObject() const
+ *
+ * Returns \c true if the type of this QJSManagedValue is \c object,
+ * or \c false otherwise.
+ */
+
+/*!
+ * Returns \c true if this QJSManagedValue holds the JavaScript \c null value,
+ * or \c false otherwise.
+ */
+bool QJSManagedValue::isNull() const
+{
+ return d && d->isNull();
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue holds an integer value, or \c false
+ * otherwise. The storage format of a number does not affect the result of any
+ * operations performed on it, but if an integer is stored, many operations are
+ * faster.
+ */
+bool QJSManagedValue::isInteger() const
+{
+ return d && d->isInteger();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript regular expression
+ * object, or \c false otherwise.
+ */
+bool QJSManagedValue::isRegularExpression() const
+{
+ return d && d->as<QV4::RegExpObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Array
+ * object, or \c false otherwise.
+ */
+bool QJSManagedValue::isArray() const
+{
+ return d && d->as<QV4::ArrayObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Url
+ * object, or \c false otherwise.
+ */
+bool QJSManagedValue::isUrl() const
+{
+ return d && d->as<QV4::UrlObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a QVariant managed on the JavaScript
+ * heap, or \c false otherwise.
+ */
+bool QJSManagedValue::isVariant() const
+{
+ return d && d->as<QV4::VariantObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a QObject pointer managed on the
+ * JavaScript heap, or \c false otherwise.
+ */
+bool QJSManagedValue::isQObject() const
+{
+ return d && d->as<QV4::QObjectWrapper>();
+}
+
+/*!
+ * Returns \c true if this value represents a QMetaObject pointer managed on the
+ * JavaScript heap, or \c false otherwise.
+ */
+bool QJSManagedValue::isQMetaObject() const
+{
+ return d && d->as<QV4::QMetaObjectWrapper>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Date object, or
+ * \c false otherwise.
+ */
+bool QJSManagedValue::isDate() const
+{
+ return d && d->as<QV4::DateObject>();
+}
+
+/*!
+ * Returns \c true if this value represents a JavaScript Error object, or
+ * \c false otherwise.
+ */
+bool QJSManagedValue::isError() const
+{
+ return d && d->as<QV4::ErrorObject>();
+}
+
+/*!
+ * Converts the manged value to a string. If the managed value holds a string,
+ * that one is returned. Otherwise a string coercion by JavaScript rules is
+ * performed.
+ *
+ * \note Conversion of a managed value to a string can throw an exception. In
+ * particular, symbols cannot be coerced into strings, or a custom
+ * toString() method may throw. In this case the result is an empty
+ * string and the engine carries an error after the conversion.
+ */
+QString QJSManagedValue::toString() const
+{
+ return d ? d->toQString() : QStringLiteral("undefined");
+}
+
+/*!
+ * Converts the manged value to a number. If the managed value holds a number,
+ * that one is returned. Otherwise a number coercion by JavaScript rules is
+ * performed.
+ *
+ * \note Conversion of a managed value to a number can throw an exception. In
+ * particular, symbols cannot be coerced into numbers, or a custom
+ * valueOf() method may throw. In this case the result is 0 and the
+ * engine carries an error after the conversion.
+ */
+double QJSManagedValue::toNumber() const
+{
+ return d ? d->toNumber() : 0;
+}
+
+/*!
+ * Converts the manged value to a boolean. If the managed value holds a boolean,
+ * that one is returned. Otherwise a boolean coercion by JavaScript rules is
+ * performed.
+ */
+bool QJSManagedValue::toBoolean() const
+{
+ return d ? d->toBoolean() : false;
+}
+
+/*!
+ * Converts the manged value to an integer. This first converts the value to a
+ * number by the rules of toNumber(), and then clamps it into the integer range
+ * by the rules given for coercing the arguments to JavaScript bit shift
+ * operators into 32bit integers.
+ *
+ * Internally, the value may already be stored as an integer, in which case a
+ * fast path is taken.
+ *
+ * \note Conversion of a managed value to a number can throw an exception. In
+ * particular, symbols cannot be coerced into numbers, or a custom
+ * valueOf() method may throw. In this case the result is 0 and the
+ * engine carries an error after the conversion.
+ *
+ * \note The JavaScript rules for coercing numbers into 32bit integers are
+ * unintuitive.
+ */
+int QJSManagedValue::toInteger() const
+{
+ return d ? d->toInt32() : 0;
+}
+
+/*!
+ * Converts the manged value to a QJSPrimitiveValue. If the managed value holds
+ * a type supported by QJSPrimitiveValue, the value is copied. Otherwise the
+ * value is converted to a string, and the string is stored in
+ * QJSPrimitiveValue.
+ *
+ * \note Conversion of a managed value to a string can throw an exception. In
+ * particular, symbols cannot be coerced into strings, or a custom
+ * toString() method may throw. In this case the result is the undefined
+ * value and the engine carries an error after the conversion.
+ */
+QJSPrimitiveValue QJSManagedValue::toPrimitive() const
+{
+ if (!d || d->isUndefined())
+ return QJSPrimitiveUndefined();
+ if (d->isNull())
+ return QJSPrimitiveNull();
+ if (d->isBoolean())
+ return d->booleanValue();
+ if (d->isInteger())
+ return d->integerValue();
+ if (d->isNumber())
+ return d->doubleValue();
+
+ bool ok;
+ const QString result = d->toQString(&ok);
+ return ok ? QJSPrimitiveValue(result) : QJSPrimitiveValue(QJSPrimitiveUndefined());
+}
+
+/*!
+ * Copies this QJSManagedValue into a new QJSValue. This is less efficient than
+ * move-constructing a QJSValue from a QJSManagedValue, but retains the
+ * QJSManagedValue.
+ */
+QJSValue QJSManagedValue::toJSValue() const
+{
+ return d ? QJSValuePrivate::fromReturnedValue(d->asReturnedValue()) : QJSValue();
+}
+
+/*!
+ * Copies this QJSManagedValue into a new QVariant. This also creates a useful
+ * QVariant if QJSManagedValue::isVariant() returns false. QVariant can hold all
+ * types supported by QJSManagedValue.
+ */
+QVariant QJSManagedValue::toVariant() const
+{
+ if (!d || d->isUndefined())
+ return QVariant();
+ if (d->isNull())
+ return QVariant(QMetaType::fromType<std::nullptr_t>(), nullptr);
+ if (d->isBoolean())
+ return QVariant(d->booleanValue());
+ if (d->isInteger())
+ return QVariant(d->integerValue());
+ if (d->isNumber())
+ return QVariant(d->doubleValue());
+ if (d->isString())
+ return QVariant(d->toQString());
+ if (QV4::Managed *m = d->as<QV4::Managed>())
+ return m->engine()->toVariant(*d, -1, true);
+
+ Q_UNREACHABLE();
+ return QVariant();
+}
+
+/*!
+ * If this QJSManagedValue holds a JavaScript regular expression object, returns
+ * an equivalent QRegularExpression. Otherwise returns an invalid one.
+ */
+QRegularExpression QJSManagedValue::toRegularExpression() const
+{
+ if (const auto *r = d ? d->as<QV4::RegExpObject>() : nullptr)
+ return r->toQRegularExpression();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a JavaScript Url object, returns
+ * an equivalent QUrl. Otherwise returns an invalid one.
+ */
+QUrl QJSManagedValue::toUrl() const
+{
+ if (const auto *u = d ? d->as<QV4::UrlObject>() : nullptr)
+ return u->toQUrl();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a QObject pointer, returns it. Otherwise
+ * returns nullptr.
+ */
+QObject *QJSManagedValue::toQObject() const
+{
+ if (const auto *o = d ? d->as<QV4::QObjectWrapper>() : nullptr)
+ return o->object();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a QMetaObject pointer, returns it.
+ * Otherwise returns nullptr.
+ */
+const QMetaObject *QJSManagedValue::toQMetaObject() const
+{
+ if (const auto *m = d ? d->as<QV4::QMetaObjectWrapper>() : nullptr)
+ return m->metaObject();
+ return {};
+}
+
+/*!
+ * If this QJSManagedValue holds a JavaScript Date object, returns an equivalent
+ * QDateTime. Otherwise returns an invalid one.
+ */
+QDateTime QJSManagedValue::toDateTime() const
+{
+ if (const auto *t = d ? d->as<QV4::DateObject>() : nullptr)
+ return t->toQDateTime();
+ return {};
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has a property \a name, otherwise
+ * returns \c false. The properties of the prototype chain are considered.
+ */
+bool QJSManagedValue::hasProperty(const QString &name) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (d->isString() && name == QStringLiteral("length"))
+ return true;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedString str(scope, obj->engine()->newString(name));
+ return obj->hasProperty(str->toPropertyKey());
+ }
+
+ return prototype().hasProperty(name);
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has a property \a name, otherwise
+ * returns \c false. The properties of the prototype chain are not considered.
+ */
+bool QJSManagedValue::hasOwnProperty(const QString &name) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (d->isString() && name == QStringLiteral("length"))
+ return true;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedString str(scope, obj->engine()->newString(name));
+ return obj->getOwnProperty(str->toPropertyKey()) != QV4::Attr_Invalid;
+ }
+
+ return false;
+}
+
+/*!
+ * Returns the property \a name of this QJSManagedValue. The prototype chain
+ * is searched if the property is not found on the actual object.
+ */
+QJSValue QJSManagedValue::property(const QString &name) const
+{
+ if (!d || d->isNullOrUndefined())
+ return QJSValue();
+
+ if (QV4::String *string = d->as<QV4::String>()) {
+ if (name == QStringLiteral("length"))
+ return QJSValue(string->d()->length());
+ }
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedString str(scope, obj->engine()->newString(name));
+ return QJSValuePrivate::fromReturnedValue(obj->get(str->toPropertyKey()));
+ }
+
+ return prototype().property(name);
+}
+
+/*!
+ * Sets the property \a name to \a value on this QJSManagedValue. This can only
+ * be done on JavaScript values of type \c object. Furhermore, \a value has to be
+ * either a primitive or belong to the same engine as this value.
+ */
+void QJSManagedValue::setProperty(const QString &name, const QJSValue &value)
+{
+ if (!d)
+ return;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
+ if (Q_UNLIKELY(v4 && v4 != obj->engine())) {
+ qWarning("QJSManagedValue::setProperty() failed: "
+ "Value was created in different engine.");
+ return;
+ }
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedString str(scope, obj->engine()->newString(name));
+ obj->put(str->toPropertyKey(), QJSValuePrivate::convertToReturnedValue(v4, value));
+ }
+}
+
+/*!
+ * Deletes the property \a name from this QJSManagedValue. Returns \c true if
+ * the deletion succeeded, or \a false otherwise.
+ */
+bool QJSManagedValue::deleteProperty(const QString &name)
+{
+ if (!d)
+ return false;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::Scope scope(obj->engine());
+ QV4::ScopedString str(scope, obj->engine()->newString(name));
+ return obj->deleteProperty(str->toPropertyKey());
+ }
+
+ return false;
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has an array index \a arrayIndex,
+ * otherwise returns \c false. The properties of the prototype chain are
+ * considered.
+ */
+bool QJSManagedValue::hasProperty(quint32 arrayIndex) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ bool hasProperty = false;
+ if (arrayIndex == std::numeric_limits<quint32>::max())
+ obj->get(obj->engine()->id_uintMax(), &hasProperty);
+ else
+ obj->get(arrayIndex, &hasProperty);
+ return hasProperty;
+ }
+
+ return prototype().hasProperty(arrayIndex);
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue has an array index \a arrayIndex,
+ * otherwise returns \c false. The properties of the prototype chain are not
+ * considered.
+ */
+bool QJSManagedValue::hasOwnProperty(quint32 arrayIndex) const
+{
+ if (!d || d->isNullOrUndefined())
+ return false;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ if (arrayIndex == std::numeric_limits<quint32>::max()) {
+ return obj->getOwnProperty(obj->engine()->id_uintMax()->toPropertyKey())
+ != QV4::Attr_Invalid;
+ } else {
+ return obj->getOwnProperty(QV4::PropertyKey::fromArrayIndex(arrayIndex))
+ != QV4::Attr_Invalid;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ * Returns the property stored at \a arrayIndex of this QJSManagedValue. The
+ * prototype chain is searched if the property is not found on the actual
+ * object.
+ */
+QJSValue QJSManagedValue::property(quint32 arrayIndex) const
+{
+ if (!d || d->isNullOrUndefined())
+ return QJSValue();
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ if (arrayIndex == std::numeric_limits<quint32>::max())
+ return QJSValuePrivate::fromReturnedValue(obj->get(obj->engine()->id_uintMax()));
+ else
+ return QJSValuePrivate::fromReturnedValue(obj->get(arrayIndex));
+ }
+
+ return prototype().property(arrayIndex);
+}
+
+/*!
+ * Stores the \a value at \a arrayIndex in this QJSManagedValue. This can only
+ * be done on JavaScript values of type \c object, and it's not recommended if the
+ * value is not an array. Furhermore, \a value has to be either a primitive or
+ * belong to the same engine as this value.
+ */
+void QJSManagedValue::setProperty(quint32 arrayIndex, const QJSValue &value)
+{
+ if (!d)
+ return;
+
+ if (QV4::Object *obj = d->as<QV4::Object>()) {
+ QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value);
+ if (Q_UNLIKELY(v4 && v4 != obj->engine())) {
+ qWarning("QJSManagedValue::setProperty() failed: "
+ "Value was created in different engine.");
+ return;
+ }
+ obj->put(arrayIndex, QJSValuePrivate::convertToReturnedValue(v4, value));
+ }
+}
+
+/*!
+ * Deletes the value stored at \a arrayIndex from this QJSManagedValue. Returns
+ * \c true if the deletion succeeded, or \a false otherwise.
+ */
+bool QJSManagedValue::deleteProperty(quint32 arrayIndex)
+{
+ if (!d)
+ return false;
+
+ if (QV4::Object *obj = d->as<QV4::Object>())
+ return obj->deleteProperty(QV4::PropertyKey::fromArrayIndex(arrayIndex));
+
+ return false;
+}
+
+/*!
+ * Returns \c true if this QJSManagedValue is a JavaScript FunctionObject, or
+ * \c false otherwise.
+ */
+bool QJSManagedValue::isCallable() const
+{
+ return d && d->isFunctionObject();
+}
+
+/*!
+ * If this QJSManagedValue represents a JavaScript FunctionObject, calls it with
+ * the given \a arguments, and returns the result. Otherwise returns a
+ * JavaScript \c undefined value.
+ *
+ * The \a arguments have to be either primitive values or belong to the same
+ * QJSEngine as this QJSManagedValue. Otherwise the call is not carried
+ * out and a JavaScript \c undefined value is returned.
+ */
+QJSValue QJSManagedValue::call(const QJSValueList &arguments) const
+{
+ const QV4::FunctionObject *f = d ? d->as<QV4::FunctionObject>() : nullptr;
+ if (!f)
+ return QJSValue();
+
+ QV4::ExecutionEngine *engine = f->engine();
+
+ QV4::Scope scope(engine);
+ QV4::JSCallData jsCallData(scope, arguments.length());
+ *jsCallData->thisObject = engine->globalObject;
+ int i = 0;
+ for (const QJSValue &arg : arguments) {
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::call() failed: Argument was created in different engine.");
+ return QJSValue();
+ }
+ jsCallData->args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ }
+
+ return QJSValuePrivate::fromReturnedValue(f->call(jsCallData));
+}
+
+/*!
+ * If this QJSManagedValue represents a JavaScript FunctionObject, calls it on
+ * \a instance with the given \a arguments, and returns the result. Otherwise
+ * returns a JavaScript \c undefined value.
+ *
+ * The \a arguments and the \a instance have to be either primitive values or
+ * belong to the same QJSEngine as this QJSManagedValue. Otherwise the call is
+ * not carried out and a JavaScript \c undefined value is returned.
+ */
+QJSValue QJSManagedValue::callWithInstance(const QJSValue &instance,
+ const QJSValueList &arguments) const
+{
+ const QV4::FunctionObject *f = d ? d->as<QV4::FunctionObject>() : nullptr;
+ if (!f)
+ return QJSValue();
+
+ QV4::ExecutionEngine *engine = f->engine();
+
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, instance))) {
+ qWarning("QJSManagedValue::callWithInstance() failed: "
+ "Instance was created in different engine.");
+ return QJSValue();
+ }
+
+ QV4::Scope scope(engine);
+ QV4::JSCallData jsCallData(scope, arguments.length());
+ *jsCallData->thisObject = QJSValuePrivate::convertToReturnedValue(engine, instance);
+ int i = 0;
+ for (const QJSValue &arg : arguments) {
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::callWithInstance() failed: "
+ "Argument was created in different engine.");
+ return QJSValue();
+ }
+ jsCallData->args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ }
+
+ return QJSValuePrivate::fromReturnedValue(f->call(jsCallData));
+}
+
+/*!
+ * If this QJSManagedValue represents a JavaScript FunctionObject, calls it as
+ * constructor with the given \a arguments, and returns the result. Otherwise
+ * returns a JavaScript \c undefined value.
+ *
+ * The \a arguments have to be either primitive values or belong to the same
+ * QJSEngine as this QJSManagedValue. Otherwise the call is not carried
+ * out and a JavaScript \c undefined value is returned.
+ */
+QJSValue QJSManagedValue::callAsConstructor(const QJSValueList &arguments) const
+{
+ const QV4::FunctionObject *f = d ? d->as<QV4::FunctionObject>() : nullptr;
+ if (!f)
+ return QJSValue();
+
+ QV4::ExecutionEngine *engine = f->engine();
+
+ QV4::Scope scope(engine);
+ QV4::JSCallData jsCallData(scope, arguments.length());
+ int i = 0;
+ for (const QJSValue &arg : arguments) {
+ if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) {
+ qWarning("QJSManagedValue::callAsConstructor() failed: "
+ "Argument was created in different engine.");
+ return QJSValue();
+ }
+ jsCallData->args[i++] = QJSValuePrivate::convertToReturnedValue(engine, arg);
+ }
+
+ return QJSValuePrivate::fromReturnedValue(f->callAsConstructor(jsCallData));
+}
+
+QJSManagedValue::QJSManagedValue(QV4::ExecutionEngine *engine) :
+ d(engine ? engine->memoryManager->m_persistentValues->allocate() : nullptr)
+{
+}
+
+QT_END_NAMESPACE