diff options
Diffstat (limited to 'src/qml/qml/qqml.cpp')
-rw-r--r-- | src/qml/qml/qqml.cpp | 2219 |
1 files changed, 1975 insertions, 244 deletions
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index cb55c0d834..eb716671b1 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -1,78 +1,75 @@ -/**************************************************************************** -** -** 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.h> -#include <private/qqmltypenotavailable_p.h> -#include <private/qqmlcomponent_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->deferredData.isEmpty() && !data->wasDeleted(object)) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); + if (!data + || !data->context + || !data->context->engine() + || data->deferredData.isEmpty() + || data->wasDeleted(object)) { + return; + } - QQmlComponentPrivate::DeferredState state; - QQmlComponentPrivate::beginDeferred(ep, object, &state); + if (!data->propertyCache) + data->propertyCache = QQmlMetaType::propertyCache(object->metaObject()); - // Release the reference for the deferral action (we still have one from construction) - data->releaseDeferredData(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine()); - QQmlComponentPrivate::completeDeferred(ep, &state); - } + 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) @@ -82,7 +79,7 @@ QQmlContext *qmlContext(const QObject *obj) QQmlEngine *qmlEngine(const QObject *obj) { - QQmlData *data = QQmlData::get(obj, false); + QQmlData *data = QQmlData::get(obj); if (!data || !data->context) return nullptr; return data->context->engine(); @@ -129,13 +126,79 @@ QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc 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 = { - 0, + QQmlPrivate::RegisterType::CurrentVersion, QMetaType(), QMetaType(), 0, @@ -149,14 +212,16 @@ int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, QQmlAttachedPropertiesFunc(), nullptr, - 0, - 0, - 0, + -1, + -1, + -1, nullptr, nullptr, nullptr, - QTypeRevision::zero() + QTypeRevision::zero(), + -1, + QQmlPrivate::ValueTypeCreationMethod::None }; return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); @@ -202,6 +267,7 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) /*! * \enum QQmlModuleImportSpecialVersions + * \relates QQmlEngine * * Defines some special values that can be passed to the version arguments of * qmlRegisterModuleImport() and qmlUnregisterModuleImport(). @@ -220,25 +286,26 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) */ /*! - * Registers an implicit import for module \a uri of major version \a majorVersion. + * \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 QmlModuleImportLatest the latest version + * \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 QmlModuleImportLatest the latest minor version of a - * \a importMajor is chosen. If \a importMajor is \l QmlModuleImportAuto the + * \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 \a QmlModuleImportModuleAny the module + * 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", QmlModuleImportModuleAny, - * "MyOtherModule", QmlModuleImportLatest); + * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny, + * "MyOtherModule", QQmlModuleImportLatest); * \endcode * * Or, you may specify that whenever major version 5 of "MyModule" is imported, @@ -252,8 +319,8 @@ static QTypeRevision resolveModuleVersion(int moduleMajor) * imported whenever "MyModule" is imported, specify the following: * * \code - * qmlRegisterModuleImport("MyModule", QmlModuleImportModuleAny, - * "MyOtherModule", QmlModuleImportAuto); + * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny, + * "MyOtherModule", QQmlModuleImportAuto); * \endcode * * \sa qmlUnregisterModuleImport() @@ -268,6 +335,7 @@ void qmlRegisterModuleImport(const char *uri, int moduleMajor, /*! + * \relates QQmlEngine * Removes a module import previously registered with qmlRegisterModuleImport() * * Calling this function makes sure that \a import of version @@ -291,30 +359,63 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q return QQmlMetaType::typeId(uri, QTypeRevision::fromVersion(versionMajor, versionMinor), qmlName); } +static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance) +{ + if (!instance) { + QQmlError error; + 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 (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 (!m_object) { - QQmlError error; - error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine.")); - QQmlEnginePrivate::get(qeng)->warning(qeng, error); + if (!checkSingletonInstance(qeng, m_object)) return nullptr; - } - if (qeng->thread() != m_object->thread()) { + 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; }; @@ -325,9 +426,9 @@ static QVector<QTypeRevision> availableRevisions(const QMetaObject *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(QTypeRevision::fromEncodedVersion(revision)); } @@ -395,13 +496,280 @@ static void uniqueRevisions(QVector<QTypeRevision> *revisions, QTypeRevision def 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( @@ -411,135 +779,40 @@ 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); - - QString noCreateReason; - - if (!creatable) { - noCreateReason = QString::fromUtf8(classInfo(type.classInfoMetaObject, "QML.UncreatableReason")); - if (noCreateReason.isEmpty()) - noCreateReason = QLatin1String("Type cannot be created in QML."); - } - - RegisterType revisionRegistration = { - 0, - 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() - }; - - const QTypeRevision added = revisionClassInfo( - type.classInfoMetaObject, "QML.AddedInVersion", - QTypeRevision::fromMinorVersion(0)); - const QTypeRevision removed = revisionClassInfo( - type.classInfoMetaObject, "QML.RemovedInVersion"); - - auto revisions = prepareRevisions(type.metaObject, added); - if (type.attachedPropertiesMetaObject) - revisions += availableRevisions(type.attachedPropertiesMetaObject); - uniqueRevisions(&revisions, type.version, added); - - for (QTypeRevision revision : revisions) { - if (revision < added) - continue; - if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion()) - break; - - // When removed, we still add revisions, but anonymous ones - if (removed.isValid() && !(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; - revisionRegistration.userdata = type.userdata; + doRegisterTypeAndRevisions(type, names); } - assignVersions(&revisionRegistration, revision, type.version); - revisionRegistration.customParser = type.customParserFactory(); - const int id = qmlregister(TypeRegistration, &revisionRegistration); - if (type.qmlTypeIds) - type.qmlTypeIds->append(id); } break; } case SingletonAndRevisionsRegistration: { const RegisterSingletonTypeAndRevisions &type = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data); - const char *elementName = classElementName(type.classInfoMetaObject); - RegisterSingletonType revisionRegistration = { - 0, - type.uri, - type.version, - elementName, - nullptr, - type.qObjectApi, - type.instanceMetaObject, - type.typeId, - type.extensionObjectCreate, - type.extensionMetaObject, - QTypeRevision() - }; - - const QTypeRevision added = revisionClassInfo( - type.classInfoMetaObject, "QML.AddedInVersion", - QTypeRevision::fromMinorVersion(0)); - const QTypeRevision removed = revisionClassInfo( - type.classInfoMetaObject, "QML.RemovedInVersion"); - - auto revisions = prepareRevisions(type.instanceMetaObject, added); - uniqueRevisions(&revisions, type.version, added); - - for (QTypeRevision revision : qAsConst(revisions)) { - if (revision < added) - continue; - if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion()) - break; - - // When removed, we still add revisions, but anonymous ones - if (removed.isValid() && !(revision < removed)) { - revisionRegistration.typeName = nullptr; - revisionRegistration.qObjectApi = nullptr; - } else { - revisionRegistration.typeName = elementName; - revisionRegistration.qObjectApi = type.qObjectApi; - } - - assignVersions(&revisionRegistration, revision, type.version); - const int id = qmlregister(SingletonRegistration, &revisionRegistration); - if (type.qmlTypeIds) - type.qmlTypeIds->append(id); + 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); - const char *elementName = classElementName(type.classInfoMetaObject); RegisterSequentialContainer revisionRegistration = { 0, type.uri, type.version, - elementName, + nullptr, type.typeId, type.metaSequence, QTypeRevision() @@ -548,24 +821,17 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data) const QTypeRevision added = revisionClassInfo( type.classInfoMetaObject, "QML.AddedInVersion", QTypeRevision::fromMinorVersion(0)); - const QTypeRevision removed = revisionClassInfo( - type.classInfoMetaObject, "QML.RemovedInVersion"); - - QVector<QTypeRevision> revisions = { added }; + QList<QTypeRevision> revisions = revisionClassInfos( + type.classInfoMetaObject, "QML.ExtraVersion"); + revisions.append(added); uniqueRevisions(&revisions, type.version, added); - for (QTypeRevision 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 (removed.isValid() && !(revision < removed)) - revisionRegistration.typeName = nullptr; - else - revisionRegistration.typeName = elementName; - assignVersions(&revisionRegistration, revision, type.version); const int id = qmlregister(SequentialContainerRegistration, &revisionRegistration); if (type.qmlTypeIds) @@ -574,32 +840,30 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data) 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: - dtype = QQmlMetaType::registerSequentialContainer(*reinterpret_cast<RegisterSequentialContainer *>(data)); - break; + 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) @@ -634,53 +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) - { - using T = QQmlTypeNotAvailable; +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 = { - 0, - QMetaType::fromType<T *>(), - QMetaType::fromType<QQmlListProperty<T>>(), - 0, - nullptr, - nullptr, - nullptr, + RegisterTypeAndRevisions type = { + 3, + QmlMetaType<T>::self(), + QmlMetaType<T>::list(), + 0, + nullptr, + nullptr, + nullptr, - uri, - QTypeRevision::fromMajorVersion(versionMajor), + uri, + QTypeRevision::fromMajorVersion(versionMajor), - &QQmlTypeNotAvailable::staticMetaObject, - classInfoMetaObject, + &QQmlTypeNotAvailable::staticMetaObject, + classInfoMetaObject, - attachedPropertiesFunc<T>(), - attachedPropertiesMetaObject<T>(), + attachedPropertiesFunc<T>(), + attachedPropertiesMetaObject<T>(), - StaticCastSelector<T, QQmlParserStatus>::cast(), - StaticCastSelector<T, QQmlPropertyValueSource>::cast(), - StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(), + StaticCastSelector<T, QQmlParserStatus>::cast(), + StaticCastSelector<T, QQmlPropertyValueSource>::cast(), + StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(), - nullptr, extension, qmlCreateCustomParser<T>, qmlTypeIds - }; + nullptr, + extension, + qmlCreateCustomParser<T>, + qmlTypeIds, + QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(), + false, + QmlMetaType<T>::sequence(), + }; - qmlregister(TypeAndRevisionsRegistration, &type); - } + qmlregister(TypeAndRevisionsRegistration, &type); +} + +QObject *AOTCompiledContext::thisObject() const +{ + return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame) + ->thisObject(); } -QJSValue QQmlPrivate::AOTCompiledContext::jsMetaType(int index) const +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 QQmlPrivate::AOTCompiledContext::setInstructionPointer(int offset) const +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 |