aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqml.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqml.cpp')
-rw-r--r--src/qml/qml/qqml.cpp2441
1 files changed, 2267 insertions, 174 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index a33936647f..eb716671b1 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -1,56 +1,232 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqml.h"
#include <QtQml/qqmlprivate.h>
+#include <private/qjsvalue_p.h>
+#include <private/qqmlbuiltinfunctions_p.h>
+#include <private/qqmlcomponent_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qqmlfinalizer_p.h>
+#include <private/qqmlloggingcategory_p.h>
#include <private/qqmlmetatype_p.h>
#include <private/qqmlmetatypedata_p.h>
#include <private/qqmltype_p_p.h>
-#include <private/qqmltypemodule_p_p.h>
+#include <private/qqmltypemodule_p.h>
+#include <private/qqmltypewrapper_p.h>
+#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4errorobject_p.h>
+#include <private/qv4identifiertable_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4qobjectwrapper_p.h>
#include <QtCore/qmutex.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQml);
+Q_DECLARE_LOGGING_CATEGORY(lcJs);
+
+/*!
+ \internal
+
+ This method completes the setup of all deferred properties of \a object.
+ Deferred properties are declared with
+ Q_CLASSINFO("DeferredPropertyNames", "comma,separated,property,list");
+
+ Any binding to a deferred property is not executed when the object is instantiated,
+ but only when completion is requested with qmlExecuteDeferred, or by manually
+ calling QQmlComponentPrivate::beginDeferred and completeDeferred.
+
+ \sa QV4::CompiledData::Binding::IsDeferredBinding,
+ QV4::CompiledData::Object::HasDeferredBindings,
+ QQmlData::deferData,
+ QQmlObjectCreator::setupBindings
+*/
+void qmlExecuteDeferred(QObject *object)
+{
+ QQmlData *data = QQmlData::get(object);
+
+ if (!data
+ || !data->context
+ || !data->context->engine()
+ || data->deferredData.isEmpty()
+ || data->wasDeleted(object)) {
+ return;
+ }
+
+ if (!data->propertyCache)
+ data->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
+
+ QQmlComponentPrivate::DeferredState state;
+ QQmlComponentPrivate::beginDeferred(ep, object, &state);
+
+ // Release the reference for the deferral action (we still have one from construction)
+ data->releaseDeferredData();
+
+ QQmlComponentPrivate::completeDeferred(ep, &state);
+}
+
+QQmlContext *qmlContext(const QObject *obj)
+{
+ return QQmlEngine::contextForObject(obj);
+}
+
+QQmlEngine *qmlEngine(const QObject *obj)
+{
+ QQmlData *data = QQmlData::get(obj);
+ if (!data || !data->context)
+ return nullptr;
+ return data->context->engine();
+}
+
+static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data,
+ QObject *object, bool create)
+{
+ if (!pf)
+ return nullptr;
+
+ QObject *rv = data->hasExtendedData() ? data->attachedProperties()->value(pf) : 0;
+ if (rv || !create)
+ return rv;
+
+ rv = pf(object);
+
+ if (rv)
+ data->attachedProperties()->insert(pf, rv);
+
+ return rv;
+}
+
+QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object,
+ const QMetaObject *attachedMetaObject)
+{
+ QQmlEngine *engine = object ? qmlEngine(object) : nullptr;
+ return QQmlMetaType::attachedPropertiesFunc(engine ? QQmlEnginePrivate::get(engine) : nullptr,
+ attachedMetaObject);
+}
+
+QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool create)
+{
+ if (!object)
+ return nullptr;
+
+ QQmlData *data = QQmlData::get(object, create);
+
+ // Attached properties are only on objects created by QML,
+ // unless explicitly requested (create==true)
+ if (!data)
+ return nullptr;
+
+ return resolveAttachedProperties(func, data, object, create);
+}
+
+QObject *qmlExtendedObject(QObject *object)
+{
+ return QQmlPrivate::qmlExtendedObject(object, 0);
+}
+
+QObject *QQmlPrivate::qmlExtendedObject(QObject *object, int index)
+{
+ if (!object)
+ return nullptr;
+
+ void *result = nullptr;
+ QObjectPrivate *d = QObjectPrivate::get(object);
+ if (!d->metaObject)
+ return nullptr;
+
+ const int id = d->metaObject->metaCall(
+ object, QMetaObject::CustomCall,
+ QQmlProxyMetaObject::extensionObjectId(index), &result);
+ if (id != QQmlProxyMetaObject::extensionObjectId(index))
+ return nullptr;
+
+ return static_cast<QObject *>(result);
+}
+
+void QQmlPrivate::qmlRegistrationWarning(
+ QQmlPrivate::QmlRegistrationWarning warning, QMetaType metaType)
+{
+ switch (warning) {
+ case UnconstructibleType:
+ qWarning().nospace()
+ << metaType.name()
+ << " is neither a default constructible QObject, nor a default- "
+ << "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
+ << "You should not use it as a QML type.";
+ break;
+ case UnconstructibleSingleton:
+ qWarning()
+ << "Singleton" << metaType.name()
+ << "needs to be a concrete class with either a default constructor"
+ << "or, when adding a default constructor is infeasible, a public static"
+ << "create(QQmlEngine *, QJSEngine *) method.";
+ break;
+ case NonQObjectWithAtached:
+ qWarning()
+ << metaType.name()
+ << "is not a QObject, but has attached properties. This won't work.";
+ break;
+ }
+}
+
+QMetaType QQmlPrivate::compositeMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .typeId();
+}
+
+QMetaType QQmlPrivate::compositeListMetaType(
+ QV4::ExecutableCompilationUnit *unit, const QString &elementName)
+{
+ return QQmlTypePrivate::compositeQmlType(
+ unit->baseCompilationUnit(), unit->engine->typeLoader(), elementName)
+ .qListTypeId();
+}
+
+int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
+ const char *uri, int versionMajor,
+ int versionMinor, const char *qmlName,
+ const QString& reason)
+{
+ QQmlPrivate::RegisterType type = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ QMetaType(),
+ QMetaType(),
+ 0,
+ nullptr,
+ nullptr,
+ reason,
+ nullptr,
+
+ uri, QTypeRevision::fromVersion(versionMajor, versionMinor), qmlName, &staticMetaObject,
+
+ QQmlAttachedPropertiesFunc(),
+ nullptr,
+
+ -1,
+ -1,
+ -1,
+
+ nullptr, nullptr,
+
+ nullptr,
+ QTypeRevision::zero(),
+ -1,
+ QQmlPrivate::ValueTypeCreationMethod::None
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
void qmlClearTypeRegistrations() // Declared in qqml.h
{
QQmlMetaType::clearTypeRegistrations();
@@ -61,60 +237,200 @@ void qmlClearTypeRegistrations() // Declared in qqml.h
//From qqml.h
bool qmlProtectModule(const char *uri, int majVersion)
{
- return QQmlMetaType::protectModule(QString::fromUtf8(uri), majVersion);
+ return QQmlMetaType::protectModule(QString::fromUtf8(uri),
+ QTypeRevision::fromMajorVersion(majVersion));
}
//From qqml.h
void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
{
- QQmlMetaType::registerModule(uri, versionMajor, versionMinor);
+ QQmlMetaType::registerModule(uri, QTypeRevision::fromVersion(versionMajor, versionMinor));
+}
+
+static QQmlDirParser::Import resolveImport(const QString &uri, int importMajor, int importMinor)
+{
+ if (importMajor == QQmlModuleImportAuto)
+ return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Auto);
+ else if (importMajor == QQmlModuleImportLatest)
+ return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Default);
+ else if (importMinor == QQmlModuleImportLatest)
+ return QQmlDirParser::Import(uri, QTypeRevision::fromMajorVersion(importMajor), QQmlDirParser::Import::Default);
+ return QQmlDirParser::Import(uri, QTypeRevision::fromVersion(importMajor, importMinor), QQmlDirParser::Import::Default);
+}
+
+static QTypeRevision resolveModuleVersion(int moduleMajor)
+{
+ return moduleMajor == QQmlModuleImportModuleAny
+ ? QTypeRevision()
+ : QTypeRevision::fromMajorVersion(moduleMajor);
+}
+
+/*!
+ * \enum QQmlModuleImportSpecialVersions
+ * \relates QQmlEngine
+ *
+ * Defines some special values that can be passed to the version arguments of
+ * qmlRegisterModuleImport() and qmlUnregisterModuleImport().
+ *
+ * \value QQmlModuleImportModuleAny When passed as majorVersion of the base
+ * module, signifies that the import is to be
+ * applied to any version of the module.
+ * \value QQmlModuleImportLatest When passed as major or minor version of
+ * the imported module, signifies that the
+ * latest overall, or latest minor version
+ * of a specified major version shall be
+ * imported.
+ * \value QQmlModuleImportAuto When passed as major version of the imported
+ * module, signifies that the version of the
+ * base module shall be forwarded.
+ */
+
+/*!
+ * \relates QQmlEngine
+ * Registers a qmldir-import for module \a uri of major version \a moduleMajor.
+ *
+ * This has the same effect as an \c import statement in a qmldir file: Whenever
+ * \a uri of version \a moduleMajor is imported, \a import of version
+ * \a importMajor. \a importMinor is automatically imported, too. If
+ * \a importMajor is \l QQmlModuleImportLatest the latest version
+ * available of that module is imported, and \a importMinor does not matter. If
+ * \a importMinor is \l QQmlModuleImportLatest the latest minor version of a
+ * \a importMajor is chosen. If \a importMajor is \l QQmlModuleImportAuto the
+ * version of \a import is version of \a uri being imported, and \a importMinor
+ * does not matter. If \a moduleMajor is \l QQmlModuleImportModuleAny the module
+ * import is applied for any major version of \a uri. For example, you may
+ * specify that whenever any version of MyModule is imported, the latest version
+ * of MyOtherModule should be imported. Then, the following call would be
+ * appropriate:
+ *
+ * \code
+ * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
+ * "MyOtherModule", QQmlModuleImportLatest);
+ * \endcode
+ *
+ * Or, you may specify that whenever major version 5 of "MyModule" is imported,
+ * then version 3.14 of "MyOtherModule" should be imported:
+ *
+ * \code
+ * qmlRegisterModuleImport("MyModule", 5, "MyOtherModule", 3, 14);
+ * \endcode
+ *
+ * Finally, if you always want the same version of "MyOtherModule" to be
+ * imported whenever "MyModule" is imported, specify the following:
+ *
+ * \code
+ * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
+ * "MyOtherModule", QQmlModuleImportAuto);
+ * \endcode
+ *
+ * \sa qmlUnregisterModuleImport()
+ */
+void qmlRegisterModuleImport(const char *uri, int moduleMajor,
+ const char *import, int importMajor, int importMinor)
+{
+ QQmlMetaType::registerModuleImport(
+ QString::fromUtf8(uri), resolveModuleVersion(moduleMajor),
+ resolveImport(QString::fromUtf8(import), importMajor, importMinor));
+}
+
+
+/*!
+ * \relates QQmlEngine
+ * Removes a module import previously registered with qmlRegisterModuleImport()
+ *
+ * Calling this function makes sure that \a import of version
+ * \a{importMajor}.\a{importMinor} is not automatically imported anymore when
+ * \a uri of version \a moduleMajor is. The version resolution works the same
+ * way as with \l qmlRegisterModuleImport().
+ *
+ * \sa qmlRegisterModuleImport()
+ */
+void qmlUnregisterModuleImport(const char *uri, int moduleMajor,
+ const char *import, int importMajor, int importMinor)
+{
+ QQmlMetaType::unregisterModuleImport(
+ QString::fromUtf8(uri), resolveModuleVersion(moduleMajor),
+ resolveImport(QString::fromUtf8(import), importMajor, importMinor));
}
//From qqml.h
int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
- return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName);
+ return QQmlMetaType::typeId(uri, QTypeRevision::fromVersion(versionMajor, versionMinor), qmlName);
}
-// From qqmlprivate.h
-QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
+static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance)
{
- if (!m_object) {
+ if (!instance) {
QQmlError error;
- error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine."));
- QQmlEnginePrivate::get(qeng)->warning(qeng, error);
- return nullptr;
+ error.setDescription(QStringLiteral("The registered singleton has already been deleted. "
+ "Ensure that it outlives the engine."));
+ QQmlEnginePrivate::get(engine)->warning(engine, error);
+ return false;
}
- if (qeng->thread() != m_object->thread()) {
+ if (engine->thread() != instance->thread()) {
+ QQmlError error;
+ error.setDescription(QStringLiteral("Registered object must live in the same thread "
+ "as the engine it was registered with"));
+ QQmlEnginePrivate::get(engine)->warning(engine, error);
+ return false;
+ }
+
+ return true;
+}
+
+// From qqmlprivate.h
+#if QT_DEPRECATED_SINCE(6, 3)
+QObject *QQmlPrivate::SingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
+{
+ if (!checkSingletonInstance(qeng, m_object))
+ return nullptr;
+
+ if (alreadyCalled) {
QQmlError error;
- error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with"));
+ error.setDescription(QStringLiteral("Singleton registered by registerSingletonInstance "
+ "must only be accessed from one engine"));
QQmlEnginePrivate::get(qeng)->warning(qeng, error);
return nullptr;
}
- if (alreadyCalled) {
+
+ alreadyCalled = true;
+ QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
+ return m_object;
+};
+#endif
+
+QObject *QQmlPrivate::SingletonInstanceFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
+{
+ if (!checkSingletonInstance(qeng, m_object))
+ return nullptr;
+
+ if (!m_engine) {
+ m_engine = qeng;
+ QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
+ } else if (m_engine != qeng) {
QQmlError error;
error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
QQmlEnginePrivate::get(qeng)->warning(qeng, error);
return nullptr;
}
- alreadyCalled = true;
- qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership);
+
return m_object;
};
-static QVector<int> availableRevisions(const QMetaObject *metaObject)
+static QVector<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
{
- QVector<int> revisions;
+ QVector<QTypeRevision> revisions;
if (!metaObject)
return revisions;
const int propertyOffset = metaObject->propertyOffset();
const int propertyCount = metaObject->propertyCount();
- for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
- propertyIndex < propertyEnd; ++propertyIndex) {
- const QMetaProperty property = metaObject->property(propertyIndex);
+ for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
+ coreIndex < propertyEnd; ++coreIndex) {
+ const QMetaProperty property = metaObject->property(coreIndex);
if (int revision = property.revision())
- revisions.append(revision);
+ revisions.append(QTypeRevision::fromEncodedVersion(revision));
}
const int methodOffset = metaObject->methodOffset();
const int methodCount = metaObject->methodCount();
@@ -122,7 +438,7 @@ static QVector<int> availableRevisions(const QMetaObject *metaObject)
methodIndex < methodEnd; ++methodIndex) {
const QMetaMethod method = metaObject->method(methodIndex);
if (int revision = method.revision())
- revisions.append(revision);
+ revisions.append(QTypeRevision::fromEncodedVersion(revision));
}
// Need to also check parent meta objects, as their revisions are inherited.
@@ -132,13 +448,328 @@ static QVector<int> availableRevisions(const QMetaObject *metaObject)
return revisions;
}
+template<typename Registration>
+void assignVersions(Registration *registration, QTypeRevision revision,
+ QTypeRevision defaultVersion)
+{
+ const quint8 majorVersion = revision.hasMajorVersion() ? revision.majorVersion()
+ : defaultVersion.majorVersion();
+ registration->version = revision.hasMinorVersion()
+ ? QTypeRevision::fromVersion(majorVersion, revision.minorVersion())
+ : QTypeRevision::fromMajorVersion(majorVersion);
+ registration->revision = revision;
+}
+
+static QVector<QTypeRevision> prepareRevisions(const QMetaObject *metaObject, QTypeRevision added)
+{
+ auto revisions = availableRevisions(metaObject);
+ revisions.append(added);
+ return revisions;
+}
+
+static void uniqueRevisions(QVector<QTypeRevision> *revisions, QTypeRevision defaultVersion,
+ QTypeRevision added)
+{
+ bool revisionsHaveMajorVersions = false;
+ for (QTypeRevision revision : QVector<QTypeRevision>(*revisions)) { // yes, copy
+ // allow any minor version for each explicitly specified past major one
+ if (revision.hasMajorVersion()) {
+ revisionsHaveMajorVersions = true;
+ if (revision.majorVersion() < defaultVersion.majorVersion())
+ revisions->append(QTypeRevision::fromVersion(revision.majorVersion(), 254));
+ }
+ }
+
+ if (revisionsHaveMajorVersions) {
+ if (!added.hasMajorVersion()) {
+ // If added in unspecified major version, assume default one.
+ revisions->append(QTypeRevision::fromVersion(defaultVersion.majorVersion(),
+ added.minorVersion()));
+ } else if (added.majorVersion() < defaultVersion.majorVersion()) {
+ // If added in past major version, add .0 of default version.
+ revisions->append(QTypeRevision::fromVersion(defaultVersion.majorVersion(), 0));
+ }
+ }
+
+ std::sort(revisions->begin(), revisions->end());
+ const auto it = std::unique(revisions->begin(), revisions->end());
+ revisions->erase(it, revisions->end());
+}
+
+static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
+ const QQmlPrivate::RegisterSingletonType &type)
+{
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->scriptCallback = type.scriptApi;
+ siinfo->qobjectCallback = type.qObjectApi;
+ siinfo->typeName = type.typeName;
+ return QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+}
+
+static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
+ const QQmlPrivate::RegisterCompositeSingletonType &type)
+{
+ QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
+ siinfo->url = QQmlTypeLoader::normalize(type.url);
+ siinfo->typeName = type.typeName;
+ return QQmlType::SingletonInstanceInfo::ConstPtr(
+ siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
+}
+
+static int finalizeType(const QQmlType &dtype)
+{
+ if (!dtype.isValid())
+ return -1;
+
+ QQmlMetaType::registerUndeletableType(dtype);
+ return dtype.index();
+}
+
+using ElementNames = QVarLengthArray<const char *, 8>;
+static ElementNames classElementNames(const QMetaObject *metaObject)
+{
+ Q_ASSERT(metaObject);
+ const char *key = "QML.Element";
+
+ const int offset = metaObject->classInfoOffset();
+ const int start = metaObject->classInfoCount() + offset - 1;
+
+ ElementNames elementNames;
+
+ for (int i = start; i >= offset; --i) {
+ const QMetaClassInfo classInfo = metaObject->classInfo(i);
+ if (qstrcmp(key, classInfo.name()) == 0) {
+ const char *elementName = classInfo.value();
+
+ if (qstrcmp(elementName, "auto") == 0) {
+ const char *strippedClassName = metaObject->className();
+ for (const char *c = strippedClassName; *c != '\0'; c++) {
+ if (*c == ':')
+ strippedClassName = c + 1;
+ }
+ elementName = strippedClassName;
+ } else if (qstrcmp(elementName, "anonymous") == 0) {
+ if (elementNames.isEmpty())
+ elementNames.push_back(nullptr);
+ else if (elementNames[0] != nullptr)
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ continue;
+ }
+
+ if (!elementNames.isEmpty() && elementNames[0] == nullptr) {
+ qWarning() << metaObject->className() << "is both anonymous and named";
+ elementNames[0] = elementName;
+ } else {
+ elementNames.push_back(elementName);
+ }
+ }
+ }
+
+ return elementNames;
+}
+
+struct AliasRegistrar
+{
+ AliasRegistrar(const ElementNames *elementNames) : elementNames(elementNames) {}
+
+ void registerAliases(int typeId)
+ {
+ if (elementNames) {
+ for (int i = 1, end = elementNames->length(); i < end; ++i)
+ otherNames.append(QString::fromUtf8(elementNames->at(i)));
+ elementNames = nullptr;
+ }
+
+ for (const QString &otherName : std::as_const(otherNames))
+ QQmlMetaType::registerTypeAlias(typeId, otherName);
+ }
+
+private:
+ const ElementNames *elementNames;
+ QVarLengthArray<QString, 8> otherNames;
+};
+
+
+static void doRegisterTypeAndRevisions(
+ const QQmlPrivate::RegisterTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
+ const bool creatable = (elementNames[0] != nullptr || isValueType)
+ && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
+
+ QString noCreateReason;
+ ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
+
+ if (!creatable) {
+ noCreateReason = QString::fromUtf8(
+ classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
+ if (noCreateReason.isEmpty())
+ noCreateReason = QLatin1String("Type cannot be created in QML.");
+ } else if (isValueType) {
+ const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
+ if (qstrcmp(method, "structured") == 0)
+ creationMethod = ValueTypeCreationMethod::Structured;
+ else if (qstrcmp(method, "construct") == 0)
+ creationMethod = ValueTypeCreationMethod::Construct;
+ }
+
+ RegisterType typeRevision = {
+ QQmlPrivate::RegisterType::CurrentVersion,
+ type.typeId,
+ type.listId,
+ creatable ? type.objectSize : 0,
+ nullptr,
+ nullptr,
+ noCreateReason,
+ type.createValueType,
+ type.uri,
+ type.version,
+ nullptr,
+ type.metaObject,
+ type.attachedPropertiesFunction,
+ type.attachedPropertiesMetaObject,
+ type.parserStatusCast,
+ type.valueSourceCast,
+ type.valueInterceptorCast,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ nullptr,
+ QTypeRevision(),
+ type.structVersion > 0 ? type.finalizerCast : -1,
+ creationMethod
+ };
+
+ QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
+ 0,
+ type.uri,
+ type.version,
+ nullptr,
+ type.listId,
+ type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
+ QTypeRevision(),
+ };
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
+ if (type.attachedPropertiesMetaObject)
+ revisions += availableRevisions(type.attachedPropertiesMetaObject);
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : revisions) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&typeRevision, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (typeRevision.version < added
+ || (removed.isValid() && !(typeRevision.version < removed))) {
+ typeRevision.elementName = nullptr;
+ typeRevision.create = nullptr;
+ typeRevision.userdata = nullptr;
+ } else {
+ typeRevision.elementName = elementNames[0];
+ typeRevision.create = creatable ? type.create : nullptr;
+ typeRevision.userdata = type.userdata;
+ }
+
+ typeRevision.customParser = type.customParserFactory();
+ const int id = qmlregister(TypeRegistration, &typeRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (typeRevision.elementName)
+ aliasRegistrar.registerAliases(id);
+
+ if (sequenceRevision.metaSequence != QMetaSequence()) {
+ sequenceRevision.version = typeRevision.version;
+ sequenceRevision.revision = typeRevision.revision;
+ const int id = QQmlPrivate::qmlregister(
+ QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+ }
+ }
+}
+
+static void doRegisterSingletonAndRevisions(
+ const QQmlPrivate::RegisterSingletonTypeAndRevisions &type,
+ const ElementNames &elementNames)
+{
+ using namespace QQmlPrivate;
+
+ RegisterSingletonType revisionRegistration = {
+ 0,
+ type.uri,
+ type.version,
+ elementNames[0],
+ nullptr,
+ type.qObjectApi,
+ type.instanceMetaObject,
+ type.typeId,
+ type.extensionObjectCreate,
+ type.extensionMetaObject,
+ QTypeRevision()
+ };
+ const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
+ = singletonInstanceInfo(revisionRegistration);
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromVersion(type.version.majorVersion(), 0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+ const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
+ "QML.ExtraVersion");
+
+ auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
+ uniqueRevisions(&revisions, type.version, added);
+
+ AliasRegistrar aliasRegistrar(&elementNames);
+ for (QTypeRevision revision : std::as_const(revisions)) {
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ assignVersions(&revisionRegistration, revision, type.version);
+
+ // When removed or before added, we still add revisions, but anonymous ones
+ if (revisionRegistration.version < added
+ || (removed.isValid() && !(revisionRegistration.version < removed))) {
+ revisionRegistration.typeName = nullptr;
+ revisionRegistration.qObjectApi = nullptr;
+ } else {
+ revisionRegistration.typeName = elementNames[0];
+ revisionRegistration.qObjectApi = type.qObjectApi;
+ }
+
+ const int id = finalizeType(
+ QQmlMetaType::registerSingletonType(revisionRegistration, siinfo));
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+
+ if (revisionRegistration.typeName)
+ aliasRegistrar.registerAliases(id);
+ }
+}
+
/*
This method is "over generalized" to allow us to (potentially) register more types of things in
the future without adding exported symbols.
*/
int QQmlPrivate::qmlregister(RegistrationType type, void *data)
{
- QQmlType dtype;
switch (type) {
case AutoParentRegistration:
return QQmlMetaType::registerAutoParentFunction(
@@ -148,149 +779,91 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
*reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
case TypeAndRevisionsRegistration: {
const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
- const char *elementName = classElementName(type.classInfoMetaObject);
- const bool creatable = (elementName != nullptr)
- && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
-
- const QString noCreateReason = creatable
- ? QString()
- : QString::fromUtf8(classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
- RegisterType revisionRegistration = {
- 1,
- type.typeId,
- type.listId,
- creatable ? type.objectSize : 0,
- nullptr,
- noCreateReason,
- type.uri,
- type.versionMajor,
- -1,
- nullptr,
- type.metaObject,
- type.attachedPropertiesFunction,
- type.attachedPropertiesMetaObject,
- type.parserStatusCast,
- type.valueSourceCast,
- type.valueInterceptorCast,
- type.extensionObjectCreate,
- type.extensionMetaObject,
- nullptr,
- -1
- };
-
- const int added = intClassInfo(type.classInfoMetaObject, "QML.AddedInMinorVersion", 0);
- const int removed = intClassInfo(type.classInfoMetaObject, "QML.RemovedInMinorVersion", -1);
-
- auto revisions = availableRevisions(type.metaObject);
- revisions.append(qMax(added, 0));
- if (type.attachedPropertiesMetaObject)
- revisions += availableRevisions(type.attachedPropertiesMetaObject);
-
- std::sort(revisions.begin(), revisions.end());
- const auto it = std::unique(revisions.begin(), revisions.end());
- revisions.erase(it, revisions.end());
-
- const bool typeWasRemoved = removed >= added;
- for (int revision : revisions) {
- if (revision < added)
- continue;
-
- // When removed, we still add revisions, but anonymous ones
- if (typeWasRemoved && revision >= removed) {
- revisionRegistration.elementName = nullptr;
- revisionRegistration.create = nullptr;
+ if (type.structVersion > 1 && type.forceAnonymous) {
+ doRegisterTypeAndRevisions(type, {nullptr});
+ } else {
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
} else {
- revisionRegistration.elementName = elementName;
- revisionRegistration.create = creatable ? type.create : nullptr;
+ doRegisterTypeAndRevisions(type, names);
}
- // Equivalent of qmlRegisterRevision<T, revision>(...)
- revisionRegistration.versionMinor = revision;
- revisionRegistration.revision = revision;
- revisionRegistration.customParser = type.customParserFactory();
-
- qmlregister(TypeRegistration, &revisionRegistration);
}
break;
}
case SingletonAndRevisionsRegistration: {
const RegisterSingletonTypeAndRevisions &type
= *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
- const char *elementName = classElementName(type.classInfoMetaObject);
- RegisterSingletonType revisionRegistration = {
- QmlCurrentSingletonTypeRegistrationVersion,
+ const ElementNames names = classElementNames(type.classInfoMetaObject);
+ if (names.isEmpty()) {
+ qWarning().nospace() << "Missing QML.Element class info for "
+ << type.classInfoMetaObject->className();
+ } else {
+ doRegisterSingletonAndRevisions(type, names);
+ }
+ break;
+ }
+ case SequentialContainerAndRevisionsRegistration: {
+ const RegisterSequentialContainerAndRevisions &type
+ = *reinterpret_cast<RegisterSequentialContainerAndRevisions *>(data);
+ RegisterSequentialContainer revisionRegistration = {
+ 0,
type.uri,
- type.versionMajor,
- -1,
- elementName,
-
- type.scriptApi,
+ type.version,
nullptr,
- type.instanceMetaObject,
type.typeId,
- -1,
-
- type.generalizedQobjectApi
+ type.metaSequence,
+ QTypeRevision()
};
- const int added = intClassInfo(type.classInfoMetaObject, "QML.AddedInMinorVersion", 0);
- const int removed = intClassInfo(type.classInfoMetaObject, "QML.RemovedInMinorVersion", -1);
-
- auto revisions = availableRevisions(type.instanceMetaObject);
- revisions.append(qMax(added, 0));
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromMinorVersion(0));
+ QList<QTypeRevision> revisions = revisionClassInfos(
+ type.classInfoMetaObject, "QML.ExtraVersion");
+ revisions.append(added);
+ uniqueRevisions(&revisions, type.version, added);
- std::sort(revisions.begin(), revisions.end());
- const auto it = std::unique(revisions.begin(), revisions.end());
- revisions.erase(it, revisions.end());
-
- const bool typeWasRemoved = removed >= added;
- for (int revision : qAsConst(revisions)) {
+ for (QTypeRevision revision : std::as_const(revisions)) {
if (revision < added)
continue;
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
- // When removed, we still add revisions, but anonymous ones
- if (typeWasRemoved && revision >= removed) {
- revisionRegistration.typeName = nullptr;
- revisionRegistration.scriptApi = nullptr;
- revisionRegistration.generalizedQobjectApi = nullptr;
- } else {
- revisionRegistration.typeName = elementName;
- revisionRegistration.scriptApi = type.scriptApi;
- revisionRegistration.generalizedQobjectApi = type.generalizedQobjectApi;
- }
-
- // Equivalent of qmlRegisterRevision<T, revision>(...)
- revisionRegistration.versionMinor = revision;
- revisionRegistration.revision = revision;
-
- qmlregister(SingletonRegistration, &revisionRegistration);
+ assignVersions(&revisionRegistration, revision, type.version);
+ const int id = qmlregister(SequentialContainerRegistration, &revisionRegistration);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
}
break;
}
case TypeRegistration:
- dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data));
- break;
+ return finalizeType(
+ QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data)));
case InterfaceRegistration:
- dtype = QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data));
- break;
+ return finalizeType(
+ QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data)));
case SingletonRegistration:
- dtype = QQmlMetaType::registerSingletonType(*reinterpret_cast<RegisterSingletonType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerSingletonType(
+ *reinterpret_cast<RegisterSingletonType *>(data),
+ singletonInstanceInfo(*reinterpret_cast<RegisterSingletonType *>(data))));
case CompositeRegistration:
- dtype = QQmlMetaType::registerCompositeType(*reinterpret_cast<RegisterCompositeType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerCompositeType(
+ *reinterpret_cast<RegisterCompositeType *>(data)));
case CompositeSingletonRegistration:
- dtype = QQmlMetaType::registerCompositeSingletonType(*reinterpret_cast<RegisterCompositeSingletonType *>(data));
- break;
+ return finalizeType(QQmlMetaType::registerCompositeSingletonType(
+ *reinterpret_cast<RegisterCompositeSingletonType *>(data),
+ singletonInstanceInfo(*reinterpret_cast<RegisterCompositeSingletonType *>(data))));
+ case SequentialContainerRegistration:
+ return finalizeType(QQmlMetaType::registerSequentialContainer(
+ *reinterpret_cast<RegisterSequentialContainer *>(data)));
default:
return -1;
}
- if (!dtype.isValid())
- return -1;
-
- QQmlMetaType::registerUndeletableType(dtype);
- return dtype.index();
+ return -1;
}
void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
@@ -303,6 +876,9 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
QQmlMetaType::removeCachedUnitLookupFunction(
reinterpret_cast<QmlUnitCacheLookupFunction>(data));
break;
+ case SequentialContainerRegistration:
+ QQmlMetaType::unregisterSequentialContainer(data);
+ break;
case TypeRegistration:
case InterfaceRegistration:
case SingletonRegistration:
@@ -312,6 +888,7 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
break;
case TypeAndRevisionsRegistration:
case SingletonAndRevisionsRegistration:
+ case SequentialContainerAndRevisionsRegistration:
// Currently unnecessary. We'd need a special data structure to hold
// URI + majorVersion and then we'd iterate the minor versions, look up the
// associated QQmlType objects by uri/elementName/major/minor and qmlunregister
@@ -321,4 +898,1520 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
}
}
+QList<QTypeRevision> QQmlPrivate::revisionClassInfos(const QMetaObject *metaObject,
+ const char *key)
+{
+ QList<QTypeRevision> revisions;
+ for (int index = indexOfOwnClassInfo(metaObject, key); index != -1;
+ index = indexOfOwnClassInfo(metaObject, key, index - 1)) {
+ revisions.push_back(QTypeRevision::fromEncodedVersion(
+ QLatin1StringView(metaObject->classInfo(index).value()).toInt()));
+ }
+ return revisions;
+}
+
+int qmlRegisterTypeNotAvailable(
+ const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, const QString &message)
+{
+ return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(
+ uri, versionMajor, versionMinor, qmlName, message);
+}
+
+namespace QQmlPrivate {
+template<>
+void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
+ const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
+ QVector<int> *qmlTypeIds, const QMetaObject *extension, bool)
+{
+ using T = QQmlTypeNotAvailable;
+
+ RegisterTypeAndRevisions type = {
+ 3,
+ QmlMetaType<T>::self(),
+ QmlMetaType<T>::list(),
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+
+ uri,
+ QTypeRevision::fromMajorVersion(versionMajor),
+
+ &QQmlTypeNotAvailable::staticMetaObject,
+ classInfoMetaObject,
+
+ attachedPropertiesFunc<T>(),
+ attachedPropertiesMetaObject<T>(),
+
+ StaticCastSelector<T, QQmlParserStatus>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
+ StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
+
+ nullptr,
+ extension,
+ qmlCreateCustomParser<T>,
+ qmlTypeIds,
+ QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
+ false,
+ QmlMetaType<T>::sequence(),
+ };
+
+ qmlregister(TypeAndRevisionsRegistration, &type);
+}
+
+QObject *AOTCompiledContext::thisObject() const
+{
+ return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
+ ->thisObject();
+}
+
+QQmlEngine *AOTCompiledContext::qmlEngine() const
+{
+ return engine->handle()->qmlEngine();
+}
+
+static QQmlPropertyCapture *propertyCapture(const AOTCompiledContext *aotContext)
+{
+ QQmlEngine *engine = aotContext->qmlEngine();
+ return engine ? QQmlEnginePrivate::get(aotContext->qmlEngine())->propertyCapture : nullptr;
+}
+
+QJSValue AOTCompiledContext::jsMetaType(int index) const
+{
+ return QJSValuePrivate::fromReturnedValue(
+ compilationUnit->runtimeClasses[index]->asReturnedValue());
+}
+
+void AOTCompiledContext::setInstructionPointer(int offset) const
+{
+ if (auto *frame = engine->handle()->currentStackFrame)
+ frame->instructionPointer = offset;
+}
+
+void AOTCompiledContext::setReturnValueUndefined() const
+{
+ if (auto *frame = engine->handle()->currentStackFrame) {
+ Q_ASSERT(frame->isMetaTypesFrame());
+ static_cast<QV4::MetaTypesStackFrame *>(frame)->setReturnValueUndefined();
+ }
+}
+
+static void captureFallbackProperty(
+ QObject *object, int coreIndex, int notifyIndex, bool isConstant,
+ const AOTCompiledContext *aotContext)
+{
+ if (isConstant)
+ return;
+
+ if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
+ capture->captureProperty(object, coreIndex, notifyIndex);
+}
+
+static void captureObjectProperty(
+ QObject *object, const QQmlPropertyCache *propertyCache,
+ const QQmlPropertyData *property, const AOTCompiledContext *aotContext)
+{
+ if (property->isConstant())
+ return;
+
+ if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
+ capture->captureProperty(object, propertyCache, property);
+}
+
+static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCache *ancestor)
+{
+ for (const QQmlPropertyCache *cache = descendent; cache; cache = cache->parent().data()) {
+ if (cache == ancestor)
+ return true;
+ }
+ return false;
+}
+
+enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
+
+struct ObjectPropertyQmlData
+{
+ QQmlData *qmlData;
+ ObjectPropertyResult result;
+};
+
+template<bool StrictType>
+ObjectPropertyQmlData findObjectPropertyQmlData(QV4::Lookup *l, QObject *object)
+{
+ QQmlData *qmlData = QQmlData::get(object);
+ if (!qmlData)
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ if (qmlData->isQueuedForDeletion)
+ return {qmlData, ObjectPropertyResult::Deleted};
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+ const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
+ if (StrictType) {
+ if (qmlData->propertyCache.data() != propertyCache)
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ } else if (!inherits(qmlData->propertyCache.data(), propertyCache)) {
+ return {qmlData, ObjectPropertyResult::NeedsInit};
+ }
+ return {qmlData, ObjectPropertyResult::OK};
+}
+
+template<bool StrictType = false>
+ObjectPropertyResult loadObjectProperty(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ const QQmlPropertyData *propertyData = l->qobjectLookup.propertyData;
+ const int coreIndex = propertyData->coreIndex();
+ if (data.qmlData->hasPendingBindingBit(coreIndex))
+ data.qmlData->flushPendingBinding(coreIndex);
+
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, propertyData, aotContext);
+ propertyData->readProperty(object, target);
+ return ObjectPropertyResult::OK;
+}
+
+template<bool StrictType = false>
+ObjectPropertyResult writeBackObjectProperty(QV4::Lookup *l, QObject *object, void *source)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ l->qobjectLookup.propertyData->writeProperty(object, source, {});
+ return ObjectPropertyResult::OK;
+}
+
+struct FallbackPropertyQmlData
+{
+ QQmlData *qmlData;
+ const QMetaObject *metaObject;
+ ObjectPropertyResult result;
+};
+
+static FallbackPropertyQmlData findFallbackPropertyQmlData(QV4::Lookup *l, QObject *object)
+{
+ QQmlData *qmlData = QQmlData::get(object);
+ if (qmlData && qmlData->isQueuedForDeletion)
+ return {qmlData, nullptr, ObjectPropertyResult::Deleted};
+
+ Q_ASSERT(!QQmlData::wasDeleted(object));
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ if (!metaObject || metaObject != object->metaObject())
+ return {qmlData, nullptr, ObjectPropertyResult::NeedsInit};
+
+ return {qmlData, metaObject, ObjectPropertyResult::OK};
+}
+
+static ObjectPropertyResult loadFallbackProperty(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ if (data.qmlData && data.qmlData->hasPendingBindingBit(coreIndex))
+ data.qmlData->flushPendingBinding(coreIndex);
+
+ captureFallbackProperty(object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, aotContext);
+
+ void *a[] = { target, nullptr };
+ data.metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
+static ObjectPropertyResult writeBackFallbackProperty(QV4::Lookup *l, QObject *object, void *source)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ void *a[] = { source, nullptr };
+ data.metaObject->metacall(
+ object, QMetaObject::WriteProperty, l->qobjectFallbackLookup.coreIndex, a);
+
+ return ObjectPropertyResult::OK;
+}
+
+ObjectPropertyResult loadObjectAsVariant(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ QVariant *variant = static_cast<QVariant *>(target);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return loadObjectProperty<true>(l, object, variant, aotContext);
+
+ *variant = QVariant(propType);
+ return loadObjectProperty<true>(l, object, variant->data(), aotContext);
+}
+
+ObjectPropertyResult writeBackObjectAsVariant(QV4::Lookup *l, QObject *object, void *source)
+{
+ QVariant *variant = static_cast<QVariant *>(source);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return writeBackObjectProperty<true>(l, object, variant);
+
+ Q_ASSERT(variant->metaType() == propType);
+ return writeBackObjectProperty<true>(l, object, variant->data());
+}
+
+ObjectPropertyResult loadFallbackAsVariant(
+ QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
+{
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ QVariant *variant = static_cast<QVariant *>(target);
+ const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return loadFallbackProperty(l, object, variant, aotContext);
+
+ *variant = QVariant(propType);
+ return loadFallbackProperty(l, object, variant->data(), aotContext);
+}
+
+ObjectPropertyResult writeBackFallbackAsVariant(QV4::Lookup *l, QObject *object, void *source)
+{
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ QVariant *variant = static_cast<QVariant *>(source);
+ const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return writeBackFallbackProperty(l, object, variant);
+
+ Q_ASSERT(variant->metaType() == propType);
+ return writeBackFallbackProperty(l, object, variant->data());
+}
+
+template<bool StrictType, typename Op>
+static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op)
+{
+ const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
+ op(property);
+ return ObjectPropertyResult::OK;
+}
+
+template<bool StrictType = false>
+static ObjectPropertyResult resetObjectProperty(
+ QV4::Lookup *l, QObject *object, QV4::ExecutionEngine *v4)
+{
+ return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
+ if (property->isResettable()) {
+ property->resetProperty(object, {});
+ } else {
+ v4->throwError(
+ QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(property->propType().name()));
+ }
+ });
+}
+
+template<bool StrictType = false>
+static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
+{
+ return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
+ property->writeProperty(object, value, {});
+ });
+}
+
+template<typename Op>
+static ObjectPropertyResult changeFallbackProperty(QV4::Lookup *l, QObject *object, Op op)
+{
+ const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
+ if (data.result != ObjectPropertyResult::OK)
+ return data.result;
+
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(coreIndex));
+
+ op(data.metaObject, coreIndex);
+ return ObjectPropertyResult::OK;
+}
+
+static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value)
+{
+ return changeFallbackProperty(l, object, [&](const QMetaObject *metaObject, int coreIndex) {
+ void *args[] = { value, nullptr };
+ metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args);
+ });
+}
+
+static ObjectPropertyResult resetFallbackProperty(
+ QV4::Lookup *l, QObject *object, const QMetaProperty *property, QV4::ExecutionEngine *v4)
+{
+ return changeFallbackProperty(l, object, [&](const QMetaObject *metaObject, int coreIndex) {
+ if (property->isResettable()) {
+ void *args[] = { nullptr };
+ metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
+ } else {
+ v4->throwError(
+ QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(property->typeName()));
+ }
+ });
+}
+
+static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
+{
+ if (!lookupType.isValid()) {
+ // If type is invalid, then the calling code depends on the lookup
+ // to be set up in order to query the type, via lookupResultMetaType.
+ // We cannot verify the type in this case.
+ } else if ((lookupType.flags() & QMetaType::IsQmlList)
+ && (propertyType.flags() & QMetaType::IsQmlList)) {
+ // We want to check the value types here, but we cannot easily do it.
+ // Internally those are all QObject* lists, though.
+ } else if (lookupType.flags() & QMetaType::PointerToQObject) {
+ // We accept any base class as type, too
+
+ const QMetaObject *typeMetaObject = lookupType.metaObject();
+ const QMetaObject *foundMetaObject = propertyType.metaObject();
+ if (!foundMetaObject)
+ foundMetaObject = QQmlMetaType::metaObjectForType(propertyType).metaObject();
+
+ while (foundMetaObject && foundMetaObject != typeMetaObject)
+ foundMetaObject = foundMetaObject->superClass();
+
+ if (!foundMetaObject)
+ return false;
+ } else if (propertyType.flags() & QMetaType::IsEnumeration) {
+ if (propertyType == lookupType)
+ return true;
+
+ // You can pass the underlying type of an enum.
+ // We don't want to check for the actual underlying type because
+ // moc and qmltyperegistrar are not very precise about it. Especially
+ // the long and longlong types can be ambiguous.
+
+ const bool isUnsigned = propertyType.flags() & QMetaType::IsUnsignedEnumeration;
+ switch (propertyType.sizeOf()) {
+ case 1:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<quint8>()
+ : lookupType == QMetaType::fromType<qint8>();
+ case 2:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<ushort>()
+ : lookupType == QMetaType::fromType<short>();
+ case 4:
+ // The default type, if moc doesn't know the actual enum type, is int.
+ // However, the compiler can still decide to encode the enum in uint.
+ // Therefore, we also accept int for uint enums.
+ // TODO: This is technically UB.
+ return isUnsigned
+ ? (lookupType == QMetaType::fromType<int>()
+ || lookupType == QMetaType::fromType<uint>())
+ : lookupType == QMetaType::fromType<int>();
+ case 8:
+ return isUnsigned
+ ? lookupType == QMetaType::fromType<qulonglong>()
+ : lookupType == QMetaType::fromType<qlonglong>();
+ }
+
+ return false;
+ } else if (propertyType != lookupType) {
+ return false;
+ }
+ return true;
+}
+
+static ObjectPropertyResult storeObjectAsVariant(
+ QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
+{
+ QVariant *variant = static_cast<QVariant *>(value);
+ const QMetaType propType = l->qobjectLookup.propertyData->propType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return storeObjectProperty<true>(l, object, variant);
+
+ if (!variant->isValid())
+ return resetObjectProperty<true>(l, object, v4);
+
+ if (isTypeCompatible(variant->metaType(), propType))
+ return storeObjectProperty<true>(l, object, variant->data());
+
+ QVariant converted(propType);
+ v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
+ return storeObjectProperty<true>(l, object, converted.data());
+}
+
+static ObjectPropertyResult storeFallbackAsVariant(
+ QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
+{
+ QVariant *variant = static_cast<QVariant *>(value);
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ const QMetaProperty property = metaObject->property(l->qobjectFallbackLookup.coreIndex);
+ const QMetaType propType = property.metaType();
+ if (propType == QMetaType::fromType<QVariant>())
+ return storeFallbackProperty(l, object, variant);
+
+ if (!variant->isValid())
+ return resetFallbackProperty(l, object, &property, v4);
+
+ if (isTypeCompatible(variant->metaType(), propType))
+ return storeFallbackProperty(l, object, variant->data());
+
+ QVariant converted(propType);
+ v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
+ return storeFallbackProperty(l, object, converted.data());
+}
+
+enum class ObjectLookupResult {
+ Failure,
+ Object,
+ Fallback,
+ ObjectAsVariant,
+ FallbackAsVariant,
+};
+
+static ObjectLookupResult initObjectLookup(
+ const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type)
+{
+ QV4::Scope scope(aotContext->engine->handle());
+ QV4::PropertyKey id = scope.engine->identifierTable->asPropertyKey(
+ aotContext->compilationUnit->runtimeStrings[l->nameIndex]);
+
+ Q_ASSERT(id.isString());
+
+ QV4::ScopedString name(scope, id.asStringOrSymbol());
+
+ Q_ASSERT(!name->equals(scope.engine->id_toString()));
+ Q_ASSERT(!name->equals(scope.engine->id_destroy()));
+
+ QQmlData *ddata = QQmlData::get(object, true);
+ Q_ASSERT(ddata);
+ if (ddata->isQueuedForDeletion)
+ return ObjectLookupResult::Failure;
+
+ const QQmlPropertyData *property;
+ if (!ddata->propertyCache) {
+ property = QQmlPropertyCache::property(object, name, aotContext->qmlContext, nullptr);
+ } else {
+ property = ddata->propertyCache->property(
+ name.getPointer(), object, aotContext->qmlContext);
+ }
+
+ const bool doVariantLookup = type == QMetaType::fromType<QVariant>();
+ if (!property) {
+ const QMetaObject *metaObject = object->metaObject();
+ if (!metaObject)
+ return ObjectLookupResult::Failure;
+
+ const int coreIndex = metaObject->indexOfProperty(
+ name->toQStringNoThrow().toUtf8().constData());
+ if (coreIndex < 0)
+ return ObjectLookupResult::Failure;
+
+ const QMetaProperty property = metaObject->property(coreIndex);
+ if (!doVariantLookup && !isTypeCompatible(type, property.metaType()))
+ return ObjectLookupResult::Failure;
+
+ l->releasePropertyCache();
+ // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
+ l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
+ l->qobjectFallbackLookup.coreIndex = coreIndex;
+ l->qobjectFallbackLookup.notifyIndex =
+ QMetaObjectPrivate::signalIndex(property.notifySignal());
+ l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
+ return doVariantLookup
+ ? ObjectLookupResult::FallbackAsVariant
+ : ObjectLookupResult::Fallback;
+ }
+
+ if (!doVariantLookup && !isTypeCompatible(type, property->propType()))
+ return ObjectLookupResult::Failure;
+
+ Q_ASSERT(ddata->propertyCache);
+
+ QV4::setupQObjectLookup(l, ddata, property);
+
+ return doVariantLookup
+ ? ObjectLookupResult::ObjectAsVariant
+ : ObjectLookupResult::Object;
+}
+
+static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
+ const QMetaObject *metaObject, QMetaType type)
+{
+ Q_ASSERT(metaObject);
+ const QByteArray name = compilationUnit->runtimeStrings[l->nameIndex]->toQString().toUtf8();
+ const int coreIndex = metaObject->indexOfProperty(name.constData());
+ QMetaType lookupType = metaObject->property(coreIndex).metaType();
+ if (!isTypeCompatible(type, lookupType))
+ return false;
+ l->qgadgetLookup.metaObject = quintptr(metaObject) + 1;
+ l->qgadgetLookup.coreIndex = coreIndex;
+ l->qgadgetLookup.metaType = lookupType.iface();
+ return true;
+}
+
+static void amendException(QV4::ExecutionEngine *engine)
+{
+ const int missingLineNumber = engine->currentStackFrame->missingLineNumber();
+ const int lineNumber = engine->currentStackFrame->lineNumber();
+ Q_ASSERT(missingLineNumber != lineNumber);
+
+ auto amendStackTrace = [&](QV4::StackTrace *stackTrace) {
+ for (auto it = stackTrace->begin(), end = stackTrace->end(); it != end; ++it) {
+ if (it->line == missingLineNumber) {
+ it->line = lineNumber;
+ break;
+ }
+ }
+ };
+
+ amendStackTrace(&engine->exceptionStackTrace);
+
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::ErrorObject> error(scope, *engine->exceptionValue);
+ if (error) // else some other value was thrown
+ amendStackTrace(error->d()->stackTrace);
+}
+
+
+bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
+{
+ if (!object)
+ return false;
+
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
+ || l->getter == QV4::Lookup::getterQObject
+ || l->getter == QV4::Lookup::getterQObjectAsVariant) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(object, property->coreIndex());
+ captureObjectProperty(object, l->qobjectLookup.propertyCache, property, this);
+ return true;
+ }
+
+ if (l->getter == QV4::Lookup::getterFallback
+ || l->getter == QV4::Lookup::getterFallbackAsVariant) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(object, coreIndex);
+ captureFallbackProperty(
+ object, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, this);
+ return true;
+ }
+
+ return false;
+}
+
+bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
+ && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
+ const QQmlPropertyData *property = l->qobjectLookup.propertyData;
+ QQmlData::flushPendingBinding(qmlScopeObject, property->coreIndex());
+ captureObjectProperty(qmlScopeObject, l->qobjectLookup.propertyCache, property, this);
+ return true;
+ }
+
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ QQmlData::flushPendingBinding(qmlScopeObject, coreIndex);
+ captureFallbackProperty(qmlScopeObject, coreIndex, l->qobjectFallbackLookup.notifyIndex,
+ l->qobjectFallbackLookup.isConstant, this);
+ return true;
+ }
+
+ return false;
+}
+
+void AOTCompiledContext::captureTranslation() const
+{
+ if (QQmlPropertyCapture *capture = propertyCapture(this))
+ capture->captureTranslation();
+}
+
+QString AOTCompiledContext::translationContext() const
+{
+#if QT_CONFIG(translation)
+ return QV4::GlobalExtensions::currentTranslationContext(engine->handle());
+#else
+ return QString();
+#endif
+}
+
+QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
+ || l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
+ || l->getter == QV4::Lookup::getterQObject
+ || l->setter == QV4::Lookup::setterQObject
+ || l->getter == QV4::Lookup::getterQObjectAsVariant
+ || l->setter == QV4::Lookup::setterQObjectAsVariant) {
+ return l->qobjectLookup.propertyData->propType();
+ } else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
+ return QMetaType(l->qgadgetLookup.metaType);
+ } else if (l->getter == QV4::QQmlTypeWrapper::lookupEnumValue) {
+ return QMetaType::fromType<int>();
+ } else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
+ || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
+ || l->getter == QV4::QObjectWrapper::lookupAttached) {
+ return QMetaType::fromType<QObject *>();
+ } else if (l->getter == QV4::Lookup::getterFallback
+ || l->setter == QV4::Lookup::setterFallback
+ || l->getter == QV4::Lookup::getterFallbackAsVariant
+ || l->setter == QV4::Lookup::setterFallbackAsVariant
+ || l->qmlContextPropertyGetter
+ == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
+ const int coreIndex = l->qobjectFallbackLookup.coreIndex;
+ return metaObject->property(coreIndex).metaType();
+ }
+ return QMetaType();
+}
+
+static bool isUndefined(const void *value, QMetaType type)
+{
+ if (type == QMetaType::fromType<QVariant>())
+ return !static_cast<const QVariant *>(value)->isValid();
+ if (type == QMetaType::fromType<QJSValue>())
+ return static_cast<const QJSValue *>(value)->isUndefined();
+ if (type == QMetaType::fromType<QJSPrimitiveValue>()) {
+ return static_cast<const QJSPrimitiveValue *>(value)->type()
+ == QJSPrimitiveValue::Undefined;
+ }
+ return false;
+}
+
+void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType type) const
+{
+ // We don't really use any part of the lookup machinery here.
+ // The QV4::Lookup is created on the stack to conveniently get the property cache, and through
+ // the property cache we store a value into the property.
+
+ QV4::Lookup l;
+ memset(&l, 0, sizeof(QV4::Lookup));
+ l.nameIndex = nameIndex;
+ l.forCall = false;
+ ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
+ switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
+ case ObjectLookupResult::ObjectAsVariant:
+ case ObjectLookupResult::Object: {
+ const QMetaType propType = l.qobjectLookup.propertyData->propType();
+ if (isTypeCompatible(type, propType)) {
+ storeResult = storeObjectProperty(&l, qmlScopeObject, value);
+ } else if (isUndefined(value, type)) {
+ storeResult = resetObjectProperty(&l, qmlScopeObject, engine->handle());
+ } else {
+ QVariant var(propType);
+ QV4::ExecutionEngine *v4 = engine->handle();
+ v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
+ storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
+ }
+
+ l.qobjectLookup.propertyCache->release();
+ break;
+ }
+ case ObjectLookupResult::FallbackAsVariant:
+ case ObjectLookupResult::Fallback: {
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
+ const QMetaProperty property = metaObject->property(l.qobjectFallbackLookup.coreIndex);
+ const QMetaType propType = property.metaType();
+ if (isTypeCompatible(type, propType)) {
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
+ } else if (isUndefined(value, type)) {
+ storeResult = resetFallbackProperty(&l, qmlScopeObject, &property, engine->handle());
+ } else {
+ QVariant var(propType);
+ QV4::ExecutionEngine *v4 = engine->handle();
+ v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
+ storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
+ }
+ break;
+ }
+ case ObjectLookupResult::Failure:
+ engine->handle()->throwTypeError();
+ return;
+ }
+
+ switch (storeResult) {
+ case ObjectPropertyResult::NeedsInit:
+ engine->handle()->throwTypeError();
+ break;
+ case ObjectPropertyResult::Deleted:
+ engine->handle()->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ break;
+ case ObjectPropertyResult::OK:
+ break;
+ }
+}
+
+QJSValue AOTCompiledContext::javaScriptGlobalProperty(uint nameIndex) const
+{
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedString name(scope, compilationUnit->runtimeStrings[nameIndex]);
+ QV4::ScopedObject global(scope, scope.engine->globalObject);
+ return QJSValuePrivate::fromReturnedValue(global->get(name->toPropertyKey()));
+}
+
+const QLoggingCategory *AOTCompiledContext::resolveLoggingCategory(QObject *wrapper, bool *ok) const
+{
+ if (wrapper) {
+ // We have to check this here because you may pass a plain QObject that only
+ // turns out to be a QQmlLoggingCategory at run time.
+ if (QQmlLoggingCategory *qQmlLoggingCategory
+ = qobject_cast<QQmlLoggingCategory *>(wrapper)) {
+ QLoggingCategory *loggingCategory = qQmlLoggingCategory->category();
+ *ok = true;
+ if (!loggingCategory) {
+ engine->handle()->throwError(
+ QStringLiteral("A QmlLoggingCatgory was provided without a valid name"));
+ }
+ return loggingCategory;
+ }
+ }
+
+ *ok = false;
+ return qmlEngine() ? &lcQml() : &lcJs();
+}
+
+void AOTCompiledContext::writeToConsole(
+ QtMsgType type, const QString &message, const QLoggingCategory *loggingCategory) const
+{
+ Q_ASSERT(loggingCategory->isEnabled(type));
+
+ const QV4::CppStackFrame *frame = engine->handle()->currentStackFrame;
+ Q_ASSERT(frame);
+
+ const QByteArray source(frame->source().toUtf8());
+ const QByteArray function(frame->function().toUtf8());
+ QMessageLogger logger(source.constData(), frame->lineNumber(),
+ function.constData(), loggingCategory->categoryName());
+
+ switch (type) {
+ case QtDebugMsg:
+ logger.debug("%s", qUtf8Printable(message));
+ break;
+ case QtInfoMsg:
+ logger.info("%s", qUtf8Printable(message));
+ break;
+ case QtWarningMsg:
+ logger.warning("%s", qUtf8Printable(message));
+ break;
+ case QtCriticalMsg:
+ logger.critical("%s", qUtf8Printable(message));
+ break;
+ default:
+ break;
+ }
+}
+
+QVariant AOTCompiledContext::constructValueType(
+ QMetaType resultMetaType, const QMetaObject *resultMetaObject,
+ int ctorIndex, void *ctorArg) const
+{
+ return QQmlValueTypeProvider::constructValueType(
+ resultMetaType, resultMetaObject, ctorIndex, ctorArg);
+}
+
+QDateTime AOTCompiledContext::constructDateTime(double timestamp) const
+{
+ return QV4::DateObject::timestampToDateTime(timestamp);
+}
+
+QDateTime AOTCompiledContext::constructDateTime(const QString &string) const
+{
+ return QV4::DateObject::stringToDateTime(string, engine->handle());
+}
+
+QDateTime AOTCompiledContext::constructDateTime(
+ double year, double month, double day, double hours,
+ double minutes, double seconds, double msecs) const
+{
+ return constructDateTime(QV4::DateObject::componentsToTimestamp(
+ year, month, day, hours, minutes, seconds, msecs, engine->handle()));
+}
+
+bool AOTCompiledContext::callQmlContextPropertyLookup(
+ uint index, void **args, const QMetaType *types, int argc) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue thisObject(scope);
+ QV4::ScopedFunctionObject function(
+ scope, l->qmlContextPropertyGetter(l, scope.engine, thisObject));
+ if (!function) {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [null] is not a function").arg(
+ compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ function->call(qmlScopeObject, args, types, argc);
+ return !scope.hasException();
+}
+
+void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const
+{
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
+}
+
+bool AOTCompiledContext::loadContextIdLookup(uint index, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ int objectId = -1;
+ QQmlContextData *context = nullptr;
+ Q_ASSERT(qmlContext);
+
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject) {
+ objectId = l->qmlContextIdObjectLookup.objectId;
+ context = qmlContext;
+ } else if (l->qmlContextPropertyGetter
+ == QV4::QQmlContextWrapper::lookupIdObjectInParentContext) {
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
+ for (context = qmlContext; context; context = context->parent().data()) {
+ objectId = context->propertyIndex(name);
+ if (objectId != -1 && objectId < context->numIdValues())
+ break;
+ }
+ } else {
+ return false;
+ }
+
+ Q_ASSERT(objectId >= 0);
+ Q_ASSERT(context != nullptr);
+ QQmlEnginePrivate *engine = QQmlEnginePrivate::get(qmlEngine());
+ if (QQmlPropertyCapture *capture = engine->propertyCapture)
+ capture->captureProperty(context->idValueBindings(objectId));
+ *static_cast<QObject **>(target) = context->idValue(objectId);
+ return true;
+}
+
+void AOTCompiledContext::initLoadContextIdLookup(uint index) const
+{
+ Q_ASSERT(!engine->hasError());
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
+ const QQmlRefPointer<QQmlContextData> ownContext = qmlContext;
+ for (auto context = ownContext; context; context = context->parent()) {
+ const int propertyIdx = context->propertyIndex(name);
+ if (propertyIdx == -1 || propertyIdx >= context->numIdValues())
+ continue;
+
+ if (context.data() == ownContext.data()) {
+ l->qmlContextIdObjectLookup.objectId = propertyIdx;
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObject;
+ } else {
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObjectInParentContext;
+ }
+
+ return;
+ }
+
+ Q_UNREACHABLE();
+}
+
+bool AOTCompiledContext::callObjectPropertyLookup(
+ uint index, QObject *object, void **args, const QMetaType *types, int argc) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
+ QV4::ScopedFunctionObject function(scope, l->getter(l, engine->handle(), thisObject));
+ if (!function) {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [object Object] is not a function")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ function->call(object, args, types, argc);
+ return !scope.hasException();
+}
+
+void AOTCompiledContext::initCallObjectPropertyLookup(uint index) const
+{
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
+}
+
+bool AOTCompiledContext::callGlobalLookup(
+ uint index, void **args, const QMetaType *types, int argc) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedFunctionObject function(scope, l->globalGetter(l, scope.engine));
+ if (!function) {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [null] is not a function")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ }
+
+ function->call(nullptr, args, types, argc);
+ return true;
+}
+
+void AOTCompiledContext::initCallGlobalLookup(uint index) const
+{
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
+}
+
+bool AOTCompiledContext::loadGlobalLookup(uint index, void *target, QMetaType type) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!QV4::ExecutionEngine::metaTypeFromJS(l->globalGetter(l, engine->handle()), type, target)) {
+ engine->handle()->throwTypeError();
+ return false;
+ }
+ return true;
+}
+
+void AOTCompiledContext::initLoadGlobalLookup(uint index) const
+{
+ Q_UNUSED(index);
+ Q_ASSERT(engine->hasError());
+ amendException(engine->handle());
+}
+
+bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+
+ if (!qmlScopeObject) {
+ engine->handle()->throwReferenceError(
+ compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+ return false;
+ }
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = loadObjectProperty(l, qmlScopeObject, target, this);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = loadFallbackProperty(l, qmlScopeObject, target, this);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted:
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of null")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
+bool AOTCompiledContext::writeBackScopeObjectPropertyLookup(uint index, void *source) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
+ result = writeBackObjectProperty(l, qmlScopeObject, source);
+ else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
+ result = writeBackFallbackProperty(l, qmlScopeObject, source);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted: // Silently omit the write back. Same as interpreter
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
+void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const
+{
+ QV4::ExecutionEngine *v4 = engine->handle();
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+
+ if (v4->hasException) {
+ amendException(v4);
+ return;
+ }
+
+ switch (initObjectLookup(this, l, qmlScopeObject, type)) {
+ case ObjectLookupResult::ObjectAsVariant:
+ case ObjectLookupResult::Object:
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
+ break;
+ case ObjectLookupResult::FallbackAsVariant:
+ case ObjectLookupResult::Fallback:
+ l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
+ break;
+ case ObjectLookupResult::Failure:
+ v4->throwTypeError();
+ break;
+ }
+}
+
+bool AOTCompiledContext::loadSingletonLookup(uint index, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+
+ if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton) {
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
+ scope, l->qmlContextSingletonLookup.singletonObject);
+
+ // We don't handle non-QObject singletons (as those can't be declared in qmltypes anyway)
+ Q_ASSERT(wrapper);
+ *static_cast<QObject **>(target) = wrapper->object();
+ return true;
+ }
+
+ return false;
+}
+
+using QmlContextPropertyGetter
+ = QV4::ReturnedValue (*)(QV4::Lookup *l, QV4::ExecutionEngine *engine, QV4::Value *thisObject);
+
+template<QmlContextPropertyGetter qmlContextPropertyGetter>
+static void initTypeWrapperLookup(
+ const AOTCompiledContext *context, QV4::Lookup *l, uint importNamespace)
+{
+ Q_ASSERT(!context->engine->hasError());
+ if (importNamespace != AOTCompiledContext::InvalidStringId) {
+ QV4::Scope scope(context->engine->handle());
+ QV4::ScopedString import(scope, context->compilationUnit->runtimeStrings[importNamespace]);
+
+ QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
+ Q_ASSERT(typeLoader);
+ if (const QQmlImportRef *importRef
+ = context->qmlContext->imports()->query(import, typeLoader).importNamespace) {
+
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
+ scope, QV4::QQmlTypeWrapper::create(
+ scope.engine, nullptr, context->qmlContext->imports(), importRef));
+ wrapper = l->qmlContextPropertyGetter(l, context->engine->handle(), wrapper);
+ l->qmlContextPropertyGetter = qmlContextPropertyGetter;
+ if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton)
+ l->qmlContextSingletonLookup.singletonObject.set(scope.engine, wrapper->heapObject());
+ else if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType)
+ l->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->heapObject());
+ return;
+ }
+ scope.engine->throwTypeError();
+ } else {
+ QV4::ExecutionEngine *v4 = context->engine->handle();
+ l->qmlContextPropertyGetter(l, v4, nullptr);
+ if (l->qmlContextPropertyGetter != qmlContextPropertyGetter) {
+ const QString error
+ = QLatin1String(qmlContextPropertyGetter
+ == QV4::QQmlContextWrapper::lookupSingleton
+ ? "%1 was a singleton at compile time, "
+ "but is not a singleton anymore."
+ : "%1 was not a singleton at compile time, "
+ "but is a singleton now.")
+ .arg(context->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+ v4->throwTypeError(error);
+ }
+ }
+}
+
+void AOTCompiledContext::initLoadSingletonLookup(uint index, uint importNamespace) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ initTypeWrapperLookup<QV4::QQmlContextWrapper::lookupSingleton>(this, l, importNamespace);
+}
+
+bool AOTCompiledContext::loadAttachedLookup(uint index, QObject *object, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QObjectWrapper::lookupAttached)
+ return false;
+
+ QV4::Scope scope(engine->handle());
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(scope, l->qmlTypeLookup.qmlTypeWrapper);
+ Q_ASSERT(wrapper);
+ *static_cast<QObject **>(target) = qmlAttachedPropertiesObject(
+ object, wrapper->d()->type().attachedPropertiesFunction(
+ QQmlEnginePrivate::get(qmlEngine())));
+ return true;
+}
+
+void AOTCompiledContext::initLoadAttachedLookup(
+ uint index, uint importNamespace, QObject *object) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
+
+ QQmlType type;
+ QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
+ Q_ASSERT(typeLoader);
+ if (importNamespace != InvalidStringId) {
+ QV4::ScopedString import(scope, compilationUnit->runtimeStrings[importNamespace]);
+ if (const QQmlImportRef *importRef
+ = qmlContext->imports()->query(import, typeLoader).importNamespace) {
+ type = qmlContext->imports()->query(name, importRef, typeLoader).type;
+ }
+ } else {
+ type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(name, typeLoader).type;
+ }
+
+ if (!type.isValid()) {
+ scope.engine->throwTypeError();
+ return;
+ }
+
+ QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
+ scope, QV4::QQmlTypeWrapper::create(scope.engine, object, type,
+ QV4::Heap::QQmlTypeWrapper::ExcludeEnums));
+
+ l->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->d());
+ l->getter = QV4::QObjectWrapper::lookupAttached;
+}
+
+bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupType)
+ return false;
+
+ const QV4::Heap::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::Heap::QQmlTypeWrapper *>(
+ l->qmlTypeLookup.qmlTypeWrapper.get());
+
+ QMetaType metaType = typeWrapper->type().typeId();
+ *static_cast<const QMetaObject **>(target)
+ = QQmlMetaType::metaObjectForType(metaType).metaObject();
+ return true;
+}
+
+void AOTCompiledContext::initLoadTypeLookup(uint index, uint importNamespace) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ initTypeWrapperLookup<QV4::QQmlContextWrapper::lookupType>(this, l, importNamespace);
+}
+
+bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ const auto doThrow = [&]() {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of null")
+ .arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
+ return false;
+ };
+
+ if (!object)
+ return doThrow();
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = loadObjectProperty(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = loadFallbackProperty(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
+ result = loadObjectAsVariant(l, object, target, this);
+ else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
+ result = loadFallbackAsVariant(l, object, target, this);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::Deleted:
+ return doThrow();
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
+bool AOTCompiledContext::writeBackObjectLookup(uint index, QObject *object, void *source) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!object)
+ return true;
+
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->getter == QV4::Lookup::getterQObject)
+ result = writeBackObjectProperty(l, object, source);
+ else if (l->getter == QV4::Lookup::getterFallback)
+ result = writeBackFallbackProperty(l, object, source);
+ else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
+ result = writeBackObjectAsVariant(l, object, source);
+ else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
+ result = writeBackFallbackAsVariant(l, object, source);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::Deleted: // Silently omit the write back
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
+void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaType type) const
+{
+ QV4::ExecutionEngine *v4 = engine->handle();
+ if (v4->hasException) {
+ amendException(v4);
+ } else {
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
+ l->getter = QV4::Lookup::getterQObject;
+ break;
+ case ObjectLookupResult::ObjectAsVariant:
+ l->getter = QV4::Lookup::getterQObjectAsVariant;
+ break;
+ case ObjectLookupResult::Fallback:
+ l->getter = QV4::Lookup::getterFallback;
+ break;
+ case ObjectLookupResult::FallbackAsVariant:
+ l->getter = QV4::Lookup::getterFallbackAsVariant;
+ break;
+ case ObjectLookupResult::Failure:
+ engine->handle()->throwTypeError();
+ break;
+ }
+ }
+}
+
+bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) const
+{
+ Q_ASSERT(value);
+
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
+ return false;
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ void *args[] = { target, nullptr };
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(value), QMetaObject::ReadProperty,
+ l->qgadgetLookup.coreIndex, args);
+ return true;
+}
+
+bool AOTCompiledContext::writeBackValueLookup(uint index, void *value, void *source) const
+{
+ Q_ASSERT(value);
+
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
+ return false;
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
+ Q_ASSERT(metaObject);
+
+ void *args[] = { source, nullptr };
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(value), QMetaObject::WriteProperty,
+ l->qgadgetLookup.coreIndex, args);
+ return true;
+}
+
+void AOTCompiledContext::initGetValueLookup(
+ uint index, const QMetaObject *metaObject, QMetaType type) const
+{
+ Q_ASSERT(!engine->hasError());
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (initValueLookup(l, compilationUnit, metaObject, type))
+ l->getter = QV4::QQmlValueTypeWrapper::lookupGetter;
+ else
+ engine->handle()->throwTypeError();
+}
+
+bool AOTCompiledContext::getEnumLookup(uint index, void *target) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
+ return false;
+ const bool isUnsigned
+ = l->qmlEnumValueLookup.metaType->flags & QMetaType::IsUnsignedEnumeration;
+ const QV4::ReturnedValue encoded = l->qmlEnumValueLookup.encodedEnumValue;
+ switch (l->qmlEnumValueLookup.metaType->size) {
+ case 1:
+ if (isUnsigned)
+ *static_cast<quint8 *>(target) = encoded;
+ else
+ *static_cast<qint8 *>(target) = encoded;
+ return true;
+ case 2:
+ if (isUnsigned)
+ *static_cast<quint16 *>(target) = encoded;
+ else
+ *static_cast<qint16 *>(target) = encoded;
+ return true;
+ case 4:
+ if (isUnsigned)
+ *static_cast<quint32 *>(target) = encoded;
+ else
+ *static_cast<qint32 *>(target) = encoded;
+ return true;
+ case 8:
+ if (isUnsigned)
+ *static_cast<quint64 *>(target) = encoded;
+ else
+ *static_cast<qint64 *>(target) = encoded;
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void AOTCompiledContext::initGetEnumLookup(
+ uint index, const QMetaObject *metaObject,
+ const char *enumerator, const char *enumValue) const
+{
+ Q_ASSERT(!engine->hasError());
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (!metaObject) {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Cannot read property '%1' of undefined")
+ .arg(QString::fromUtf8(enumValue)));
+ return;
+ }
+ const int enumIndex = metaObject->indexOfEnumerator(enumerator);
+ const QMetaEnum metaEnum = metaObject->enumerator(enumIndex);
+ l->qmlEnumValueLookup.encodedEnumValue = metaEnum.keyToValue(enumValue);
+ l->qmlEnumValueLookup.metaType = metaEnum.metaType().iface();
+ l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
+}
+
+bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *value) const
+{
+ const auto doThrow = [&]() {
+ engine->handle()->throwTypeError(
+ QStringLiteral("Value is null and could not be converted to an object"));
+ return false;
+ };
+
+ if (!object)
+ return doThrow();
+
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
+ if (l->setter == QV4::Lookup::setterQObject)
+ result = storeObjectProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallback)
+ result = storeFallbackProperty(l, object, value);
+ else if (l->setter == QV4::Lookup::setterQObjectAsVariant)
+ result = storeObjectAsVariant(engine->handle(), l, object, value);
+ else if (l->setter == QV4::Lookup::setterFallbackAsVariant)
+ result = storeFallbackAsVariant(engine->handle(), l, object, value);
+ else
+ return false;
+
+ switch (result) {
+ case ObjectPropertyResult::Deleted:
+ return doThrow();
+ case ObjectPropertyResult::NeedsInit:
+ return false;
+ case ObjectPropertyResult::OK:
+ return true;
+ }
+
+ Q_UNREACHABLE_RETURN(false);
+}
+
+void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaType type) const
+{
+ QV4::ExecutionEngine *v4 = engine->handle();
+ if (v4->hasException) {
+ amendException(v4);
+ } else {
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ switch (initObjectLookup(this, l, object, type)) {
+ case ObjectLookupResult::Object:
+ l->setter = QV4::Lookup::setterQObject;
+ break;
+ case ObjectLookupResult::ObjectAsVariant:
+ l->setter = QV4::Lookup::setterQObjectAsVariant;
+ break;
+ case ObjectLookupResult::Fallback:
+ l->setter = QV4::Lookup::setterFallback;
+ break;
+ case ObjectLookupResult::FallbackAsVariant:
+ l->setter = QV4::Lookup::setterFallbackAsVariant;
+ break;
+ case ObjectLookupResult::Failure:
+ engine->handle()->throwTypeError();
+ break;
+ }
+ }
+}
+
+bool AOTCompiledContext::setValueLookup(
+ uint index, void *target, void *value) const
+{
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (l->setter != QV4::QQmlValueTypeWrapper::lookupSetter)
+ return false;
+
+ const QMetaObject *metaObject
+ = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
+
+ void *args[] = { value, nullptr };
+ metaObject->d.static_metacall(
+ reinterpret_cast<QObject*>(target), QMetaObject::WriteProperty,
+ l->qgadgetLookup.coreIndex, args);
+ return true;
+}
+
+void AOTCompiledContext::initSetValueLookup(uint index, const QMetaObject *metaObject,
+ QMetaType type) const
+{
+ Q_ASSERT(!engine->hasError());
+ QV4::Lookup *l = compilationUnit->runtimeLookups + index;
+ if (initValueLookup(l, compilationUnit, metaObject, type))
+ l->setter = QV4::QQmlValueTypeWrapper::lookupSetter;
+ else
+ engine->handle()->throwTypeError();
+}
+
+} // namespace QQmlPrivate
+
QT_END_NAMESPACE