diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-20 17:05:50 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-31 15:18:55 +0200 |
commit | 6c76ee30ce1662ca8f8368de5ebd1e6bcfdf0d9e (patch) | |
tree | bea1db48da70a74565afab07801f20f143f8067d /src/qml/compiler | |
parent | 2f24150b03a8141b3e64442bcfcc08448b9a79e5 (diff) |
Eliminate qmldevtools_build
Move the relevant files into more fitting locations and build the
devtools from only parser, compiler and qmldirparser.
Change-Id: Ibf37a1187f36d02983f9f43c6622acb243785b7b
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/compiler')
20 files changed, 908 insertions, 5371 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 3291d6e1f5..c3dd5890d6 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -2,6 +2,7 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD HEADERS += \ + $$PWD/qv4alloca_p.h \ $$PWD/qv4bytecodegenerator_p.h \ $$PWD/qv4compileddata_p.h \ $$PWD/qv4compiler_p.h \ @@ -11,7 +12,11 @@ HEADERS += \ $$PWD/qv4codegen_p.h \ $$PWD/qqmlirbuilder_p.h \ $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4bytecodehandler_p.h + $$PWD/qv4bytecodehandler_p.h \ + $$PWD/qv4calldata_p.h \ + $$PWD/qv4util_p.h \ + $$PWD/qv4staticvalue_p.h \ + $$PWD/qv4stringtoarrayindex_p.h SOURCES += \ $$PWD/qv4bytecodegenerator.cpp \ @@ -24,30 +29,6 @@ SOURCES += \ $$PWD/qv4instr_moth.cpp \ $$PWD/qv4bytecodehandler.cpp -!qmldevtools_build { - -HEADERS += \ - $$PWD/qqmlirloader_p.h \ - $$PWD/qqmlpropertyresolver_p.h \ - $$PWD/qqmltypecompiler_p.h \ - $$PWD/qqmlpropertycachecreator_p.h \ - $$PWD/qqmlpropertyvalidator_p.h \ - $$PWD/qv4compilationunitmapper_p.h \ - $$PWD/qv4executablecompilationunit_p.h - -SOURCES += \ - $$PWD/qqmlirloader.cpp \ - $$PWD/qqmlpropertyresolver.cpp \ - $$PWD/qqmltypecompiler.cpp \ - $$PWD/qqmlpropertycachecreator.cpp \ - $$PWD/qqmlpropertyvalidator.cpp \ - $$PWD/qv4compilationunitmapper.cpp \ - $$PWD/qv4executablecompilationunit.cpp - -unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp -else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -} - gcc { equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing } diff --git a/src/qml/compiler/qqmlirloader.cpp b/src/qml/compiler/qqmlirloader.cpp deleted file mode 100644 index c2eb6da2fa..0000000000 --- a/src/qml/compiler/qqmlirloader.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlirloader_p.h" -#include <private/qqmlirbuilder_p.h> - -QT_BEGIN_NAMESPACE - -QQmlIRLoader::QQmlIRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) - : unit(qmlData) - , output(output) -{ - pool = output->jsParserEngine.pool(); -} - -void QQmlIRLoader::load() -{ - output->jsGenerator.stringTable.initializeFromBackingUnit(unit); - - const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); - - for (quint32 i = 0; i < qmlUnit->nImports; ++i) - output->imports << qmlUnit->importAt(i); - - if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { - QmlIR::Pragma *p = New<QmlIR::Pragma>(); - p->location = QV4::CompiledData::Location(); - p->type = QmlIR::Pragma::PragmaSingleton; - output->pragmas << p; - } - - for (uint i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); - QmlIR::Object *object = loadObject(serializedObject); - output->objects.append(object); - } -} - -struct FakeExpression : public QQmlJS::AST::NullExpression -{ - FakeExpression(int start, int length) - : location(start, length) - {} - - virtual QQmlJS::AST::SourceLocation firstSourceLocation() const - { return location; } - - virtual QQmlJS::AST::SourceLocation lastSourceLocation() const - { return location; } - -private: - QQmlJS::AST::SourceLocation location; -}; - -QmlIR::Object *QQmlIRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) -{ - QmlIR::Object *object = pool->New<QmlIR::Object>(); - object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); - - object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; - object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - object->flags = serializedObject->flags; - object->id = serializedObject->id; - object->location = serializedObject->location; - object->locationOfIdProperty = serializedObject->locationOfIdProperty; - - QVector<int> functionIndices; - functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); - - for (uint i = 0; i < serializedObject->nBindings; ++i) { - QmlIR::Binding *b = pool->New<QmlIR::Binding>(); - *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; - object->bindings->append(b); - if (b->type == QV4::CompiledData::Binding::Type_Script) { - functionIndices.append(b->value.compiledScriptIndex); - b->value.compiledScriptIndex = functionIndices.count() - 1; - - QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); - foe->nameIndex = 0; - - QQmlJS::AST::ExpressionNode *expr; - - if (b->stringIndex != quint32(0)) { - const int start = output->code.length(); - const QString script = output->stringAt(b->stringIndex); - const int length = script.length(); - output->code.append(script); - expr = new (pool) FakeExpression(start, length); - } else - expr = new (pool) QQmlJS::AST::NullExpression(); - foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy - object->functionsAndExpressions->append(foe); - } - } - - Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); - - for (uint i = 0; i < serializedObject->nSignals; ++i) { - const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); - QmlIR::Signal *s = pool->New<QmlIR::Signal>(); - s->nameIndex = serializedSignal->nameIndex; - s->location = serializedSignal->location; - s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); - - for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); - *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); - s->parameters->append(p); - } - - object->qmlSignals->append(s); - } - - for (uint i = 0; i < serializedObject->nEnums; ++i) { - const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); - QmlIR::Enum *e = pool->New<QmlIR::Enum>(); - e->nameIndex = serializedEnum->nameIndex; - e->location = serializedEnum->location; - e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >(); - - for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { - QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>(); - *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i); - e->enumValues->append(v); - } - - object->qmlEnums->append(e); - } - - const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); - for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { - QmlIR::Property *p = pool->New<QmlIR::Property>(); - *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; - object->properties->append(p); - } - - { - const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); - for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { - QmlIR::Alias *a = pool->New<QmlIR::Alias>(); - *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; - object->aliases->append(a); - } - } - - const quint32_le *functionIdx = serializedObject->functionOffsetTable(); - for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { - QmlIR::Function *f = pool->New<QmlIR::Function>(); - const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); - - functionIndices.append(*functionIdx); - f->index = functionIndices.count() - 1; - f->location = compiledFunction->location; - f->nameIndex = compiledFunction->nameIndex; - - f->formals.allocate(pool, int(compiledFunction->nFormals)); - const quint32_le *formalNameIdx = compiledFunction->formalsTable(); - for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) - f->formals[i] = *formalNameIdx; - - object->functions->append(f); - } - - object->runtimeFunctionIndices.allocate(pool, functionIndices); - - return object; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp deleted file mode 100644 index bd4f2a0612..0000000000 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlpropertycachecreator_p.h" - -#include <private/qqmlengine_p.h> - -QT_BEGIN_NAMESPACE - -QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0); - -QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, - const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache) - : referencingObjectIndex(referencingObjectIndex) - , instantiatingBinding(instantiatingBinding) - , instantiatingPropertyName(instantiatingPropertyName) - , referencingObjectPropertyCache(referencingObjectPropertyCache) -{ -} - -bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() -{ - if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty) - return true; - - Q_ASSERT(referencingObjectIndex >= 0); - Q_ASSERT(referencingObjectPropertyCache); - Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); - - bool notInRevision = false; - instantiatingProperty = QQmlPropertyResolver(referencingObjectPropertyCache) - .property(instantiatingPropertyName, ¬InRevision, - QQmlPropertyResolver::IgnoreRevision); - return instantiatingProperty != nullptr; -} - -QQmlRefPointer<QQmlPropertyCache> QQmlBindingInstantiationContext::instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const -{ - if (instantiatingProperty) { - if (instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType(), instantiatingProperty->typeMinorVersion()); - } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType())) { - return enginePrivate->cache(vtmo, instantiatingProperty->typeMinorVersion()); - } - } - return QQmlRefPointer<QQmlPropertyCache>(); -} - -void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const -{ - for (QQmlBindingInstantiationContext pendingBinding: *this) { - const int groupPropertyObjectIndex = pendingBinding.instantiatingBinding->value.objectIndex; - - if (propertyCaches->at(groupPropertyObjectIndex)) - continue; - - if (!pendingBinding.resolveInstantiatingProperty()) - continue; - - auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate); - propertyCaches->set(groupPropertyObjectIndex, cache); - } -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h deleted file mode 100644 index 28eea27675..0000000000 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ /dev/null @@ -1,843 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLPROPERTYCACHECREATOR_P_H -#define QQMLPROPERTYCACHECREATOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmlvaluetype_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlmetaobject_p.h> -#include <private/qqmlpropertyresolver_p.h> - -QT_BEGIN_NAMESPACE - -struct QQmlBindingInstantiationContext { - QQmlBindingInstantiationContext() {} - QQmlBindingInstantiationContext(int referencingObjectIndex, - const QV4::CompiledData::Binding *instantiatingBinding, - const QString &instantiatingPropertyName, - QQmlPropertyCache *referencingObjectPropertyCache); - - bool resolveInstantiatingProperty(); - QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; - - int referencingObjectIndex = -1; - const QV4::CompiledData::Binding *instantiatingBinding = nullptr; - QString instantiatingPropertyName; - QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache; - QQmlPropertyData *instantiatingProperty = nullptr; -}; - -struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext> -{ - void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; -}; - -struct QQmlPropertyCacheCreatorBase -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) -public: - static QAtomicInt classIndexCounter; -}; - -template <typename ObjectContainer> -class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase -{ -public: - typedef typename ObjectContainer::CompiledObject CompiledObject; - - QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, - QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, - QQmlEnginePrivate *enginePrivate, - const ObjectContainer *objectContainer, const QQmlImports *imports); - - QQmlCompileError buildMetaObjects(); - -protected: - QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context); - QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; - QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache); - - QString stringAt(int index) const { return objectContainer->stringAt(index); } - - QQmlEnginePrivate * const enginePrivate; - const ObjectContainer * const objectContainer; - const QQmlImports * const imports; - QQmlPropertyCacheVector *propertyCaches; - QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings; -}; - -template <typename ObjectContainer> -inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, - QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings, - QQmlEnginePrivate *enginePrivate, - const ObjectContainer *objectContainer, const QQmlImports *imports) - : enginePrivate(enginePrivate) - , objectContainer(objectContainer) - , imports(imports) - , propertyCaches(propertyCaches) - , pendingGroupPropertyBindings(pendingGroupPropertyBindings) -{ - propertyCaches->resize(objectContainer->objectCount()); -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects() -{ - QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(/*root object*/0, context); -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context) -{ - const CompiledObject *obj = objectContainer->objectAt(objectIndex); - - bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0; - if (!needVMEMetaObject) { - auto binding = obj->bindingsBegin(); - auto end = obj->bindingsEnd(); - for ( ; binding != end; ++binding) { - if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - // If the on assignment is inside a group property, we need to distinguish between QObject based - // group properties and value type group properties. For the former the base type is derived from - // the property that references us, for the latter we only need a meta-object on the referencing object - // because interceptors can't go to the shared value type instances. - if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) { - if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) { - const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex); - auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache); - if (error.isSet()) - return error; - } - } else { - // On assignments are implemented using value interceptors, which require a VME meta object. - needVMEMetaObject = true; - } - break; - } - } - } - - QQmlRefPointer<QQmlPropertyCache> baseTypeCache; - { - QQmlCompileError error; - baseTypeCache = propertyCacheForObject(obj, context, &error); - if (error.isSet()) - return error; - } - - if (baseTypeCache) { - if (needVMEMetaObject) { - QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache); - if (error.isSet()) - return error; - } else { - propertyCaches->set(objectIndex, baseTypeCache); - } - } - - if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) { - auto binding = obj->bindingsBegin(); - auto end = obj->bindingsEnd(); - for ( ; binding != end; ++binding) - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache); - - // Binding to group property where we failed to look up the type of the - // property? Possibly a group property that is an alias that's not resolved yet. - // Let's attempt to resolve it after we're done with the aliases and fill in the - // propertyCaches entry then. - if (!context.resolveInstantiatingProperty()) - pendingGroupPropertyBindings->append(context); - - QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context); - if (error.isSet()) - return error; - } - } - - QQmlCompileError noError; - return noError; -} - -template <typename ObjectContainer> -inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const -{ - if (context.instantiatingProperty) { - return context.instantiatingPropertyCache(enginePrivate); - } else if (obj->inheritedTypeNameIndex != 0) { - auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - - if (typeRef->isFullyDynamicType) { - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties.")); - return nullptr; - } - if (obj->signalCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals.")); - return nullptr; - } - if (obj->functionCount() > 0) { - *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions.")); - return nullptr; - } - } - - return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) { - auto *typeRef = objectContainer->resolvedType( - context.instantiatingBinding->propertyNameIndex); - Q_ASSERT(typeRef); - QQmlType qmltype = typeRef->type; - if (!qmltype.isValid()) { - QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) { - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } - - const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); - if (!attachedMo) { - *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object")); - return nullptr; - } - return enginePrivate->cache(attachedMo); - } - return nullptr; -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache) -{ - QQmlRefPointer<QQmlPropertyCache> cache; - cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(), - obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(), - obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount())); - - propertyCaches->set(objectIndex, cache); - propertyCaches->setNeedsVMEMetaObject(objectIndex); - - struct TypeData { - QV4::CompiledData::Property::Type dtype; - int metaType; - } builtinTypes[] = { - { QV4::CompiledData::Property::Var, QMetaType::QVariant }, - { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, - { QV4::CompiledData::Property::Int, QMetaType::Int }, - { QV4::CompiledData::Property::Bool, QMetaType::Bool }, - { QV4::CompiledData::Property::Real, QMetaType::Double }, - { QV4::CompiledData::Property::String, QMetaType::QString }, - { QV4::CompiledData::Property::Url, QMetaType::QUrl }, - { QV4::CompiledData::Property::Color, QMetaType::QColor }, - { QV4::CompiledData::Property::Font, QMetaType::QFont }, - { QV4::CompiledData::Property::Time, QMetaType::QTime }, - { QV4::CompiledData::Property::Date, QMetaType::QDate }, - { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, - { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, - { QV4::CompiledData::Property::Point, QMetaType::QPointF }, - { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, - { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, - { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, - { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, - { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, - { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } -}; - static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (objectIndex == /*root object*/0) { - const QString path = objectContainer->url().path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + - QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); - } - } - if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(baseTypeCache.data()).className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); - } - - cache->_dynamicClassName = newClassName; - - int varPropCount = 0; - - QQmlPropertyResolver resolver(baseTypeCache); - - auto p = obj->propertiesBegin(); - auto pend = obj->propertiesEnd(); - for ( ; p != pend; ++p) { - if (p->type == QV4::CompiledData::Property::Var) - varPropCount++; - - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), ¬InRevision); - if (d && d->isFinal()) - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); - } - - auto a = obj->aliasesBegin(); - auto aend = obj->aliasesEnd(); - for ( ; a != aend; ++a) { - bool notInRevision = false; - QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), ¬InRevision); - if (d && d->isFinal()) - return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property")); - } - - int effectivePropertyIndex = cache->propertyIndexCacheStart; - int effectiveMethodIndex = cache->methodIndexCacheStart; - - // For property change signal override detection. - // We prepopulate a set of signal names which already exist in the object, - // and throw an error if there is a signal/method defined as an override. - QSet<QString> seenSignals; - seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); - QQmlPropertyCache *parentCache = cache.data(); - while ((parentCache = parentCache->parent())) { - if (int pSigCount = parentCache->signalCount()) { - int pSigOffset = parentCache->signalOffset(); - for (int i = pSigOffset; i < pSigCount; ++i) { - QQmlPropertyData *currPSig = parentCache->signal(i); - // XXX TODO: find a better way to get signal name from the property data :-/ - for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); - iter != parentCache->stringCache.end(); ++iter) { - if (currPSig == (*iter).second) { - seenSignals.insert(iter.key()); - break; - } - } - } - } - } - - // Set up notify signals for properties - first normal, then alias - p = obj->propertiesBegin(); - pend = obj->propertiesEnd(); - for ( ; p != pend; ++p) { - auto flags = QQmlPropertyData::defaultSignalFlags(); - - QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - a = obj->aliasesBegin(); - aend = obj->aliasesEnd(); - for ( ; a != aend; ++a) { - auto flags = QQmlPropertyData::defaultSignalFlags(); - - QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - - auto e = obj->enumsBegin(); - auto eend = obj->enumsEnd(); - for ( ; e != eend; ++e) { - const int enumValueCount = e->enumValueCount(); - QVector<QQmlEnumValue> values; - values.reserve(enumValueCount); - - auto enumValue = e->enumValuesBegin(); - auto end = e->enumValuesEnd(); - for ( ; enumValue != end; ++enumValue) - values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value)); - - cache->appendEnum(stringAt(e->nameIndex), values); - } - - // Dynamic signals - auto s = obj->signalsBegin(); - auto send = obj->signalsEnd(); - for ( ; s != send; ++s) { - const int paramCount = s->parameterCount(); - - QList<QByteArray> names; - names.reserve(paramCount); - QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - int i = 0; - auto param = s->parametersBegin(); - auto end = s->parametersEnd(); - for ( ; param != end; ++param, ++i) { - names.append(stringAt(param->nameIndex).toUtf8()); - if (param->type < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[param->type].metaType; - } else { - // lazily resolved type - Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); - const QString customTypeName = stringAt(param->customTypeNameIndex); - QQmlType qmltype; - if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName)); - - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - paramTypes[i + 1] = compilationUnit->metaTypeId; - } else { - paramTypes[i + 1] = qmltype.typeId(); - } - } - } - } - - auto flags = QQmlPropertyData::defaultSignalFlags(); - if (paramCount) - flags.hasArguments = true; - - QString signalName = stringAt(s->nameIndex); - if (seenSignals.contains(signalName)) - return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal")); - seenSignals.insert(signalName); - - cache->appendSignal(signalName, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():nullptr, names); - } - - - // Dynamic slots - auto function = objectContainer->objectFunctionsBegin(obj); - auto fend = objectContainer->objectFunctionsEnd(obj); - for ( ; function != fend; ++function) { - auto flags = QQmlPropertyData::defaultSlotFlags(); - - const QString slotName = stringAt(function->nameIndex); - if (seenSignals.contains(slotName)) - return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal")); - // Note: we don't append slotName to the seenSignals list, since we don't - // protect against overriding change signals or methods with properties. - - QList<QByteArray> parameterNames; - auto formal = function->formalsBegin(); - auto end = function->formalsEnd(); - for ( ; formal != end; ++formal) { - flags.hasArguments = true; - parameterNames << stringAt(*formal).toUtf8(); - } - - cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); - } - - - // Dynamic properties - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - int propertyIdx = 0; - p = obj->propertiesBegin(); - pend = obj->propertiesEnd(); - for ( ; p != pend; ++p, ++propertyIdx) { - int propertyType = 0; - int propertTypeMinorVersion = 0; - QQmlPropertyData::Flags propertyFlags; - - if (p->type == QV4::CompiledData::Property::Var) { - propertyType = QMetaType::QVariant; - propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType; - } else if (p->type < builtinTypeCount) { - propertyType = builtinTypes[p->type].metaType; - - if (p->type == QV4::CompiledData::Property::Variant) - propertyFlags.type = QQmlPropertyData::Flags::QVariantType; - } else { - Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || - p->type == QV4::CompiledData::Property::Custom); - - QQmlType qmltype; - if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) { - return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type")); - } - - Q_ASSERT(qmltype.isValid()); - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = compilationUnit->metaTypeId; - } else { - propertyType = compilationUnit->listMetaTypeId; - } - } else { - if (p->type == QV4::CompiledData::Property::Custom) { - propertyType = qmltype.typeId(); - propertTypeMinorVersion = qmltype.minorVersion(); - } else { - propertyType = qmltype.qListTypeId(); - } - } - - if (p->type == QV4::CompiledData::Property::Custom) - propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType; - else - propertyFlags.type = QQmlPropertyData::Flags::QListType; - } - - if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) - propertyFlags.isWritable = true; - - - QString propertyName = stringAt(p->nameIndex); - if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) - cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, propertTypeMinorVersion, effectiveSignalIndex); - - effectiveSignalIndex++; - } - - QQmlCompileError noError; - return noError; -} - -template <typename ObjectContainer> -class QQmlPropertyCacheAliasCreator -{ -public: - typedef typename ObjectContainer::CompiledObject CompiledObject; - - QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); - - void appendAliasPropertiesToMetaObjects(); - - QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); - -private: - void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags); - - void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const; - - int objectForId(const CompiledObject &component, int id) const; - - QQmlPropertyCacheVector *propertyCaches; - const ObjectContainer *objectContainer; -}; - -template <typename ObjectContainer> -inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) - : propertyCaches(propertyCaches) - , objectContainer(objectContainer) -{ - -} - -template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects() -{ - // skip the root object (index 0) as that one does not have a first object index originating - // from a binding. - for (int i = 1; i < objectContainer->objectCount(); ++i) { - const CompiledObject &component = *objectContainer->objectAt(i); - if (!(component.flags & QV4::CompiledData::Object::IsComponent)) - continue; - - const auto rootBinding = component.bindingsBegin(); - appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); - } - - const int rootObjectIndex = 0; - appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); -} - -template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) -{ - QVector<int> objectsWithAliases; - collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases); - if (objectsWithAliases.isEmpty()) - return; - - const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) { - auto alias = object.aliasesBegin(); - auto end = object.aliasesEnd(); - for ( ; alias != end; ++alias) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); - - const int targetObjectIndex = objectForId(component, alias->targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - - if (alias->aliasToLocalAlias) - continue; - - if (alias->encodedMetaPropertyIndex == -1) - continue; - - const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); - Q_ASSERT(targetCache); - - int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex(); - QQmlPropertyData *targetProperty = targetCache->property(coreIndex); - if (!targetProperty) - return false; - } - return true; - }; - - do { - QVector<int> pendingObjects; - - for (int objectIndex: qAsConst(objectsWithAliases)) { - const CompiledObject &object = *objectContainer->objectAt(objectIndex); - - if (allAliasTargetsExist(object)) { - appendAliasesToPropertyCache(component, objectIndex); - } else { - pendingObjects.append(objectIndex); - } - - } - qSwap(objectsWithAliases, pendingObjects); - } while (!objectsWithAliases.isEmpty()); -} - -template <typename ObjectContainer> -inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const -{ - const CompiledObject &object = *objectContainer->objectAt(objectIndex); - if (object.aliasCount() > 0) - objectsWithAliases->append(objectIndex); - - // Stop at Component boundary - if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) - return; - - auto binding = object.bindingsBegin(); - auto end = object.bindingsEnd(); - for (; binding != end; ++binding) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases); - } -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias( - const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, - QQmlPropertyData::Flags *propertyFlags) -{ - *type = 0; - bool writable = false; - bool resettable = false; - - propertyFlags->isAlias = true; - - if (alias.aliasToLocalAlias) { - const QV4::CompiledData::Alias *lastAlias = &alias; - QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias}); - - do { - const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex); - Q_ASSERT(targetObject); - - auto nextAlias = targetObject->aliasesBegin(); - for (uint i = 0; i < lastAlias->localAliasIndex; ++i) - ++nextAlias; - - const QV4::CompiledData::Alias *targetAlias = &(*nextAlias); - if (seenAliases.contains(targetAlias)) { - return QQmlCompileError(targetAlias->location, - QQmlPropertyCacheCreatorBase::tr("Cyclic alias")); - } - - seenAliases.append(targetAlias); - lastAlias = targetAlias; - } while (lastAlias->aliasToLocalAlias); - - return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags); - } - - const int targetObjectIndex = objectForId(component, alias.targetObjectId); - Q_ASSERT(targetObjectIndex >= 0); - const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex); - - if (alias.encodedMetaPropertyIndex == -1) { - Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); - auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex); - if (!typeRef) { - // Can be caused by the alias target not being a valid id or property. E.g.: - // property alias dataValue: dataVal - // invalidAliasComponent { id: dataVal } - return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); - } - - if (typeRef->type.isValid()) - *type = typeRef->type.typeId(); - else - *type = typeRef->compilationUnit->metaTypeId; - - *minorVersion = typeRef->minorVersion; - - propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType; - } else { - int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex(); - int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex(); - - QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); - Q_ASSERT(targetCache); - QQmlPropertyData *targetProperty = targetCache->property(coreIndex); - Q_ASSERT(targetProperty); - - *type = targetProperty->propType(); - - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - *type = QVariant::Int; - else - *type = valueTypeMetaObject->property(valueTypeIndex).userType(); - } else { - if (targetProperty->isEnum()) { - *type = QVariant::Int; - } else { - // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); - - if (targetProperty->isVarProperty()) - propertyFlags->type = QQmlPropertyData::Flags::QVariantType; - } - } - } - - propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable; - propertyFlags->isResettable = resettable; - return QQmlCompileError(); -} - -template <typename ObjectContainer> -inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache( - const CompiledObject &component, int objectIndex) -{ - const CompiledObject &object = *objectContainer->objectAt(objectIndex); - if (!object.aliasCount()) - return QQmlCompileError(); - - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); - Q_ASSERT(propertyCache); - - int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count(); - int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count(); - - int aliasIndex = 0; - auto alias = object.aliasesBegin(); - auto end = object.aliasesEnd(); - for ( ; alias != end; ++alias, ++aliasIndex) { - Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); - - int type = 0; - int minorVersion = 0; - QQmlPropertyData::Flags propertyFlags; - QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); - if (error.isSet()) - return error; - - const QString propertyName = objectContainer->stringAt(alias->nameIndex); - - if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias) - propertyCache->_defaultPropertyName = propertyName; - - propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, minorVersion, effectiveSignalIndex++); - } - - return QQmlCompileError(); -} - -template <typename ObjectContainer> -inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const -{ - for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) { - const int candidateIndex = component.namedObjectsInComponentTable()[i]; - const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex); - if (candidate.id == id) - return candidateIndex; - } - return -1; -} - -QT_END_NAMESPACE - -#endif // QQMLPROPERTYCACHECREATOR_P_H diff --git a/src/qml/compiler/qqmlpropertyresolver.cpp b/src/qml/compiler/qqmlpropertyresolver.cpp deleted file mode 100644 index 90eaca0b90..0000000000 --- a/src/qml/compiler/qqmlpropertyresolver.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlpropertyresolver_p.h" - -QT_BEGIN_NAMESPACE - -QQmlPropertyData *QQmlPropertyResolver::property(const QString &name, bool *notInRevision, - RevisionCheck check) const -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - - // Find the first property - while (d && d->isFunction()) - d = cache->overrideData(d); - - if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else { - return d; - } -} - - -QQmlPropertyData *QQmlPropertyResolver::signal(const QString &name, bool *notInRevision) const -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(QLatin1String("Changed"))) { - QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - - d = property(propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex()); - } - - return nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp deleted file mode 100644 index 71d5318652..0000000000 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ /dev/null @@ -1,729 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlpropertyvalidator_p.h" - -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlstringconverters_p.h> -#include <private/qqmlpropertyresolver_p.h> -#include <QtCore/qdatetime.h> - -QT_BEGIN_NAMESPACE - -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) - : enginePrivate(enginePrivate) - , compilationUnit(compilationUnit) - , imports(imports) - , qmlUnit(compilationUnit->unitData()) - , propertyCaches(compilationUnit->propertyCaches) - , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) -{ - bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); -} - -QVector<QQmlCompileError> QQmlPropertyValidator::validate() -{ - return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); -} - -typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; - -struct BindingFinder -{ - bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const - { - return name < binding->propertyNameIndex; - } - bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const - { - return binding->propertyNameIndex < name; - } - bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const - { - return lhs->propertyNameIndex < rhs->propertyNameIndex; - } -}; - -QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const -{ - const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex); - - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->nBindings == 1); - const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return validateObject(componentBinding->value.objectIndex, componentBinding); - } - - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); - if (!propertyCache) - return QVector<QQmlCompileError>(); - - QQmlCustomParser *customParser = nullptr; - if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) - customParser = typeRef->type.customParser(); - } - - QList<const QV4::CompiledData::Binding*> customBindings; - - // Collect group properties first for sanity checking - // vector values are sorted by property name string index. - GroupPropertyVector groupProperties; - const QV4::CompiledData::Binding *binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - if (!binding->isGroupProperty()) - continue; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - continue; - - if (populatingValueTypeGroupProperty) { - return recordError(binding->location, tr("Property assignment expected")); - } - - GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - groupProperties.insert(pos, binding); - } - - QQmlPropertyResolver propertyResolver(propertyCache); - - QString defaultPropertyName; - QQmlPropertyData *defaultProperty = nullptr; - if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); - defaultPropertyName = cache->defaultPropertyName(); - defaultProperty = cache->defaultProperty(); - } else { - defaultPropertyName = propertyCache->defaultPropertyName(); - defaultProperty = propertyCache->defaultProperty(); - } - - QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); - - binding = obj->bindingTable(); - for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { - QString name = stringAt(binding->propertyNameIndex); - - if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - customBindings << binding; - continue; - } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) - && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - customBindings << binding; - continue; - } - } - - bool bindingToDefaultProperty = false; - bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; - - bool notInRevision = false; - QQmlPropertyData *pd = nullptr; - if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { - pd = propertyResolver.signal(name, ¬InRevision); - } else { - pd = propertyResolver.property(name, ¬InRevision, - QQmlPropertyResolver::CheckRevision); - } - - if (notInRevision) { - QString typeName = stringAt(obj->inheritedTypeNameIndex); - auto *objectType = resolvedType(obj->inheritedTypeNameIndex); - if (objectType && objectType->type.isValid()) { - return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); - } else { - return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); - } - } - } else { - if (isGroupProperty) - return recordError(binding->location, tr("Cannot assign a value directly to a grouped property")); - - pd = defaultProperty; - name = defaultPropertyName; - bindingToDefaultProperty = true; - } - - if (pd) - collectedBindingPropertyData[i] = pd; - - if (name.constData()->isUpper() && !binding->isAttachedProperty()) { - QQmlType type; - QQmlImportNamespace *typeNamespace = nullptr; - imports.resolveType(stringAt(binding->propertyNameIndex), &type, nullptr, nullptr, &typeNamespace); - if (typeNamespace) - return recordError(binding->location, tr("Invalid use of namespace")); - return recordError(binding->location, tr("Invalid attached object assignment")); - } - - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - const bool populatingValueTypeGroupProperty - = pd - && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()) - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment); - const QVector<QQmlCompileError> subObjectValidatorErrors - = validateObject(binding->value.objectIndex, binding, - populatingValueTypeGroupProperty); - if (!subObjectValidatorErrors.isEmpty()) - return subObjectValidatorErrors; - } - - // Signal handlers were resolved and checked earlier in the signal handler conversion pass. - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { - return recordError(binding->location, tr("Attached properties cannot be used here")); - } - continue; - } - - if (pd) { - GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); - const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); - - if (!pd->isWritable() - && !pd->isQList() - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) - ) { - - if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) - return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); - return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - } - - if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { - QString error; - if (pd->propType() == qMetaTypeId<QQmlScriptString>()) - error = tr( "Cannot assign multiple values to a script property"); - else - error = tr( "Cannot assign multiple values to a singular property"); - return recordError(binding->valueLocation, error); - } - - if (!bindingToDefaultProperty - && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) - && assigningToGroupProperty) { - QV4::CompiledData::Location loc = binding->valueLocation; - if (loc < (*assignedGroupProperty)->valueLocation) - loc = (*assignedGroupProperty)->valueLocation; - - if (pd && QQmlValueTypeFactory::isValueType(pd->propType())) - return recordError(loc, tr("Property has already been assigned a value")); - return recordError(loc, tr("Cannot assign a value directly to a grouped property")); - } - - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding); - if (bindingError.isSet()) - return recordError(bindingError); - } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - QQmlCompileError bindingError = validateObjectBinding(pd, name, binding); - if (bindingError.isSet()) - return recordError(bindingError); - } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType())) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) { - if (!pd->isWritable()) { - return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); - } - } else { - return recordError(binding->location, tr("Invalid grouped property access")); - } - } else { - if (!enginePrivate->propertyCacheForType(pd->propType())) { - return recordError(binding->location, - tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type") - .arg(name) - .arg(QString::fromLatin1(QMetaType::typeName(pd->propType()))) - ); - } - } - } - } else { - if (customParser) { - customBindings << binding; - continue; - } - if (bindingToDefaultProperty) { - return recordError(binding->location, tr("Cannot assign to non-existent default property")); - } else { - return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name)); - } - } - } - - if (obj->idNameIndex) { - if (populatingValueTypeGroupProperty) - return recordError(obj->locationOfIdProperty, tr("Invalid use of id property with a value type")); - - bool notInRevision = false; - collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); - } - - if (customParser && !customBindings.isEmpty()) { - customParser->clearErrors(); - customParser->validator = this; - customParser->engine = enginePrivate; - customParser->imports = &imports; - customParser->verifyBindings(compilationUnit, customBindings); - customParser->validator = nullptr; - customParser->engine = nullptr; - customParser->imports = (QQmlImports*)nullptr; - QVector<QQmlCompileError> parserErrors = customParser->errors(); - if (!parserErrors.isEmpty()) - return parserErrors; - } - - (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - - QVector<QQmlCompileError> noError; - return noError; -} - -QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const -{ - if (property->isQList()) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); - } - - QQmlCompileError noError; - - if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) - return noError; - - QString value = compilationUnit->bindingValueAsString(binding); - QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex()); - bool ok; - if (p.isFlagType()) { - p.enumerator().keysToValue(value.toUtf8().constData(), &ok); - } else - p.enumerator().keyToValue(value.toUtf8().constData(), &ok); - - if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration")); - } - return noError; - } - - auto warnOrError = [&](const QString &error) { - if (binding->type == QV4::CompiledData::Binding::Type_Null) { - QQmlError warning; - warning.setUrl(compilationUnit->url()); - warning.setLine(binding->valueLocation.line); - warning.setColumn(binding->valueLocation.column); - warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML " - "is deprecated. This will become a compile error in " - "future versions of Qt.")); - enginePrivate->warning(warning); - return noError; - } - return QQmlCompileError(binding->valueLocation, error); - }; - - switch (property->propType()) { - case QMetaType::QVariant: - break; - case QVariant::String: { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string expected")); - } - } - break; - case QVariant::StringList: { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string or string list expected")); - } - } - break; - case QVariant::ByteArray: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return warnOrError(tr("Invalid property assignment: byte array expected")); - } - } - break; - case QVariant::Url: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return warnOrError(tr("Invalid property assignment: url expected")); - } - } - break; - case QVariant::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = compilationUnit->bindingValueAsNumber(binding); - if (double(uint(d)) == d) - return noError; - } - return warnOrError(tr("Invalid property assignment: unsigned int expected")); - } - break; - case QVariant::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { - double d = compilationUnit->bindingValueAsNumber(binding); - if (double(int(d)) == d) - return noError; - } - return warnOrError(tr("Invalid property assignment: int expected")); - } - break; - case QMetaType::Float: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return warnOrError(tr("Invalid property assignment: number expected")); - } - } - break; - case QVariant::Double: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return warnOrError(tr("Invalid property assignment: number expected")); - } - } - break; - case QVariant::Color: { - bool ok = false; - QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: color expected")); - } - } - break; -#if QT_CONFIG(datestring) - case QVariant::Date: { - bool ok = false; - QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: date expected")); - } - } - break; - case QVariant::Time: { - bool ok = false; - QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: time expected")); - } - } - break; - case QVariant::DateTime: { - bool ok = false; - QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: datetime expected")); - } - } - break; -#endif // datestring - case QVariant::Point: { - bool ok = false; - QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::PointF: { - bool ok = false; - QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::Size: { - bool ok = false; - QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: size expected")); - } - } - break; - case QVariant::SizeF: { - bool ok = false; - QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: size expected")); - } - } - break; - case QVariant::Rect: { - bool ok = false; - QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: rect expected")); - } - } - break; - case QVariant::RectF: { - bool ok = false; - QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); - if (!ok) { - return warnOrError(tr("Invalid property assignment: point expected")); - } - } - break; - case QVariant::Bool: { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return warnOrError(tr("Invalid property assignment: boolean expected")); - } - } - break; - case QVariant::Vector2D: { - struct { - float xp; - float yp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 2D vector expected")); - } - } - break; - case QVariant::Vector3D: { - struct { - float xp; - float yp; - float zy; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 3D vector expected")); - } - } - break; - case QVariant::Vector4D: { - struct { - float xp; - float yp; - float zy; - float wp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 4D vector expected")); - } - } - break; - case QVariant::Quaternion: { - struct { - float wp; - float xp; - float yp; - float zp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: quaternion expected")); - } - } - break; - case QVariant::RegExp: - case QVariant::RegularExpression: - return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); - default: { - // generate single literal value assignment to a list property if required - if (property->propType() == qMetaTypeId<QList<qreal> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return warnOrError(tr("Invalid property assignment: number or array of numbers expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QList<int> >()) { - bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); - if (ok) { - double n = compilationUnit->bindingValueAsNumber(binding); - if (double(int(n)) != n) - ok = false; - } - if (!ok) - return warnOrError(tr("Invalid property assignment: int or array of ints expected")); - break; - } else if (property->propType() == qMetaTypeId<QList<bool> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return warnOrError(tr("Invalid property assignment: bool or array of bools expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_String) { - return warnOrError(tr("Invalid property assignment: url or array of urls expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QList<QString> >()) { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string or array of strings expected")); - } - break; - } else if (property->propType() == qMetaTypeId<QJSValue>()) { - break; - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { - break; - } else if (property->isQObject() - && binding->type == QV4::CompiledData::Binding::Type_Null) { - break; - } - - // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); - if (!converter) { - return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); - } - } - break; - } - return noError; -} - -/*! - Returns true if from can be assigned to a (QObject) property of type - to. -*/ -bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const -{ - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); - - while (fromMo) { - if (fromMo == toMo) - return true; - fromMo = fromMo->parent(); - } - return false; -} - -QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const -{ - QVector<QQmlCompileError> errors; - errors.append(QQmlCompileError(location, description)); - return errors; -} - -QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const -{ - QVector<QQmlCompileError> errors; - errors.append(error); - return errors; -} - -QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const -{ - QQmlCompileError noError; - - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); - - bool isValueSource = false; - bool isPropertyInterceptor = false; - - const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex); - if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) { - QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - const QMetaObject *mo = cache->firstCppMetaObject(); - QQmlType qmlType; - while (mo && !qmlType.isValid()) { - qmlType = QQmlMetaType::qmlType(mo); - mo = mo->superClass(); - } - Q_ASSERT(qmlType.isValid()); - - isValueSource = qmlType.propertyValueSourceCast() != -1; - isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1; - } - - if (!isValueSource && !isPropertyInterceptor) { - return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName)); - } - - return noError; - } - - if (QQmlMetaType::isInterface(property->propType())) { - // Can only check at instantiation time if the created sub-object successfully casts to the - // target interface. - return noError; - } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) { - // We can convert everything to QVariant :) - return noError; - } else if (property->isQList()) { - const int listType = enginePrivate->listType(property->propType()); - if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); - if (!canCoerce(listType, source)) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); - } - } - return noError; - } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { - return noError; - } else if (QQmlValueTypeFactory::isValueType(property->propType())) { - auto typeName = QMetaType::typeName(property->propType()); - return QQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object") - .arg(typeName ? QString::fromLatin1(typeName) : QString::fromLatin1("<unknown type>")) - .arg(propertyName)); - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); - } else { - // We want to use the raw metaObject here as the raw metaobject is the - // actual property type before we applied any extensions that might - // effect the properties on the type, but don't effect assignability - // Using -1 for the minor version ensures that we get the raw metaObject. - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1); - - // Will be true if the assigned type inherits propertyMetaObject - bool isAssignable = false; - // Determine isAssignable value - if (propertyMetaObject) { - QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); - while (c && !isAssignable) { - isAssignable |= c == propertyMetaObject; - c = c->parent(); - } - } - - if (!isAssignable) { - return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") - .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType())))); - } - } - return noError; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h deleted file mode 100644 index 8244b2df21..0000000000 --- a/src/qml/compiler/qqmlpropertyvalidator_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLPROPERTYVALIDATOR_P_H -#define QQMLPROPERTYVALIDATOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmltypecompiler_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlPropertyValidator -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) -public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit); - - QVector<QQmlCompileError> validate(); - -private: - QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const; - QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; - QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; - - bool canCoerce(int to, QQmlPropertyCache *fromMo) const; - - Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const; - Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const; - QString stringAt(int index) const { return compilationUnit->stringAt(index); } - QV4::ResolvedTypeReference *resolvedType(int id) const - { - return compilationUnit->resolvedType(id); - } - - QQmlEnginePrivate *enginePrivate; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; - const QQmlImports &imports; - const QV4::CompiledData::Unit *qmlUnit; - const QQmlPropertyCacheVector &propertyCaches; - - QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject; -}; - -QT_END_NAMESPACE - -#endif // QQMLPROPERTYVALIDATOR_P_H diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp deleted file mode 100644 index 66320b8db9..0000000000 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ /dev/null @@ -1,1429 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltypecompiler_p.h" - -#include <private/qqmlirbuilder_p.h> -#include <private/qqmlobjectcreator_p.h> -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlvmemetaobject_p.h> -#include <private/qqmlcomponent_p.h> -#include <private/qqmlpropertyresolver_p.h> - -#define COMPILE_EXCEPTION(token, desc) \ - { \ - recordError((token)->location, desc); \ - return false; \ - } - -QT_BEGIN_NAMESPACE - -QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, - QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) - : resolvedTypes(resolvedTypeCache) - , engine(engine) - , typeData(typeData) - , dependencyHasher(dependencyHasher) - , typeNameCache(typeNameCache) - , document(parsedQML) -{ -} - -QQmlRefPointer<QV4::ExecutableCompilationUnit> QQmlTypeCompiler::compile() -{ - // Build property caches and VME meta object data - - for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd(); - it != end; ++it) { - QQmlCustomParser *customParser = (*it)->type.customParser(); - if (customParser) - customParsers.insert(it.key(), customParser); - } - - QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings; - - - { - QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings, - engine, this, imports()); - QQmlCompileError error = propertyCacheBuilder.buildMetaObjects(); - if (error.isSet()) { - recordError(error); - return nullptr; - } - } - - { - QQmlDefaultPropertyMerger merger(this); - merger.mergeDefaultProperties(); - } - - { - SignalHandlerConverter converter(this); - if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) - return nullptr; - } - - { - QQmlEnumTypeResolver enumResolver(this); - if (!enumResolver.resolveEnumBindings()) - return nullptr; - } - - { - QQmlCustomParserScriptIndexer cpi(this); - cpi.annotateBindingsWithScriptStrings(); - } - - { - QQmlAliasAnnotator annotator(this); - annotator.annotateBindingsToAliases(); - } - - // Resolve component boundaries and aliases - - { - // Scan for components, determine their scopes and resolve aliases within the scope. - QQmlComponentAndAliasResolver resolver(this); - if (!resolver.resolve()) - return nullptr; - - pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_propertyCaches); - } - - { - QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this); - if (!deferredAndCustomParserBindingScanner.scanObject()) - return nullptr; - } - - if (!document->javaScriptCompilationUnit.unitData()) { - // Compile JS binding expressions and signal handlers if necessary - { - // We can compile script strings ahead of time, but they must be compiled - // without type optimizations as their scope is always entirely dynamic. - QQmlScriptStringScanner sss(this); - sss.scan(); - } - - document->jsModule.fileName = typeData->urlString(); - document->jsModule.finalUrl = typeData->finalUrlString(); - QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, - document->program, &document->jsGenerator.stringTable, engine->v4engine()->illegalNames()); - QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); - if (!jsCodeGen.generateCodeForComponents()) - return nullptr; - - document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false); - } - - // Generate QML compiled type data structures - - QmlIR::QmlUnitGenerator qmlGenerator; - qmlGenerator.generate(*document, dependencyHasher); - - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit - = QV4::ExecutableCompilationUnit::create(std::move( - document->javaScriptCompilationUnit)); - compilationUnit->typeNameCache = typeNameCache; - compilationUnit->resolvedTypes = *resolvedTypes; - compilationUnit->propertyCaches = std::move(m_propertyCaches); - Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount())); - - if (errors.isEmpty()) - return compilationUnit; - else - return nullptr; -} - -void QQmlTypeCompiler::recordError(QQmlError error) -{ - error.setUrl(url()); - errors << error; -} - -void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description) -{ - QQmlError error; - error.setLine(location.line); - error.setColumn(location.column); - error.setDescription(description); - error.setUrl(url()); - errors << error; -} - -void QQmlTypeCompiler::recordError(const QQmlCompileError &error) -{ - recordError(error.location, error.description); -} - -QString QQmlTypeCompiler::stringAt(int idx) const -{ - return document->stringAt(idx); -} - -int QQmlTypeCompiler::registerString(const QString &str) -{ - return document->jsGenerator.registerString(str); -} - -int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v) -{ - return document->jsGenerator.registerConstant(v); -} - -const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const -{ - return document->javaScriptCompilationUnit.unitData(); -} - -const QQmlImports *QQmlTypeCompiler::imports() const -{ - return &typeData->imports(); -} - -QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const -{ - return &document->objects; -} - -void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) -{ - m_propertyCaches = std::move(caches); - Q_ASSERT(m_propertyCaches.count() > 0); -} - -const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const -{ - return &m_propertyCaches; -} - -QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches() -{ - return std::move(m_propertyCaches); -} - -QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() -{ - return document->jsParserEngine.pool(); -} - -QStringRef QQmlTypeCompiler::newStringRef(const QString &string) -{ - return document->jsParserEngine.newStringRef(string); -} - -const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const -{ - return &document->jsGenerator.stringTable; -} - -QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const -{ - return object->bindingAsString(document, scriptIndex); -} - -void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion) -{ - const quint32 moduleIdx = registerString(module); - const quint32 qualifierIdx = registerString(qualifier); - - for (int i = 0, count = document->imports.count(); i < count; ++i) { - const QV4::CompiledData::Import *existingImport = document->imports.at(i); - if (existingImport->type == QV4::CompiledData::Import::ImportLibrary - && existingImport->uriIndex == moduleIdx - && existingImport->qualifierIndex == qualifierIdx) - return; - } - auto pool = memoryPool(); - QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>(); - import->type = QV4::CompiledData::Import::ImportLibrary; - import->majorVersion = majorVersion; - import->minorVersion = minorVersion; - import->uriIndex = moduleIdx; - import->qualifierIndex = qualifierIdx; - document->imports.append(import); -} - -QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler) - : compiler(typeCompiler) -{ -} - - - -SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , qmlObjects(*typeCompiler->qmlObjects()) - , imports(typeCompiler->imports()) - , customParsers(typeCompiler->customParserCache()) - , illegalNames(typeCompiler->enginePrivate()->v4engine()->illegalNames()) - , propertyCaches(typeCompiler->propertyCaches()) -{ -} - -bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations() -{ - for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) { - const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - QQmlPropertyCache *cache = propertyCaches->at(objectIndex); - if (!cache) - continue; - if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) { - if (!(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) - continue; - } - const QString elementName = stringAt(obj->inheritedTypeNameIndex); - if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache)) - return false; - } - return true; -} - -bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache) -{ - // map from signal name defined in qml itself to list of parameters - QHash<QString, QStringList> customSignals; - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - QString propertyName = stringAt(binding->propertyNameIndex); - // Attached property? - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); - auto *typeRef = resolvedType(binding->propertyNameIndex); - QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (!type.isValid()) { - if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) { - if (type.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } - - const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); - if (!attachedType) - COMPILE_EXCEPTION(binding, tr("Non-existent attached object")); - QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType); - if (!convertSignalHandlerExpressionsToFunctionDeclarations(attachedObj, propertyName, cache)) - return false; - continue; - } - - if (!QmlIR::IRBuilder::isSignalPropertyName(propertyName)) - continue; - - QQmlPropertyResolver resolver(propertyCache); - - Q_ASSERT(propertyName.startsWith(QLatin1String("on"))); - propertyName.remove(0, 2); - - // Note that the property name could start with any alpha or '_' or '$' character, - // so we need to do the lower-casing of the first alpha character. - for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) { - if (propertyName.at(firstAlphaIndex).isUpper()) { - propertyName[firstAlphaIndex] = propertyName.at(firstAlphaIndex).toLower(); - break; - } - } - - QList<QString> parameters; - - bool notInRevision = false; - QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); - if (signal) { - int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex()); - sigIndex = propertyCache->originalClone(sigIndex); - - bool unnamedParameter = false; - - QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex); - for (int i = 0; i < parameterNames.count(); ++i) { - const QString param = QString::fromUtf8(parameterNames.at(i)); - if (param.isEmpty()) - unnamedParameter = true; - else if (unnamedParameter) { - COMPILE_EXCEPTION(binding, tr("Signal uses unnamed parameter followed by named parameter.")); - } else if (illegalNames.contains(param)) { - COMPILE_EXCEPTION(binding, tr("Signal parameter \"%1\" hides global variable.").arg(param)); - } - parameters += param; - } - } else { - if (notInRevision) { - // Try assinging it as a property later - if (resolver.property(propertyName, /*notInRevision ptr*/nullptr)) - continue; - - const QString &originalPropertyName = stringAt(binding->propertyNameIndex); - - auto *typeRef = resolvedType(obj->inheritedTypeNameIndex); - const QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (type.isValid()) { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion())); - } else { - COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName)); - } - } - - // Try to look up the signal parameter names in the object itself - - // build cache if necessary - if (customSignals.isEmpty()) { - for (const QmlIR::Signal *signal = obj->firstSignal(); signal; signal = signal->next) { - const QString &signalName = stringAt(signal->nameIndex); - customSignals.insert(signalName, signal->parameterStringList(compiler->stringPool())); - } - - for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) { - const QString propName = stringAt(property->nameIndex); - customSignals.insert(propName, QStringList()); - } - } - - QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName); - if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) { - QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed"))); - entry = customSignals.constFind(alternateName); - } - - if (entry == customSignals.constEnd()) { - // Can't find even a custom signal, then just don't do anything and try - // keeping the binding as a regular property assignment. - continue; - } - - parameters = entry.value(); - } - - // Binding object to signal means connect the signal to the object's default method. - if (binding->type == QV4::CompiledData::Binding::Type_Object) { - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject; - continue; - } - - if (binding->type != QV4::CompiledData::Binding::Type_Script) { - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - COMPILE_EXCEPTION(binding, tr("Cannot assign a value to a signal (expecting a script to be run)")); - } else { - COMPILE_EXCEPTION(binding, tr("Incorrectly specified signal assignment")); - } - } - - QQmlJS::MemoryPool *pool = compiler->memoryPool(); - - QQmlJS::AST::FormalParameterList *paramList = nullptr; - for (const QString ¶m : qAsConst(parameters)) { - QStringRef paramNameRef = compiler->newStringRef(param); - - QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr); - paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b); - } - - if (paramList) - paramList = paramList->finish(pool); - - QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); - QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr; - if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) { - if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) { - functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body); - functionDeclaration->functionToken = fe->functionToken; - functionDeclaration->identifierToken = fe->identifierToken; - functionDeclaration->lparenToken = fe->lparenToken; - functionDeclaration->rparenToken = fe->rparenToken; - functionDeclaration->lbraceToken = fe->lbraceToken; - functionDeclaration->rbraceToken = fe->rbraceToken; - } - } - if (!functionDeclaration) { - QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node); - QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement); - body = body->finish(); - - functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body); - functionDeclaration->lbraceToken = functionDeclaration->functionToken - = foe->node->firstSourceLocation(); - functionDeclaration->rbraceToken = foe->node->lastSourceLocation(); - } - foe->node = functionDeclaration; - binding->propertyNameIndex = compiler->registerString(propertyName); - binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; - } - return true; -} - -QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , imports(typeCompiler->imports()) -{ -} - -bool QQmlEnumTypeResolver::resolveEnumBindings() -{ - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); - if (!propertyCache) - continue; - const QmlIR::Object *obj = qmlObjects.at(i); - - QQmlPropertyResolver resolver(propertyCache); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - - const QString propertyName = stringAt(binding->propertyNameIndex); - bool notInRevision = false; - QQmlPropertyData *pd = resolver.property(propertyName, ¬InRevision); - if (!pd) - continue; - - if (!pd->isEnum() && pd->propType() != QMetaType::Int) - continue; - - if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding)) - return false; - } - } - - return true; -} - -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &staticQtMetaObject; } -}; - -bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject) -{ - if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) { - COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString())); - } - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue)); -// binding->setNumberValueInternal((double)enumValue); - binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; - return true; -} - -bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) -{ - bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum(); - if (!prop->isEnum() && !isIntProp) - return true; - - if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)) - COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex))); - - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); - const QString string = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - if (!string.constData()->isUpper()) - return true; - - // we support one or two '.' in the enum phrase: - // * <TypeName>.<EnumValue> - // * <TypeName>.<ScopedEnumName>.<EnumValue> - - int dot = string.indexOf(QLatin1Char('.')); - if (dot == -1 || dot == string.length()-1) - return true; - - int dot2 = string.indexOf(QLatin1Char('.'), dot+1); - if (dot2 != -1 && dot2 != string.length()-1) { - if (!string.at(dot+1).isUpper()) - return true; - if (string.indexOf(QLatin1Char('.'), dot2+1) != -1) - return true; - } - - QHashedStringRef typeName(string.constData(), dot); - const bool isQtObject = (typeName == QLatin1String("Qt")); - const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(dot + 1, dot2 - dot - 1) : QStringRef()); - // ### consider supporting scoped enums in Qt namespace - const QStringRef enumValue = string.midRef(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1); - - if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here? - // Allow enum assignment to ints. - bool ok; - int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok); - if (ok) { - if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject)) - return false; - } - return true; - } - QQmlType type; - imports->resolveType(typeName, &type, nullptr, nullptr, nullptr); - - if (!type.isValid() && !isQtObject) - return true; - - int value = 0; - bool ok = false; - - auto *tr = resolvedType(obj->inheritedTypeNameIndex); - if (type.isValid() && tr && tr->type == type) { - // When these two match, we can short cut the search - QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex()); - QMetaEnum menum = mprop.enumerator(); - QByteArray enumName = enumValue.toUtf8(); - if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8()) - return true; - - if (mprop.isFlagType()) { - value = menum.keysToValue(enumName.constData(), &ok); - } else { - value = menum.keyToValue(enumName.constData(), &ok); - } - } else { - // Otherwise we have to search the whole type - if (type.isValid()) { - if (!scopedEnumName.isEmpty()) - value = type.scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok); - else - value = type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok); - } else { - QByteArray enumName = enumValue.toUtf8(); - const QMetaObject *metaObject = StaticQtMetaObject::get(); - for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - value = e.keyToValue(enumName.constData(), &ok); - } - } - } - - if (!ok) - return true; - - return assignEnumToBinding(binding, enumValue, value, isQtObject); -} - -int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const -{ - Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer"); - *ok = false; - - if (scope != QLatin1String("Qt")) { - QQmlType type; - imports->resolveType(scope, &type, nullptr, nullptr, nullptr); - if (!type.isValid()) - return -1; - if (!enumName.isEmpty()) - return type.scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok); - return type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok); - } - - const QMetaObject *mo = StaticQtMetaObject::get(); - int i = mo->enumeratorCount(); - const QByteArray ba = enumValue.toUtf8(); - while (i--) { - int v = mo->enumerator(i).keyToValue(ba.constData(), ok); - if (*ok) - return v; - } - return -1; -} - -QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , customParsers(typeCompiler->customParserCache()) -{ -} - -void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings() -{ - scanObjectRecursively(/*root object*/0); -} - -void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings) -{ - const QmlIR::Object * const obj = qmlObjects.at(objectIndex); - if (!annotateScriptBindings) - annotateScriptBindings = customParsers.contains(obj->inheritedTypeNameIndex); - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - scanObjectRecursively(binding->value.objectIndex, annotateScriptBindings); - continue; - } else if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - if (!annotateScriptBindings) - continue; - const QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - binding->stringIndex = compiler->registerString(script); - } -} - -QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) -{ -} - -void QQmlAliasAnnotator::annotateBindingsToAliases() -{ - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); - if (!propertyCache) - continue; - - const QmlIR::Object *obj = qmlObjects.at(i); - - QQmlPropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (!binding->isValueBinding()) - continue; - bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; - if (pd && pd->isAlias()) - binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias; - } - } -} - -QQmlScriptStringScanner::QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) -{ - -} - -void QQmlScriptStringScanner::scan() -{ - const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>(); - for (int i = 0; i < qmlObjects.count(); ++i) { - QQmlPropertyCache *propertyCache = propertyCaches->at(i); - if (!propertyCache) - continue; - - const QmlIR::Object *obj = qmlObjects.at(i); - - QQmlPropertyResolver resolver(propertyCache); - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - bool notInRevision = false; - QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), ¬InRevision) : defaultProperty; - if (!pd || pd->propType() != scriptStringMetaType) - continue; - - QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); - binding->stringIndex = compiler->registerString(script); - } - } -} - -QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , enginePrivate(typeCompiler->enginePrivate()) - , pool(typeCompiler->memoryPool()) - , qmlObjects(typeCompiler->qmlObjects()) - , propertyCaches(std::move(typeCompiler->takePropertyCaches())) -{ -} - -static bool isUsableComponent(const QMetaObject *metaObject) -{ - // The metaObject is a component we're interested in if it either is a QQmlComponent itself - // or if any of its parents is a QQmlAbstractDelegateComponent. We don't want to include - // qqmldelegatecomponent_p.h because it belongs to QtQmlModels. - - if (metaObject == &QQmlComponent::staticMetaObject) - return true; - - for (; metaObject; metaObject = metaObject->superClass()) { - if (qstrcmp(metaObject->className(), "QQmlAbstractDelegateComponent") == 0) - return true; - } - - return false; -} - -void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache) -{ - QQmlPropertyResolver propertyResolver(propertyCache); - - QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty(); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) - continue; - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex); - auto *tr = resolvedType(targetObject->inheritedTypeNameIndex); - Q_ASSERT(tr); - - const QMetaObject *firstMetaObject = nullptr; - if (tr->type.isValid()) - firstMetaObject = tr->type.metaObject(); - else if (tr->compilationUnit) - firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject(); - if (isUsableComponent(firstMetaObject)) - continue; - // if here, not a QQmlComponent, so needs wrapping - - QQmlPropertyData *pd = nullptr; - if (binding->propertyNameIndex != quint32(0)) { - bool notInRevision = false; - pd = propertyResolver.property(stringAt(binding->propertyNameIndex), ¬InRevision); - } else { - pd = defaultProperty; - } - if (!pd || !pd->isQObject()) - continue; - - QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), pd->typeMinorVersion()); - const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr; - while (mo) { - if (mo == &QQmlComponent::staticMetaObject) - break; - mo = mo->superClass(); - } - - if (!mo) - continue; - - // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}" - QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject); - Q_ASSERT(componentType.isValid()); - const QString qualifier = QStringLiteral("QmlInternals"); - - compiler->addImport(componentType.module(), qualifier, componentType.majorVersion(), componentType.minorVersion()); - - QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); - syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), compiler->registerString(QString())); - syntheticComponent->location = binding->valueLocation; - syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; - - if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) { - auto typeRef = new QV4::ResolvedTypeReference; - typeRef->type = componentType; - typeRef->majorVersion = componentType.majorVersion(); - typeRef->minorVersion = componentType.minorVersion(); - insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef); - } - - qmlObjects->append(syntheticComponent); - const int componentIndex = qmlObjects->count() - 1; - // Keep property caches symmetric - QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject); - propertyCaches.append(componentCache); - - QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>(); - *syntheticBinding = *binding; - syntheticBinding->type = QV4::CompiledData::Binding::Type_Object; - QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false); - Q_ASSERT(error.isEmpty()); - Q_UNUSED(error); - - binding->value.objectIndex = componentIndex; - - componentRoots.append(componentIndex); - } -} - -bool QQmlComponentAndAliasResolver::resolve() -{ - // Detect real Component {} objects as well as implicitly defined components, such as - // someItemDelegate: Item {} - // In the implicit case Item is surrounded by a synthetic Component {} because the property - // on the left hand side is of QQmlComponent type. - const int objCountWithoutSynthesizedComponents = qmlObjects->count(); - for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { - QmlIR::Object *obj = qmlObjects->at(i); - QQmlPropertyCache *cache = propertyCaches.at(i); - if (obj->inheritedTypeNameIndex == 0 && !cache) - continue; - - bool isExplicitComponent = false; - - if (obj->inheritedTypeNameIndex) { - auto *tref = resolvedType(obj->inheritedTypeNameIndex); - Q_ASSERT(tref); - if (tref->type.metaObject() == &QQmlComponent::staticMetaObject) - isExplicitComponent = true; - } - if (!isExplicitComponent) { - if (cache) - findAndRegisterImplicitComponents(obj, cache); - continue; - } - - obj->flags |= QV4::CompiledData::Object::IsComponent; - - if (obj->functionCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); - if (obj->propertyCount() > 0 || obj->aliasCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); - if (obj->signalCount() > 0) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); - - if (obj->bindingCount() == 0) - COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); - - const QmlIR::Binding *rootBinding = obj->firstBinding(); - - for (const QmlIR::Binding *b = rootBinding; b; b = b->next) { - if (b->propertyNameIndex != 0) - COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); - } - - if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) - COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - - // For the root object, we are going to collect ids/aliases and resolve them for as a separate - // last pass. - if (i != 0) - componentRoots.append(i); - - } - - for (int i = 0; i < componentRoots.count(); ++i) { - QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); - const QmlIR::Binding *rootBinding = component->firstBinding(); - - _idToObjectIndex.clear(); - - _objectsWithAliases.clear(); - - if (!collectIdsAndAliases(rootBinding->value.objectIndex)) - return false; - - component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); - - if (!resolveAliases(componentRoots.at(i))) - return false; - } - - // Collect ids and aliases for root - _idToObjectIndex.clear(); - _objectsWithAliases.clear(); - - collectIdsAndAliases(/*root object*/0); - - QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0); - rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); - - if (!resolveAliases(/*root object*/0)) - return false; - - // Implicit component insertion may have added objects and thus we also need - // to extend the symmetric propertyCaches. - compiler->setPropertyCaches(std::move(propertyCaches)); - compiler->setComponentRoots(componentRoots); - - return true; -} - -bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) -{ - QmlIR::Object *obj = qmlObjects->at(objectIndex); - - if (obj->idNameIndex != 0) { - if (_idToObjectIndex.contains(obj->idNameIndex)) { - recordError(obj->locationOfIdProperty, tr("id is not unique")); - return false; - } - obj->id = _idToObjectIndex.count(); - _idToObjectIndex.insert(obj->idNameIndex, objectIndex); - } - - if (obj->aliasCount() > 0) - _objectsWithAliases.append(objectIndex); - - // Stop at Component boundary - if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) - return true; - - for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Object - && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty - && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) - continue; - - if (!collectIdsAndAliases(binding->value.objectIndex)) - return false; - } - - return true; -} - -bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) -{ - if (_objectsWithAliases.isEmpty()) - return true; - - QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler); - - bool atLeastOneAliasResolved; - do { - atLeastOneAliasResolved = false; - QVector<int> pendingObjects; - - for (int objectIndex: qAsConst(_objectsWithAliases)) { - - QQmlCompileError error; - const auto result = resolveAliasesInObject(objectIndex, &error); - - if (error.isSet()) { - recordError(error); - return false; - } - - if (result == AllAliasesResolved) { - QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); - if (error.isSet()) { - recordError(error); - return false; - } - atLeastOneAliasResolved = true; - } else if (result == SomeAliasesResolved) { - atLeastOneAliasResolved = true; - pendingObjects.append(objectIndex); - } else { - pendingObjects.append(objectIndex); - } - } - qSwap(_objectsWithAliases, pendingObjects); - } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved); - - if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) { - const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first()); - for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) { - if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) { - recordError(alias->location, tr("Circular alias reference detected")); - return false; - } - } - } - - return true; -} - -QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error) -{ - const QmlIR::Object * const obj = qmlObjects->at(objectIndex); - if (!obj->aliasCount()) - return AllAliasesResolved; - - int numResolvedAliases = 0; - bool seenUnresolvedAlias = false; - - for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) { - if (alias->flags & QV4::CompiledData::Alias::Resolved) - continue; - - seenUnresolvedAlias = true; - - const int idIndex = alias->idIndex; - const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); - if (targetObjectIndex == -1) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); - break; - } - - const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); - Q_ASSERT(targetObject->id >= 0); - alias->targetObjectId = targetObject->id; - alias->aliasToLocalAlias = false; - - const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); - - QStringRef property; - QStringRef subProperty; - - const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.')); - if (propertySeparator != -1) { - property = aliasPropertyValue.leftRef(propertySeparator); - subProperty = aliasPropertyValue.midRef(propertySeparator + 1); - } else - property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length()); - - QQmlPropertyIndex propIdx; - - if (property.isEmpty()) { - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; - } else { - QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); - if (!targetCache) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); - break; - } - - QQmlPropertyResolver resolver(targetCache); - - QQmlPropertyData *targetProperty = resolver.property(property.toString()); - - // If it's an alias that we haven't resolved yet, try again later. - if (!targetProperty) { - bool aliasPointsToOtherAlias = false; - int localAliasIndex = 0; - for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) { - if (stringAt(targetAlias->nameIndex) == property) { - aliasPointsToOtherAlias = true; - break; - } - } - if (aliasPointsToOtherAlias) { - if (targetObjectIndex == objectIndex) { - alias->localAliasIndex = localAliasIndex; - alias->aliasToLocalAlias = true; - alias->flags |= QV4::CompiledData::Alias::Resolved; - ++numResolvedAliases; - continue; - } - - // restore - alias->idIndex = idIndex; - // Try again later and resolve the target alias first. - break; - } - } - - if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); - break; - } - - propIdx = QQmlPropertyIndex(targetProperty->coreIndex()); - - if (!subProperty.isEmpty()) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); - if (!valueTypeMetaObject) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } - - int valueTypeIndex = - valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); - if (valueTypeIndex == -1) { - *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - - propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); - } else { - if (targetProperty->isQObject()) - alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; - } - } - - alias->encodedMetaPropertyIndex = propIdx.toEncoded(); - alias->flags |= QV4::CompiledData::Alias::Resolved; - numResolvedAliases++; - } - - if (numResolvedAliases == 0) - return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved; - - return SomeAliasesResolved; -} - -QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , customParsers(typeCompiler->customParserCache()) - , _seenObjectWithId(false) -{ -} - -bool QQmlDeferredAndCustomParserBindingScanner::scanObject() -{ - return scanObject(/*root object*/0); -} - -bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) -{ - QmlIR::Object *obj = qmlObjects->at(objectIndex); - if (obj->idNameIndex != 0) - _seenObjectWithId = true; - - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - return scanObject(componentBinding->value.objectIndex); - } - - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); - if (!propertyCache) - return true; - - QString defaultPropertyName; - QQmlPropertyData *defaultProperty = nullptr; - if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); - defaultPropertyName = cache->defaultPropertyName(); - defaultProperty = cache->defaultProperty(); - } else { - defaultPropertyName = propertyCache->defaultPropertyName(); - defaultProperty = propertyCache->defaultProperty(); - } - - QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); - - QQmlPropertyResolver propertyResolver(propertyCache); - - QStringList deferredPropertyNames; - { - const QMetaObject *mo = propertyCache->firstCppMetaObject(); - const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - } - } - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - QQmlPropertyData *pd = nullptr; - QString name = stringAt(binding->propertyNameIndex); - - if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - continue; - } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) - && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - continue; - } - } - - if (name.isEmpty()) { - pd = defaultProperty; - name = defaultPropertyName; - } else { - if (name.constData()->isUpper()) - continue; - - bool notInRevision = false; - pd = propertyResolver.property(name, ¬InRevision, - QQmlPropertyResolver::CheckRevision); - } - - bool seenSubObjectWithId = false; - - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - qSwap(_seenObjectWithId, seenSubObjectWithId); - const bool subObjectValid = scanObject(binding->value.objectIndex); - qSwap(_seenObjectWithId, seenSubObjectWithId); - if (!subObjectValid) - return false; - _seenObjectWithId |= seenSubObjectWithId; - } - - if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty - && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { - - binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; - obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; - } - - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) - continue; - - if (!pd) { - if (customParser) { - obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; - binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; - } - } - } - - return true; -} - -QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) - : QQmlCompilePass(typeCompiler) - , customParsers(typeCompiler->customParserCache()) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) - , v4CodeGen(v4CodeGen) -{ -} - -bool QQmlJSCodeGenerator::generateCodeForComponents() -{ - const QVector<quint32> &componentRoots = compiler->componentRoots(); - for (int i = 0; i < componentRoots.count(); ++i) { - if (!compileComponent(componentRoots.at(i))) - return false; - } - - return compileComponent(/*root object*/0); -} - -bool QQmlJSCodeGenerator::compileComponent(int contextObject) -{ - const QmlIR::Object *obj = qmlObjects.at(contextObject); - if (obj->flags & QV4::CompiledData::Object::IsComponent) { - Q_ASSERT(obj->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); - contextObject = componentBinding->value.objectIndex; - } - - if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) - return false; - - return true; -} - -bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) -{ - QmlIR::Object *object = qmlObjects.at(objectIndex); - if (object->flags & QV4::CompiledData::Object::IsComponent) - return true; - - if (object->functionsAndExpressions->count > 0) { - QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) - functionsToCompile << *foe; - const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); - const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors(); - if (!jsErrors.isEmpty()) { - for (const QQmlError &e : jsErrors) - compiler->recordError(e); - return false; - } - - QQmlJS::MemoryPool *pool = compiler->memoryPool(); - object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices); - } - - for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { - if (binding->type < QV4::CompiledData::Binding::Type_Object) - continue; - - int target = binding->value.objectIndex; - int scope = binding->type == QV4::CompiledData::Binding::Type_Object ? target : scopeObjectIndex; - - if (!compileJavaScriptCodeInObjectsRecursively(binding->value.objectIndex, scope)) - return false; - } - - return true; -} - -QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , propertyCaches(typeCompiler->propertyCaches()) -{ - -} - -void QQmlDefaultPropertyMerger::mergeDefaultProperties() -{ - for (int i = 0; i < qmlObjects.count(); ++i) - mergeDefaultProperties(i); -} - -void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) -{ - QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex); - if (!propertyCache) - return; - - QmlIR::Object *object = qmlObjects.at(objectIndex); - - QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName(); - QmlIR::Binding *bindingsToReinsert = nullptr; - QmlIR::Binding *tail = nullptr; - - QmlIR::Binding *previousBinding = nullptr; - QmlIR::Binding *binding = object->firstBinding(); - while (binding) { - if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) { - previousBinding = binding; - binding = binding->next; - continue; - } - - QmlIR::Binding *toReinsert = binding; - binding = object->unlinkBinding(previousBinding, binding); - - if (!tail) { - bindingsToReinsert = toReinsert; - tail = toReinsert; - } else { - tail->next = toReinsert; - tail = tail->next; - } - tail->next = nullptr; - } - - binding = bindingsToReinsert; - while (binding) { - QmlIR::Binding *toReinsert = binding; - binding = binding->next; - object->insertSorted(toReinsert); - } -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h deleted file mode 100644 index f588909c42..0000000000 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ /dev/null @@ -1,347 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLTYPECOMPILER_P_H -#define QQMLTYPECOMPILER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qglobal.h> -#include <qqmlerror.h> -#include <qhash.h> -#include <private/qqmltypeloader_p.h> -#include <private/qqmlirbuilder_p.h> -#include <private/qqmlpropertycachecreator_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlEnginePrivate; -class QQmlError; -class QQmlTypeData; -class QQmlImports; - -namespace QmlIR { -struct Document; -} - -namespace QV4 { -namespace CompiledData { -struct QmlUnit; -struct Location; -} -} - -struct QQmlTypeCompiler -{ - Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) -public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, - const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, - const QV4::CompiledData::DependentTypesHasher &dependencyHasher); - - // --- interface used by QQmlPropertyCacheCreator - typedef QmlIR::Object CompiledObject; - const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); } - int objectCount() const { return document->objects.count(); } - QString stringAt(int idx) const; - QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } - QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } - QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr; - // --- - - QQmlRefPointer<QV4::ExecutableCompilationUnit> compile(); - - QList<QQmlError> compilationErrors() const { return errors; } - void recordError(QQmlError error); - void recordError(const QV4::CompiledData::Location &location, const QString &description); - void recordError(const QQmlCompileError &error); - - int registerString(const QString &str); - int registerConstant(QV4::ReturnedValue v); - - const QV4::CompiledData::Unit *qmlUnit() const; - - QUrl url() const { return typeData->finalUrl(); } - QQmlEnginePrivate *enginePrivate() const { return engine; } - const QQmlImports *imports() const; - QVector<QmlIR::Object *> *qmlObjects() const; - void setPropertyCaches(QQmlPropertyCacheVector &&caches); - const QQmlPropertyCacheVector *propertyCaches() const; - QQmlPropertyCacheVector &&takePropertyCaches(); - void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; } - const QVector<quint32> &componentRoots() const { return m_componentRoots; } - QQmlJS::MemoryPool *memoryPool(); - QStringRef newStringRef(const QString &string); - const QV4::Compiler::StringTableGenerator *stringPool() const; - - const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; } - - QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const; - - void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion); - - QV4::ResolvedTypeReference *resolvedType(int id) const - { - return resolvedTypes->value(id); - } - -private: - QList<QQmlError> errors; - QQmlEnginePrivate *engine; - QQmlTypeData *typeData; - const QV4::CompiledData::DependentTypesHasher &dependencyHasher; - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - QmlIR::Document *document; - // index is string index of type name (use obj->inheritedTypeNameIndex) - QHash<int, QQmlCustomParser*> customParsers; - - // index in first hash is component index, vector inside contains object indices of objects with id property - QVector<quint32> m_componentRoots; - QQmlPropertyCacheVector m_propertyCaches; -}; - -struct QQmlCompilePass -{ - QQmlCompilePass(QQmlTypeCompiler *typeCompiler); - - QString stringAt(int idx) const { return compiler->stringAt(idx); } -protected: - void recordError(const QV4::CompiledData::Location &location, const QString &description) const - { compiler->recordError(location, description); } - void recordError(const QQmlCompileError &error) - { compiler->recordError(error); } - - QV4::ResolvedTypeReference *resolvedType(int id) const - { return compiler->resolvedType(id); } - bool containsResolvedType(int id) const - { return compiler->resolvedTypes->contains(id); } - QV4::ResolvedTypeReferenceMap::iterator insertResolvedType( - int id, QV4::ResolvedTypeReference *value) - { return compiler->resolvedTypes->insert(id, value); } - - QQmlTypeCompiler *compiler; -}; - -// "Converts" signal expressions to full-fleged function declarations with -// parameters taken from the signal declarations -// It also updates the QV4::CompiledData::Binding objects to set the property name -// to the final signal name (onTextChanged -> textChanged) and sets the IsSignalExpression flag. -struct SignalHandlerConverter : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(SignalHandlerConverter) -public: - SignalHandlerConverter(QQmlTypeCompiler *typeCompiler); - - bool convertSignalHandlerExpressionsToFunctionDeclarations(); - -private: - bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache); - - QQmlEnginePrivate *enginePrivate; - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlImports *imports; - const QHash<int, QQmlCustomParser*> &customParsers; - const QSet<QString> &illegalNames; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -// ### This will go away when the codegen resolves all enums to constant expressions -// and we replace the constant expression with a literal binding instead of using -// a script. -class QQmlEnumTypeResolver : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlEnumTypeResolver) -public: - QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler); - - bool resolveEnumBindings(); - -private: - bool assignEnumToBinding(QmlIR::Binding *binding, const QStringRef &enumName, int enumValue, bool isQtObject); - bool assignEnumToBinding(QmlIR::Binding *binding, const QString &enumName, int enumValue, bool isQtObject) - { - return assignEnumToBinding(binding, QStringRef(&enumName), enumValue, isQtObject); - } - bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, - const QQmlPropertyData *prop, - QmlIR::Binding *binding); - int evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const; - - - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - const QQmlImports *imports; -}; - -class QQmlCustomParserScriptIndexer: public QQmlCompilePass -{ -public: - QQmlCustomParserScriptIndexer(QQmlTypeCompiler *typeCompiler); - - void annotateBindingsWithScriptStrings(); - -private: - void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false); - - const QVector<QmlIR::Object*> &qmlObjects; - const QHash<int, QQmlCustomParser*> &customParsers; -}; - -// Annotate properties bound to aliases with a flag -class QQmlAliasAnnotator : public QQmlCompilePass -{ -public: - QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler); - - void annotateBindingsToAliases(); -private: - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -class QQmlScriptStringScanner : public QQmlCompilePass -{ -public: - QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler); - - void scan(); - -private: - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -class QQmlComponentAndAliasResolver : public QQmlCompilePass -{ - Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) -public: - QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler); - - bool resolve(); - -protected: - void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache); - bool collectIdsAndAliases(int objectIndex); - bool resolveAliases(int componentIndex); - void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags); - - enum AliasResolutionResult { - NoAliasResolved, - SomeAliasesResolved, - AllAliasesResolved - }; - - AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error); - - QQmlEnginePrivate *enginePrivate; - QQmlJS::MemoryPool *pool; - - QVector<QmlIR::Object*> *qmlObjects; - - // indices of the objects that are actually Component {} - QVector<quint32> componentRoots; - - // Deliberate choice of map over hash here to ensure stable generated output. - QMap<int, int> _idToObjectIndex; - QVector<int> _objectsWithAliases; - - QQmlPropertyCacheVector propertyCaches; -}; - -class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass -{ -public: - QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); - - bool scanObject(); - -private: - bool scanObject(int objectIndex); - - QVector<QmlIR::Object*> *qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - const QHash<int, QQmlCustomParser*> &customParsers; - - bool _seenObjectWithId; -}; - -// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone. -class QQmlJSCodeGenerator : public QQmlCompilePass -{ -public: - QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen); - - bool generateCodeForComponents(); - -private: - bool compileComponent(int componentRoot); - bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - - const QHash<int, QQmlCustomParser*> &customParsers; - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - QmlIR::JSCodeGen * const v4CodeGen; -}; - -class QQmlDefaultPropertyMerger : public QQmlCompilePass -{ -public: - QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler); - - void mergeDefaultProperties(); - -private: - void mergeDefaultProperties(int objectIndex); - - const QVector<QmlIR::Object*> &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -QT_END_NAMESPACE - -#endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qqmlirloader_p.h b/src/qml/compiler/qv4alloca_p.h index aa303c923f..65c3e4d65a 100644 --- a/src/qml/compiler/qqmlirloader_p.h +++ b/src/qml/compiler/qv4alloca_p.h @@ -1,9 +1,9 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the tools applications of the Qt Toolkit. +** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLIRLOADER_P_H -#define QQMLIRLOADER_P_H +#ifndef QV4_ALLOCA_H +#define QV4_ALLOCA_H // // W A R N I N G @@ -51,31 +51,58 @@ // We mean it. // -#include <private/qv4compileddata_p.h> -#include <private/qqmljsmemorypool_p.h> +#include <QtCore/private/qglobal_p.h> -QT_BEGIN_NAMESPACE +#if QT_CONFIG(alloca_h) +# include <alloca.h> +#elif QT_CONFIG(alloca_malloc_h) +# include <malloc.h> +// This does not matter unless compiling in strict standard mode. +# ifdef Q_CC_MSVC +# define alloca _alloca +# endif +#else +# include <stdlib.h> +#endif + +// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing +// the occurrences of alloca() in case it's not supported. +// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate +// memory allocation from the declaration and RAII. +#define Q_ALLOCA_VAR(type, name, size) \ + Q_ALLOCA_DECLARE(type, name); \ + Q_ALLOCA_ASSIGN(type, name, size) -namespace QmlIR { -struct Document; -struct Object; -} +#if QT_CONFIG(alloca) -struct Q_QML_PRIVATE_EXPORT QQmlIRLoader { - QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 - void load(); +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast<type*>(alloca(size)) +#else +QT_BEGIN_NAMESPACE +class Qt_AllocaWrapper +{ +public: + Qt_AllocaWrapper() { m_data = 0; } + ~Qt_AllocaWrapper() { free(m_data); } + void *data() { return m_data; } + void allocate(int size) { m_data = malloc(size); memset(m_data, 0, size); } private: - QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + void *m_data; +}; +QT_END_NAMESPACE - template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = nullptr - const QV4::CompiledData::Unit *unit; - QmlIR::Document *output; - QQmlJS::MemoryPool *pool; -}; +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast<type*>(_qt_alloca_##name.data()) -QT_END_NAMESPACE +#endif -#endif // QQMLIRLOADER_P_H +#endif diff --git a/src/qml/compiler/qqmlpropertyresolver_p.h b/src/qml/compiler/qv4calldata_p.h index df857f242e..5a5280cb86 100644 --- a/src/qml/compiler/qqmlpropertyresolver_p.h +++ b/src/qml/compiler/qv4calldata_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the tools applications of the Qt Toolkit. +** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLPROPERTYRESOLVER_P_H -#define QQMLPROPERTYRESOLVER_P_H +#ifndef QV4CALLDATA_P_H +#define QV4CALLDATA_P_H // // W A R N I N G @@ -51,37 +51,73 @@ // We mean it. // -#include <private/qtqmlglobal_p.h> -#include <private/qqmlpropertycache_p.h> -#include <private/qqmlrefcount_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE -struct Q_QML_EXPORT QQmlPropertyResolver +namespace QV4 { + +struct CallData { - QQmlPropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache) - : cache(cache) - {} + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + NewTarget = 4, + Argc = 5, - QQmlPropertyData *property(int index) const - { - return cache->property(index); + LastOffset = Argc, + OffsetCount = LastOffset + 1 + }; + + StaticValue function; + StaticValue context; + StaticValue accumulator; + StaticValue thisObject; + StaticValue newTarget; + StaticValue _argc; + + int argc() const { + Q_ASSERT(_argc.isInteger()); + return _argc.int_32(); } - enum RevisionCheck { - CheckRevision, - IgnoreRevision - }; + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } - QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, - RevisionCheck check = CheckRevision) const; + inline ReturnedValue argument(int i) const { + return i < argc() ? args[i].asReturnedValue() + : StaticValue::undefinedValue().asReturnedValue(); + } + + StaticValue args[1]; - // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; + static Q_DECL_CONSTEXPR int HeaderSize() + { + return offsetof(CallData, args) / sizeof(QV4::StaticValue); + } - QQmlRefPointer<QQmlPropertyCache> cache; + template<typename Value> + Value *argValues(); + + template<typename Value> + const Value *argValues() const; }; +Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); +Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(StaticValue)); + +} // namespace QV4 + QT_END_NAMESPACE -#endif // QQMLPROPERTYRESOLVER_P_H +#endif // QV4CALLDATA_P_H diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp deleted file mode 100644 index 350f6f9485..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compilationunitmapper_p.h" - -#include "qv4compileddata_p.h" -#include <QFileInfo> -#include <QDateTime> -#include <QCoreApplication> - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompilationUnitMapper::CompilationUnitMapper() - : dataPtr(nullptr) -{ - -} - -CompilationUnitMapper::~CompilationUnitMapper() -{ - close(); -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp deleted file mode 100644 index 6768bc9596..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compilationunitmapper_p.h" - -#include <sys/mman.h> -#include <functional> -#include <private/qcore_unix_p.h> -#include <QScopeGuard> -#include <QDateTime> - -#include "qv4compileddata_p.h" - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString) -{ - close(); - - int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY); - if (fd == -1) { - *errorString = qt_error_string(errno); - return nullptr; - } - - auto cleanup = qScopeGuard([fd]{ - qt_safe_close(fd) ; - }); - - CompiledData::Unit header; - qint64 bytesRead = qt_safe_read(fd, reinterpret_cast<char *>(&header), sizeof(header)); - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return nullptr; - } - - if (!header.verifyHeader(sourceTimeStamp, errorString)) - return nullptr; - - // Data structure and qt version matched, so now we can access the rest of the file safely. - - length = static_cast<size_t>(lseek(fd, 0, SEEK_END)); - - void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0); - if (ptr == MAP_FAILED) { - *errorString = qt_error_string(errno); - return nullptr; - } - dataPtr = ptr; - - return reinterpret_cast<CompiledData::Unit*>(dataPtr); -} - -void CompilationUnitMapper::close() -{ - // Do not unmap the data here. - if (dataPtr != nullptr) { - // Do not unmap cache files that are built with the StaticData flag. That's the majority of - // them and it's necessary to benefit from the QString literal optimization. There might - // still be QString instances around that point into that memory area. The memory is backed - // on the disk, so the kernel is free to release the pages and all that remains is the - // address space allocation. - if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData)) - munmap(dataPtr, length); - } - dataPtr = nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp deleted file mode 100644 index 779c1288fe..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compilationunitmapper_p.h" - -#include "qv4compileddata_p.h" -#include <QScopeGuard> -#include <QFileInfo> -#include <QDateTime> -#include <qt_windows.h> - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString) -{ - close(); - - // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry - // is exported from QtCore. - HANDLE handle = -#if defined(Q_OS_WINRT) - CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), - GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, - OPEN_EXISTING, nullptr); -#else - CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), - GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, - nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - nullptr); -#endif - if (handle == INVALID_HANDLE_VALUE) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - auto fileHandleCleanup = qScopeGuard([handle]{ - CloseHandle(handle); - }); - - CompiledData::Unit header; - DWORD bytesRead; - if (!ReadFile(handle, reinterpret_cast<char *>(&header), sizeof(header), &bytesRead, nullptr)) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - if (bytesRead != sizeof(header)) { - *errorString = QStringLiteral("File too small for the header fields"); - return nullptr; - } - - if (!header.verifyHeader(sourceTimeStamp, errorString)) - return nullptr; - - // Data structure and qt version matched, so now we can access the rest of the file safely. - - HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0); - if (!fileMappingHandle) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - auto mappingCleanup = qScopeGuard([fileMappingHandle]{ - CloseHandle(fileMappingHandle); - }); - - dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0); - if (!dataPtr) { - *errorString = qt_error_string(GetLastError()); - return nullptr; - } - - return reinterpret_cast<CompiledData::Unit*>(dataPtr); -} - -void CompilationUnitMapper::close() -{ - if (dataPtr != nullptr) { - // Do not unmap cache files that are built with the StaticData flag. That's the majority of - // them and it's necessary to benefit from the QString literal optimization. There might - // still be QString instances around that point into that memory area. The memory is backed - // on the disk, so the kernel is free to release the pages and all that remains is the - // address space allocation. - if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData)) - UnmapViewOfFile(dataPtr); - } - dataPtr = nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 8722263b04..d8f293211e 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -44,9 +44,16 @@ #include <private/qv4alloca_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsast_p.h> -#include <wtf/MathExtras.h> #include <QCryptographicHash> +// Efficient implementation that takes advantage of powers of two. +static inline size_t roundUpToMultipleOf(size_t divisor, size_t x) +{ + Q_ASSERT(divisor && !(divisor & (divisor - 1))); + const size_t remainderMask = divisor - 1; + return (x + remainderMask) & ~remainderMask; +} + QV4::Compiler::StringTableGenerator::StringTableGenerator() { clear(); @@ -91,7 +98,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast<char *>(unit); quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable); - char *stringData = reinterpret_cast<char *>(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); + char *stringData = reinterpret_cast<char *>(stringTable) + roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); for (int i = backingUnitTableSize ; i < strings.size(); ++i) { const int index = i - backingUnitTableSize; stringTable[index] = stringData - dataStart; @@ -393,7 +400,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; - quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*function))); + quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*function))); function->nameIndex = getStringId(irFunction->name); function->flags = 0; @@ -543,7 +550,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context { QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); - quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*block))); + quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*block))); block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone; block->nLocals = irBlock->locals.size(); @@ -606,7 +613,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.constantTableSize = constants.size(); // Ensure we load constants from well-aligned addresses into for example SSE registers. - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(16, nextOffset)); unit.offsetToConstantTable = nextOffset; nextOffset += unit.constantTableSize * sizeof(ReturnedValue); @@ -617,19 +624,19 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp *jsClassDataOffset = nextOffset; nextOffset += jsClassData.size(); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.translationTableSize = translations.count(); unit.offsetToTranslationTable = nextOffset; nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) { *tableSizePtr = count; *offsetPtr = nextOffset; nextOffset += count * sizeof(CompiledData::ExportEntry); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); }; reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable); @@ -639,12 +646,12 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.importEntryTableSize = module->importEntries.count(); unit.offsetToImportEntryTable = nextOffset; nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.moduleRequestTableSize = module->moduleRequests.count(); unit.offsetToModuleRequestTable = nextOffset; nextOffset += unit.moduleRequestTableSize * sizeof(uint); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); quint32 functionSize = 0; for (int i = 0; i < module->functions.size(); ++i) { @@ -684,7 +691,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp if (option == GenerateWithStringTable) { unit.stringTableSize = stringTable.stringCount(); - nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); unit.offsetToStringTable = nextOffset; nextOffset += stringTable.sizeOfTableAndData(); } else { diff --git a/src/qml/compiler/qv4executablecompilationunit.cpp b/src/qml/compiler/qv4executablecompilationunit.cpp deleted file mode 100644 index c68f6a7cbf..0000000000 --- a/src/qml/compiler/qv4executablecompilationunit.cpp +++ /dev/null @@ -1,809 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4executablecompilationunit_p.h" - -#include <private/qv4engine_p.h> -#include <private/qv4regexp_p.h> -#include <private/qv4lookup_p.h> -#include <private/qv4qmlcontext_p.h> -#include <private/qv4identifiertable_p.h> -#include <private/qv4instr_moth_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qqmlengine_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <private/qqmlvaluetypewrapper_p.h> -#include <private/qv4module_p.h> -#include <private/qv4compilationunitmapper_p.h> - -#include <QtQml/qqmlfile.h> -#include <QtQml/qqmlpropertymap.h> - -#include <QtCore/qdir.h> -#include <QtCore/qstandardpaths.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qscopeguard.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -ExecutableCompilationUnit::ExecutableCompilationUnit() = default; - -ExecutableCompilationUnit::ExecutableCompilationUnit( - CompiledData::CompilationUnit &&compilationUnit) - : CompiledData::CompilationUnit(std::move(compilationUnit)) -{} - -ExecutableCompilationUnit::~ExecutableCompilationUnit() -{ - unlink(); -} - -QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url) -{ - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; -} - -static QString toString(QV4::ReturnedValue v) -{ - Value val = Value::fromReturnedValue(v); - QString result; - if (val.isInt32()) - result = QLatin1String("int "); - else if (val.isDouble()) - result = QLatin1String("double "); - if (val.isEmpty()) - result += QLatin1String("empty"); - else - result += val.toQStringNoThrow(); - return result; -} - -static void dumpConstantTable(const StaticValue *constants, uint count) -{ - QDebug d = qDebug(); - d.nospace() << right; - for (uint i = 0; i < count; ++i) { - d << qSetFieldWidth(8) << i << qSetFieldWidth(0) << ": " - << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n"; - } -} - -QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) -{ - this->engine = engine; - engine->compilationUnits.insert(this); - - Q_ASSERT(!runtimeStrings); - Q_ASSERT(data); - const quint32 stringCount = totalStringCount(); - runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*)); - // memset the strings to 0 in case a GC run happens while we're within the loop below - memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*)); - for (uint i = 0; i < stringCount; ++i) - runtimeStrings[i] = engine->newString(stringAt(i)); - - runtimeRegularExpressions - = new QV4::Value[data->regexpTableSize]; - // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeRegularExpressions, 0, - data->regexpTableSize * sizeof(QV4::Value)); - for (uint i = 0; i < data->regexpTableSize; ++i) { - const CompiledData::RegExp *re = data->regexpAt(i); - uint f = re->flags; - const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f); - runtimeRegularExpressions[i] = QV4::RegExp::create( - engine, stringAt(re->stringIndex), flags); - } - - if (data->lookupTableSize) { - runtimeLookups = new QV4::Lookup[data->lookupTableSize]; - memset(runtimeLookups, 0, data->lookupTableSize * sizeof(QV4::Lookup)); - const CompiledData::Lookup *compiledLookups = data->lookupTable(); - for (uint i = 0; i < data->lookupTableSize; ++i) { - QV4::Lookup *l = runtimeLookups + i; - - CompiledData::Lookup::Type type - = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags)); - if (type == CompiledData::Lookup::Type_Getter) - l->getter = QV4::Lookup::getterGeneric; - else if (type == CompiledData::Lookup::Type_Setter) - l->setter = QV4::Lookup::setterGeneric; - else if (type == CompiledData::Lookup::Type_GlobalGetter) - l->globalGetter = QV4::Lookup::globalGetterGeneric; - else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) - l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - l->nameIndex = compiledLookups[i].nameIndex; - } - } - - if (data->jsClassTableSize) { - runtimeClasses - = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize - * sizeof(QV4::Heap::InternalClass *)); - // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeClasses, 0, - data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); - for (uint i = 0; i < data->jsClassTableSize; ++i) { - int memberCount = 0; - const CompiledData::JSClassMember *member - = data->jsClassAt(i, &memberCount); - runtimeClasses[i] - = engine->internalClasses(QV4::ExecutionEngine::Class_Object); - for (int j = 0; j < memberCount; ++j, ++member) - runtimeClasses[i] - = runtimeClasses[i]->addMember( - engine->identifierTable->asPropertyKey( - runtimeStrings[member->nameOffset]), - member->isAccessor - ? QV4::Attr_Accessor - : QV4::Attr_Data); - } - } - - runtimeFunctions.resize(data->functionTableSize); - for (int i = 0 ;i < runtimeFunctions.size(); ++i) { - const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction); - } - - Scope scope(engine); - Scoped<InternalClass> ic(scope); - - runtimeBlocks.resize(data->blockTableSize); - for (int i = 0 ;i < runtimeBlocks.size(); ++i) { - const QV4::CompiledData::Block *compiledBlock = data->blockAt(i); - ic = engine->internalClasses(EngineBase::Class_CallContext); - - // first locals - const quint32_le *localsIndices = compiledBlock->localsTable(); - for (quint32 j = 0; j < compiledBlock->nLocals; ++j) - ic = ic->addMember( - engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]), - Attr_NotConfigurable); - runtimeBlocks[i] = ic->d(); - } - - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); - if (showCode) { - qDebug() << "=== Constant table"; - dumpConstantTable(constants, data->constantTableSize); - qDebug() << "=== String table"; - for (uint i = 0, end = totalStringCount(); i < end; ++i) - qDebug() << " " << i << ":" << runtimeStrings[i]->toQString(); - qDebug() << "=== Closure table"; - for (uint i = 0; i < data->functionTableSize; ++i) - qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString(); - qDebug() << "root function at index " - << (data->indexOfRootFunction != -1 - ? data->indexOfRootFunction : 0); - } - - if (data->indexOfRootFunction != -1) - return runtimeFunctions[data->indexOfRootFunction]; - else - return nullptr; -} - -Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const -{ - Q_ASSERT(index < int(data->templateObjectTableSize)); - if (!templateObjects.size()) - templateObjects.resize(data->templateObjectTableSize); - Heap::Object *o = templateObjects.at(index); - if (o) - return o; - - // create the template object - Scope scope(engine); - const CompiledData::TemplateObject *t = data->templateObjectAt(index); - Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size)); - Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size)); - ScopedValue s(scope); - for (uint i = 0; i < t->size; ++i) { - s = runtimeStrings[t->stringIndexAt(i)]; - a->arraySet(i, s); - s = runtimeStrings[t->rawStringIndexAt(i)]; - raw->arraySet(i, s); - } - - ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1); - a->defineReadonlyProperty(QStringLiteral("raw"), raw); - ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1); - - templateObjects[index] = a->objectValue()->d(); - return templateObjects.at(index); -} - -void ExecutableCompilationUnit::unlink() -{ - if (engine) - nextCompilationUnit.remove(); - - if (isRegisteredWithEngine) { - Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0)); - if (qmlEngine) - qmlEngine->unregisterInternalCompositeType(this); - QQmlMetaType::unregisterInternalCompositeType(this); - isRegisteredWithEngine = false; - } - - propertyCaches.clear(); - - if (runtimeLookups) { - for (uint i = 0; i < data->lookupTableSize; ++i) { - QV4::Lookup &l = runtimeLookups[i]; - if (l.getter == QV4::QObjectWrapper::lookupGetter) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) { - if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) - pc->release(); - } - - if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } - } - } - - dependentScripts.clear(); - - typeNameCache = nullptr; - - qDeleteAll(resolvedTypes); - resolvedTypes.clear(); - - engine = nullptr; - qmlEngine = nullptr; - - delete [] runtimeLookups; - runtimeLookups = nullptr; - - for (QV4::Function *f : qAsConst(runtimeFunctions)) - f->destroy(); - runtimeFunctions.clear(); - - CompiledData::CompilationUnit::unlink(); -} - -void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) -{ - if (runtimeStrings) { - for (uint i = 0, end = totalStringCount(); i < end; ++i) - if (runtimeStrings[i]) - runtimeStrings[i]->mark(markStack); - } - if (runtimeRegularExpressions) { - for (uint i = 0; i < data->regexpTableSize; ++i) - Value::fromStaticValue(runtimeRegularExpressions[i]).mark(markStack); - } - if (runtimeClasses) { - for (uint i = 0; i < data->jsClassTableSize; ++i) - if (runtimeClasses[i]) - runtimeClasses[i]->mark(markStack); - } - for (QV4::Function *f : qAsConst(runtimeFunctions)) - if (f && f->internalClass) - f->internalClass->mark(markStack); - for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks)) - if (c) - c->mark(markStack); - - for (QV4::Heap::Object *o : qAsConst(templateObjects)) - if (o) - o->mark(markStack); - - if (runtimeLookups) { - for (uint i = 0; i < data->lookupTableSize; ++i) - runtimeLookups[i].markObjects(markStack); - } - - if (auto mod = module()) - mod->mark(markStack); -} - -IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex) -{ - IdentifierHash namedObjectCache(engine); - const CompiledData::Object *component = objectAt(componentObjectIndex); - const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); - for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { - const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr); - namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); - } - return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); -} - -void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) -{ - this->qmlEngine = qmlEngine; - - // Add to type registry of composites - if (propertyCaches.needsVMEMetaObject(/*root object*/0)) { - QQmlMetaType::registerInternalCompositeType(this); - qmlEngine->registerInternalCompositeType(this); - } else { - const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); - auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - if (typeRef->compilationUnit) { - metaTypeId = typeRef->compilationUnit->metaTypeId; - listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; - } else { - metaTypeId = typeRef->type.typeId(); - listMetaTypeId = typeRef->type.qListTypeId(); - } - } - - // Collect some data for instantiation later. - int bindingCount = 0; - int parserStatusCount = 0; - int objectCount = 0; - for (quint32 i = 0, count = this->objectCount(); i < count; ++i) { - const QV4::CompiledData::Object *obj = objectAt(i); - bindingCount += obj->nBindings; - if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) { - if (typeRef->type.parserStatusCast() != -1) - ++parserStatusCount; - } - ++objectCount; - if (typeRef->compilationUnit) { - bindingCount += typeRef->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; - objectCount += typeRef->compilationUnit->totalObjectCount; - } - } - } - - totalBindingsCount = bindingCount; - totalParserStatusCount = parserStatusCount; - totalObjectCount = objectCount; -} - -bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const -{ - if (!dependencyHasher) { - for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { - if (data->dependencyMD5Checksum[i] != 0) - return false; - } - return true; - } - const QByteArray checksum = dependencyHasher(); - return checksum.size() == sizeof(data->dependencyMD5Checksum) - && memcmp(data->dependencyMD5Checksum, checksum.constData(), - sizeof(data->dependencyMD5Checksum)) == 0; -} - -QStringList ExecutableCompilationUnit::moduleRequests() const -{ - QStringList requests; - requests.reserve(data->moduleRequestTableSize); - for (uint i = 0; i < data->moduleRequestTableSize; ++i) - requests << stringAt(data->moduleRequestTable()[i]); - return requests; -} - -Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) -{ - if (isESModule() && module()) - return module(); - - if (data->indexOfRootFunction < 0) - return nullptr; - - if (!this->engine) - linkToEngine(engine); - - Scope scope(engine); - Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this)); - - if (isESModule()) - setModule(module->d()); - - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); - if (engine->hasException) - return nullptr; - dependentModuleUnit->instantiate(engine); - } - - ScopedString importName(scope); - - const uint importCount = data->importEntryTableSize; - if (importCount > 0) { - imports = new const StaticValue *[importCount]; - memset(imports, 0, importCount * sizeof(StaticValue *)); - } - for (uint i = 0; i < importCount; ++i) { - const CompiledData::ImportEntry &entry = data->importEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - importName = runtimeStrings[entry.importName]; - const Value *valuePtr = dependentModuleUnit->resolveExport(importName); - if (!valuePtr) { - QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); - referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); - return nullptr; - } - imports[i] = valuePtr; - } - - for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; - - ScopedString importName(scope, runtimeStrings[entry.importName]); - if (!dependentModuleUnit->resolveExport(importName)) { - QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference "); - referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); - return nullptr; - } - } - - return module->d(); -} - -const Value *ExecutableCompilationUnit::resolveExportRecursively( - QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet) -{ - if (!module()) - return nullptr; - - for (const auto &entry: *resolveSet) - if (entry.module == this && entry.exportName->isEqualTo(exportName)) - return nullptr; - - (*resolveSet) << ResolveSetEntry(this, exportName); - - if (exportName->toQString() == QLatin1String("*")) - return &module()->self; - - Scope scope(engine); - - if (auto localExport = lookupNameInExportTable( - data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) { - ScopedString localName(scope, runtimeStrings[localExport->localName]); - uint index = module()->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey()); - if (index == UINT_MAX) - return nullptr; - if (index >= module()->scope->locals.size) - return &(imports[index - module()->scope->locals.size]->asValue<Value>()); - return &module()->scope->locals[index]; - } - - if (auto indirectExport = lookupNameInExportTable( - data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { - auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; - ScopedString importName(scope, runtimeStrings[indirectExport->importName]); - return dependentModuleUnit->resolveExportRecursively(importName, resolveSet); - } - - - if (exportName->toQString() == QLatin1String("default")) - return nullptr; - - const Value *starResolution = nullptr; - - for (uint i = 0; i < data->starExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; - - const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet); - // ### handle ambiguous - if (resolution) { - if (!starResolution) { - starResolution = resolution; - continue; - } - if (resolution != starResolution) - return nullptr; - } - } - - return starResolution; -} - -const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable( - const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const -{ - const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize; - auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) { - return stringAt(lhs.exportName) < name->toQString(); - }); - if (matchingExport == lastExportEntry || stringAt(matchingExport->exportName) != name->toQString()) - return nullptr; - return matchingExport; -} - -void ExecutableCompilationUnit::getExportedNamesRecursively( - QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet, - bool includeDefaultExport) const -{ - if (exportNameSet->contains(this)) - return; - exportNameSet->append(this); - - const auto append = [names, includeDefaultExport](const QString &name) { - if (!includeDefaultExport && name == QLatin1String("default")) - return; - names->append(name); - }; - - for (uint i = 0; i < data->localExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i]; - append(stringAt(entry.exportName)); - } - - for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; - append(stringAt(entry.exportName)); - } - - for (uint i = 0; i < data->starExportEntryTableSize; ++i) { - const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - if (!dependentModuleUnit) - return; - dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false); - } -} - -void ExecutableCompilationUnit::evaluate() -{ - QV4::Scope scope(engine); - QV4::Scoped<Module> mod(scope, module()); - mod->evaluate(); -} - -void ExecutableCompilationUnit::evaluateModuleRequests() -{ - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); - if (engine->hasException) - return; - dependentModuleUnit->evaluate(); - if (engine->hasException) - return; - } -} - -bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) -{ - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } - - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); - - const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) }; - for (const QString &cachePath : cachePaths) { - CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString); - if (!mappedUnit) - continue; - - const CompiledData::Unit * const oldDataPtr - = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data - : nullptr; - const CompiledData::Unit *oldData = data; - auto dataPtrRevert = qScopeGuard([this, oldData](){ - setUnitData(oldData); - }); - setUnitData(mappedUnit); - - if (data->sourceFileIndex != 0 - && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - continue; - } - - dataPtrRevert.dismiss(); - free(const_cast<CompiledData::Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; - } - - return false; -} - -bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) -{ - if (data->sourceTimeStamp == 0) { - *errorString = QStringLiteral("Missing time stamp for source file"); - return false; - } - - if (!QQmlFile::isLocalFile(unitUrl)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } - - return CompilationUnit::saveToDisk(localCacheFilePath(unitUrl), errorString); -} - -/*! -Returns the property cache, if one alread exists. The cache is not referenced. -*/ -QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const -{ - if (type.isValid()) - return typePropertyCache; - else - return compilationUnit->rootPropertyCache(); -} - -/*! -Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. -*/ -QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) -{ - if (typePropertyCache) { - return typePropertyCache; - } else if (type.isValid()) { - typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion); - return typePropertyCache; - } else { - return compilationUnit->rootPropertyCache(); - } -} - -bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) -{ - if (type.isValid()) { - bool ok = false; - hash->addData(createPropertyCache(engine)->checksum(&ok)); - return ok; - } - if (!compilationUnit) - return false; - hash->addData(compilationUnit->data->md5Checksum, - sizeof(compilationUnit->data->md5Checksum)); - return true; -} - -template <typename T> -bool qtTypeInherits(const QMetaObject *mo) { - while (mo) { - if (mo == &T::staticMetaObject) - return true; - mo = mo->superClass(); - } - return false; -} - -void ResolvedTypeReference::doDynamicTypeCheck() -{ - const QMetaObject *mo = nullptr; - if (typePropertyCache) - mo = typePropertyCache->firstCppMetaObject(); - else if (type.isValid()) - mo = type.metaObject(); - else if (compilationUnit) - mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); - isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); -} - -bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const -{ - for (auto it = constBegin(), end = constEnd(); it != end; ++it) { - if (!it.value()->addToHash(hash, engine)) - return false; - } - - return true; -} - -QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const -{ - using namespace CompiledData; - switch (binding->type) { - case Binding::Type_Script: - case Binding::Type_String: - return stringAt(binding->stringIndex); - case Binding::Type_Null: - return QStringLiteral("null"); - case Binding::Type_Boolean: - return binding->value.b ? QStringLiteral("true") : QStringLiteral("false"); - case Binding::Type_Number: - return QString::number(bindingValueAsNumber(binding)); - case Binding::Type_Invalid: - return QString(); -#if !QT_CONFIG(translation) - case Binding::Type_TranslationById: - case Binding::Type_Translation: - return unit->stringAt( - unit->data->translations()[binding->value.translationDataIndex].stringIndex); -#else - case Binding::Type_TranslationById: { - const TranslationData &translation - = data->translations()[binding->value.translationDataIndex]; - QByteArray id = stringAt(translation.stringIndex).toUtf8(); - return qtTrId(id.constData(), translation.number); - } - case Binding::Type_Translation: { - const TranslationData &translation - = data->translations()[binding->value.translationDataIndex]; - // This code must match that in the qsTr() implementation - const QString &path = fileName(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5) - : QStringRef(); - QByteArray contextUtf8 = context.toUtf8(); - QByteArray comment = stringAt(translation.commentIndex).toUtf8(); - QByteArray text = stringAt(translation.stringIndex).toUtf8(); - return QCoreApplication::translate(contextUtf8.constData(), text.constData(), - comment.constData(), translation.number); - } -#endif - default: - break; - } - return QString(); -} - -QString ExecutableCompilationUnit::bindingValueAsScriptString( - const CompiledData::Binding *binding) const -{ - return (binding->type == CompiledData::Binding::Type_String) - ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex)) - : bindingValueAsString(binding); -} - -} // namespace QV4 - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4executablecompilationunit_p.h b/src/qml/compiler/qv4executablecompilationunit_p.h deleted file mode 100644 index 4e3aadf28a..0000000000 --- a/src/qml/compiler/qv4executablecompilationunit_p.h +++ /dev/null @@ -1,324 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4EXECUTABLECOMPILATIONUNIT_P_H -#define QV4EXECUTABLECOMPILATIONUNIT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4compileddata_p.h> -#include <private/qqmlrefcount_p.h> -#include <private/qintrusivelist_p.h> -#include <private/qqmlpropertycachevector_p.h> -#include <private/qqmltype_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlEnginePrivate; -namespace QV4 { - -class CompilationUnitMapper; -struct ResolvedTypeReference; -// map from name index -// While this could be a hash, a map is chosen here to provide a stable -// order, which is used to calculating a check-sum on dependent meta-objects. -struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> -{ - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; -}; - -class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit, - public QQmlRefCount -{ - Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit) -public: - friend class QQmlRefPointer<ExecutableCompilationUnit>; - - static QQmlRefPointer<ExecutableCompilationUnit> create( - CompiledData::CompilationUnit &&compilationUnit) - { - return QQmlRefPointer<ExecutableCompilationUnit>( - new ExecutableCompilationUnit(std::move(compilationUnit)), - QQmlRefPointer<ExecutableCompilationUnit>::Adopt); - } - - static QQmlRefPointer<ExecutableCompilationUnit> create() - { - return QQmlRefPointer<ExecutableCompilationUnit>( - new ExecutableCompilationUnit, - QQmlRefPointer<ExecutableCompilationUnit>::Adopt); - } - - QIntrusiveListNode nextCompilationUnit; - ExecutionEngine *engine = nullptr; - QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. - - // url() and fileName() shall be used to load the actual QML/JS code or to show errors or - // warnings about that code. They include any potential URL interceptions and thus represent the - // "physical" location of the code. - // - // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code - // They are _not_ intercepted and thus represent the "logical" name for the code. - - QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QUrl finalUrl() const - { - if (m_finalUrl.isNull) - m_finalUrl = QUrl(finalUrlString()); - return m_finalUrl; - } - - QV4::Lookup *runtimeLookups = nullptr; - QVector<QV4::Function *> runtimeFunctions; - QVector<QV4::Heap::InternalClass *> runtimeBlocks; - mutable QVector<QV4::Heap::Object *> templateObjects; - mutable QQmlNullableValue<QUrl> m_url; - mutable QQmlNullableValue<QUrl> m_finalUrl; - - // QML specific fields - QQmlPropertyCacheVector propertyCaches; - QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } - - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - - // index is object index. This allows fast access to the - // property data when initializing bindings, avoiding expensive - // lookups by string (property name). - QVector<CompiledData::BindingPropertyData> bindingPropertyDataPerObject; - - // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects - // this is initialized on-demand by QQmlContextData - QHash<int, IdentifierHash> namedObjectsPerComponentCache; - inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); - - int totalBindingsCount = 0; // Number of bindings used in this type - int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount = 0; // Number of objects explicitly instantiated - - QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; - ResolvedTypeReferenceMap resolvedTypes; - ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } - - bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; - - int metaTypeId = -1; - int listMetaTypeId = -1; - bool isRegisteredWithEngine = false; - - QScopedPointer<CompilationUnitMapper> backingFile; - - // --- interface for QQmlPropertyCacheCreator - using CompiledObject = CompiledData::Object; - using CompiledFunction = CompiledData::Function; - - int objectCount() const { return qmlData->nObjects; } - const CompiledObject *objectAt(int index) const - { - return qmlData->objectAt(index); - } - - int importCount() const { return qmlData->nImports; } - const CompiledData::Import *importAt(int index) const - { - return qmlData->importAt(index); - } - - Heap::Object *templateObjectAt(int index) const; - - struct FunctionIterator - { - FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index) - : unit(unit), object(object), index(index) {} - const CompiledData::Unit *unit; - const CompiledObject *object; - int index; - - const CompiledFunction *operator->() const - { - return unit->functionAt(object->functionOffsetTable()[index]); - } - - void operator++() { ++index; } - bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } - bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } - }; - - FunctionIterator objectFunctionsBegin(const CompiledObject *object) const - { - return FunctionIterator(data, object, 0); - } - - FunctionIterator objectFunctionsEnd(const CompiledObject *object) const - { - return FunctionIterator(data, object, object->nFunctions); - } - - bool isESModule() const - { - return data->flags & CompiledData::Unit::IsESModule; - } - - bool isSharedLibrary() const - { - return data->flags & CompiledData::Unit::IsSharedLibrary; - } - - QStringList moduleRequests() const; - Heap::Module *instantiate(ExecutionEngine *engine); - const Value *resolveExport(QV4::String *exportName) - { - QVector<ResolveSetEntry> resolveSet; - return resolveExportRecursively(exportName, &resolveSet); - } - - QStringList exportedNames() const - { - QStringList names; - QVector<const ExecutableCompilationUnit*> exportNameSet; - getExportedNamesRecursively(&names, &exportNameSet); - names.sort(); - auto last = std::unique(names.begin(), names.end()); - names.erase(last, names.end()); - return names; - } - - void evaluate(); - void evaluateModuleRequests(); - - QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); - void unlink(); - - void markObjects(MarkStack *markStack); - - bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); - - static QString localCacheFilePath(const QUrl &url); - bool saveToDisk(const QUrl &unitUrl, QString *errorString); - - QString bindingValueAsString(const CompiledData::Binding *binding) const; - QString bindingValueAsScriptString(const CompiledData::Binding *binding) const; - double bindingValueAsNumber(const CompiledData::Binding *binding) const - { - if (binding->type != CompiledData::Binding::Type_Number) - return 0.0; - return constants[binding->value.constantValueIndex].doubleValue(); - } - -protected: - quint32 totalStringCount() const - { return data->stringTableSize; } - -private: - struct ResolveSetEntry - { - ResolveSetEntry() {} - ResolveSetEntry(ExecutableCompilationUnit *module, QV4::String *exportName) - : module(module), exportName(exportName) {} - ExecutableCompilationUnit *module = nullptr; - QV4::String *exportName = nullptr; - }; - - ExecutableCompilationUnit(); - ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit); - ~ExecutableCompilationUnit(); - - const Value *resolveExportRecursively(QV4::String *exportName, - QVector<ResolveSetEntry> *resolveSet); - - QUrl urlAt(int index) const { return QUrl(stringAt(index)); } - - Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex); - const CompiledData::ExportEntry *lookupNameInExportTable( - const CompiledData::ExportEntry *firstExportEntry, int tableSize, - QV4::String *name) const; - - void getExportedNamesRecursively( - QStringList *names, QVector<const ExecutableCompilationUnit *> *exportNameSet, - bool includeDefaultExport = true) const; -}; - -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlRefPointer<QQmlPropertyCache> propertyCache() const; - QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); -}; - -IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex) -{ - auto it = namedObjectsPerComponentCache.find(componentObjectIndex); - if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) - return createNamedObjectsPerComponent(componentObjectIndex); - return *it; -} - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4EXECUTABLECOMPILATIONUNIT_P_H diff --git a/src/qml/compiler/qv4staticvalue_p.h b/src/qml/compiler/qv4staticvalue_p.h new file mode 100644 index 0000000000..c6b4bdb158 --- /dev/null +++ b/src/qml/compiler/qv4staticvalue_p.h @@ -0,0 +1,548 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef QV4STATICVALUE_P_H +#define QV4STATICVALUE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/private/qv4global_p.h> +#include <QtCore/private/qnumeric_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Double { + quint64 d; + + Double(double dbl) { + memcpy(&d, &dbl, sizeof(double)); + } + + int sign() const { + return (d >> 63) ? -1 : 1; + } + + bool isDenormal() const { + return static_cast<int>((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast<int>((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast<quint64>(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast<int>(d); + if (i == d) + return i; + return Double(d).toInt32(); + } + + int toInt32() { + int e = exponent() - 52; + if (e < 0) { + if (e <= -53) + return 0; + return sign() * static_cast<int>(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast<int>(significant()) << e); + } + } +}; + +struct Q_QML_PRIVATE_EXPORT StaticValue +{ + StaticValue() = default; + constexpr StaticValue(quint64 val) : _val(val) {} + + StaticValue &operator=(ReturnedValue v) + { + _val = v; + return *this; + } + + template<typename Value> + StaticValue &operator=(const Value &); + + template<typename Value> + const Value &asValue() const; + + template<typename Value> + Value &asValue(); + + /* + We use 8 bytes for a value and a different variant of NaN boxing. A Double + NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a + signalling NaN it is the top 14 bits. The other values are usually set to 0 by the + processor, and are thus free for us to store other data. We keep pointers in there for + managed objects, and encode the other types using the free space given to use by the unused + bits for NaN values. This also works for pointers on 64 bit systems, as they all currently + only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for + pointers.) + + We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between + managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave + the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is + set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are + used to encode Null/Int/Bool. + + Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. + + Specific bit-sequences: + 0 = always 0 + 1 = always 1 + x = stored value + a,b,c,d = specific bit values, see notes + + 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | + 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value + ------------------------------------------------------------------------+-------------- + 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined + 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) + a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf + dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double + 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) + 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + + Notes: + - a: xor-ed signbit, always 1 for NaN + - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value + - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 + - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ + and JS + - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 + - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, + so: (val >> (64-15)) == 1 + - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where + any non double results in a NaN + - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to + 63) are zero. No need to shift. + */ + + quint64 _val; + + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + static inline int valueOffset() { return 0; } + static inline int tagOffset() { return 4; } +#else // !Q_LITTLE_ENDIAN + static inline int valueOffset() { return 4; } + static inline int tagOffset() { return 0; } +#endif + static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } + QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } + QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } + + QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const + { + return int(value()); + } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) + { + setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); + } + QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } + + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() + { + setTagValue(quint32(ValueTypeInternal::Empty), 0); + } + + // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible + // and use negative numbers here + enum QuickType { + QT_ManagedOrUndefined = 0, + QT_ManagedOrUndefined1 = 1, + QT_ManagedOrUndefined2 = 2, + QT_ManagedOrUndefined3 = 3, + QT_Empty = 4, + QT_Null = 5, + QT_Bool = 6, + QT_Int = 7 + // all other values are doubles + }; + + enum Type { + Undefined_Type = 0, + Managed_Type = 1, + Empty_Type = 4, + Null_Type = 5, + Boolean_Type = 6, + Integer_Type = 7, + Double_Type = 8 + }; + + inline Type type() const { + int t = quickType(); + if (t < QT_Empty) + return _val ? Managed_Type : Undefined_Type; + if (t > QT_Int) + return Double_Type; + return static_cast<Type>(t); + } + + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ull; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-15, + IsIntegerOrBool_Shift = 64-16, + QuickType_Shift = 64 - 17, + IsPositiveIntShift = 31 + }; + + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + enum class ValueTypeInternal_64 { + Empty = Immediate_Mask_64 | 0, + Null = Immediate_Mask_64 | 0x08000u, + Boolean = Immediate_Mask_64 | 0x10000u, + Integer = Immediate_Mask_64 | 0x18000u + }; + + // Used only by 32-bit encoding + enum Masks { + SilentNaNBit = 0x00040000, + NotDouble_Mask = 0x7ffa0000, + }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; + + enum class ValueTypeInternal_32 { + Empty = Immediate_Mask_32 | 0, + Null = Immediate_Mask_32 | 0x08000u, + Boolean = Immediate_Mask_32 | 0x10000u, + Integer = Immediate_Mask_32 | 0x18000u + }; + + enum { + Managed_Type_Internal = 0 + }; + + using ValueTypeInternal = ValueTypeInternal_64; + + enum { + NaN_Mask = 0x7ff80000, + }; + + inline quint64 quickType() const { return (_val >> QuickType_Shift); } + + // used internally in property + inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } + inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } + inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } + inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } + inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } + inline bool isNumber() const { return quickType() >= QT_Int; } + + inline bool isUndefined() const { return _val == 0; } + inline bool isDouble() const { return (_val >> IsDouble_Shift); } + inline bool isManaged() const + { +#if QT_POINTER_SIZE == 4 + return value() && tag() == Managed_Type_Internal; +#else + return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + inline bool isManagedOrUndefined() const + { +#if QT_POINTER_SIZE == 4 + return tag() == Managed_Type_Internal; +#else + return ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + + inline bool isIntOrBool() const { + return (_val >> IsIntegerOrBool_Shift) == 3; + } + + inline bool integerCompatible() const { + Q_ASSERT(!isEmpty()); + return (_val >> IsIntegerConvertible_Shift) == 1; + } + + static inline bool integerCompatible(StaticValue a, StaticValue b) { + return a.integerCompatible() && b.integerCompatible(); + } + + static inline bool bothDouble(StaticValue a, StaticValue b) { + return a.isDouble() && b.isDouble(); + } + + inline bool isNaN() const + { + return (tag() & 0x7ffc0000 ) == 0x00040000; + } + + inline bool isPositiveInt() const { +#if QT_POINTER_SIZE == 4 + return isInteger() && int_32() >= 0; +#else + return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); +#endif + } + + QML_NEARLY_ALWAYS_INLINE double doubleValue() const { + Q_ASSERT(isDouble()); + double d; + StaticValue v = *this; + v._val ^= NaNEncodeMask; + memcpy(&d, &v._val, 8); + return d; + } + + QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { + if (qt_is_nan(d)) + d = qt_qnan(); + memcpy(&_val, &d, 8); + _val ^= NaNEncodeMask; + Q_ASSERT(isDouble()); + } + + inline bool isInt32() { + if (tag() == quint32(ValueTypeInternal::Integer)) + return true; + if (isDouble()) { + double d = doubleValue(); + if (isInt32(d)) { + setInt_32(int(d)); + return true; + } + } + return false; + } + + QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + int i = int(d); + return (i == d && !(d == 0 && std::signbit(d))); + } + + double asDouble() const { + if (tag() == quint32(ValueTypeInternal::Integer)) + return int_32(); + return doubleValue(); + } + + bool booleanValue() const { + return int_32(); + } + + int integerValue() const { + return int_32(); + } + + inline bool tryIntegerConversion() { + bool b = integerCompatible(); + if (b) + setTagValue(quint32(ValueTypeInternal::Integer), value()); + return b; + } + + bool toBoolean() const { + if (integerCompatible()) + return static_cast<bool>(int_32()); + + if (isManagedOrUndefined()) + return false; + + // double + const double d = doubleValue(); + return d && !std::isnan(d); + } + + inline int toInt32() const + { + switch (type()) { + case Null_Type: + case Boolean_Type: + case Integer_Type: + return int_32(); + case Double_Type: + return Double::toInt32(doubleValue()); + case Empty_Type: + case Undefined_Type: + case Managed_Type: + break; + } + return Double::toInt32(std::numeric_limits<double>::quiet_NaN()); + } + + ReturnedValue *data_ptr() { return &_val; } + constexpr ReturnedValue asReturnedValue() const { return _val; } + constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; } + + inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } + static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } + static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } + inline static constexpr StaticValue undefinedValue() { return { 0 }; } + static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } + + static inline StaticValue fromDouble(double d) + { + StaticValue v; + v.setDouble(d); + return v; + } + + static inline StaticValue fromUInt32(uint i) + { + StaticValue v; + if (i < uint(std::numeric_limits<int>::max())) { + v.setTagValue(quint32(ValueTypeInternal::Integer), i); + } else { + v.setDouble(i); + } + return v; + } + + static double toInteger(double d) + { + if (std::isnan(d)) + return +0; + if (!d || std::isinf(d)) + return d; + return d >= 0 ? std::floor(d) : std::ceil(d); + } + + static int toInt32(double d) + { + return Double::toInt32(d); + } + + static unsigned int toUInt32(double d) + { + return static_cast<uint>(toInt32(d)); + } +}; +Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value); + +struct Encode { + static constexpr ReturnedValue undefined() { + return StaticValue::undefinedValue().asReturnedValue(); + } + static constexpr ReturnedValue null() { + return StaticValue::nullValue().asReturnedValue(); + } + + explicit constexpr Encode(bool b) + : val(StaticValue::fromBoolean(b).asReturnedValue()) + { + } + explicit Encode(double d) { + val = StaticValue::fromDouble(d).asReturnedValue(); + } + explicit constexpr Encode(int i) + : val(StaticValue::fromInt32(i).asReturnedValue()) + { + } + explicit Encode(uint i) { + val = StaticValue::fromUInt32(i).asReturnedValue(); + } + explicit constexpr Encode(ReturnedValue v) + : val(v) + { + } + constexpr Encode(StaticValue v) + : val(v.asReturnedValue()) + { + } + + template<typename HeapBase> + explicit Encode(HeapBase *o); + + explicit Encode(StaticValue *o) { + Q_ASSERT(o); + val = o->asReturnedValue(); + } + + static ReturnedValue smallestNumber(double d) { + if (StaticValue::isInt32(d)) + return Encode(static_cast<int>(d)); + else + return Encode(d); + } + + constexpr operator ReturnedValue() const { + return val; + } + quint64 val; +private: + explicit Encode(void *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4STATICVALUE_P_H diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4stringtoarrayindex_p.h index 80f914c141..61bd988d1e 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4stringtoarrayindex_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** 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. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QV4COMPILATIONUNITMAPPER_H -#define QV4COMPILATIONUNITMAPPER_H +#ifndef QV4STRINGTOARRAYINDEX_P_H +#define QV4STRINGTOARRAYINDEX_P_H // // W A R N I N G @@ -51,35 +51,46 @@ // We mean it. // -#include <private/qv4global_p.h> -#include <QFile> +#include <QtCore/private/qnumeric_p.h> +#include <QtCore/qstring.h> +#include <limits> QT_BEGIN_NAMESPACE namespace QV4 { -namespace CompiledData { -struct Unit; -} +inline uint charToUInt(const QChar *ch) { return ch->unicode(); } +inline uint charToUInt(const char *ch) { return static_cast<unsigned char>(*ch); } -class CompilationUnitMapper +template <typename T> +uint stringToArrayIndex(const T *ch, const T *end) { -public: - CompilationUnitMapper(); - ~CompilationUnitMapper(); - - CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); - void close(); + uint i = charToUInt(ch) - '0'; + if (i > 9) + return std::numeric_limits<uint>::max(); + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return std::numeric_limits<uint>::max(); -private: -#if defined(Q_OS_UNIX) - size_t length; -#endif - void *dataPtr; -}; + while (ch < end) { + uint x = charToUInt(ch) - '0'; + if (x > 9) + return std::numeric_limits<uint>::max(); + if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x + return std::numeric_limits<uint>::max(); + ++ch; + } + return i; +} +inline uint stringToArrayIndex(const QString &str) +{ + return stringToArrayIndex(str.constData(), str.constData() + str.length()); } +} // namespace QV4 + QT_END_NAMESPACE -#endif // QV4COMPILATIONUNITMAPPER_H +#endif // QV4STRINGTOARRAYINDEX_P_H diff --git a/src/qml/compiler/qv4util_p.h b/src/qml/compiler/qv4util_p.h new file mode 100644 index 0000000000..bd9758c1fb --- /dev/null +++ b/src/qml/compiler/qv4util_p.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4UTIL_H +#define QV4UTIL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QBitArray> +#include <algorithm> +#include <vector> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND) +// Sanity: +class BitVector +{ + std::vector<bool> bits; + +public: + BitVector(int size = 0, bool value = false) + : bits(size, value) + {} + + void clear() + { bits = std::vector<bool>(bits.size(), false); } + + void reserve(int size) + { bits.reserve(size); } + + int size() const + { + Q_ASSERT(bits.size() < INT_MAX); + return static_cast<int>(bits.size()); + } + + void resize(int newSize) + { bits.resize(newSize); } + + void resize(int newSize, bool newValue) + { bits.resize(newSize, newValue); } + + void assign(int newSize, bool value) + { bits.assign(newSize, value); } + + int findNext(int start, bool value, bool wrapAround) const + { + // The ++operator of std::vector<bool>::iterator in libc++ has a bug when using it on an + // iterator pointing to the last element. It will not be set to ::end(), but beyond + // that. (It will be set to the first multiple of the native word size that is bigger + // than size().) + // + // See http://llvm.org/bugs/show_bug.cgi?id=19663 + // + // The work-around is to calculate the distance, and compare it to the size() to see if it's + // beyond the end, or take the minimum of the distance and the size. + + size_t pos = std::distance(bits.begin(), + std::find(bits.begin() + start, bits.end(), value)); + if (wrapAround && pos >= static_cast<size_t>(size())) + pos = std::distance(bits.begin(), + std::find(bits.begin(), bits.begin() + start, value)); + + pos = qMin(pos, static_cast<size_t>(size())); + + Q_ASSERT(pos <= static_cast<size_t>(size())); + Q_ASSERT(pos < INT_MAX); + + return static_cast<int>(pos); + } + + bool at(int idx) const + { return bits.at(idx); } + + void setBit(int idx) + { bits[idx] = true; } + + void clearBit(int idx) + { bits[idx] = false; } +}; +#else // Insanity: +class BitVector +{ + QBitArray bits; + +public: + BitVector(int size = 0, bool value = false) + : bits(size, value) + {} + + void clear() + { bits = QBitArray(bits.size(), false); } + + void reserve(int size) + { Q_UNUSED(size); } + + int size() const + { return bits.size(); } + + void resize(int newSize) + { bits.resize(newSize); } + + void resize(int newSize, bool newValue) + { + int oldSize = bits.size(); + bits.resize(newSize); + bits.fill(newValue, oldSize, bits.size()); + } + + void assign(int newSize, bool value) + { + bits.resize(newSize); + bits.fill(value); + } + + int findNext(int start, bool value, bool wrapAround) const + { + for (int i = start, ei = size(); i < ei; ++i) { + if (at(i) == value) + return i; + } + + if (wrapAround) { + for (int i = 0, ei = start; i < ei; ++i) { + if (at(i) == value) + return i; + } + } + + return size(); + } + + bool at(int idx) const + { return bits.at(idx); } + + void setBit(int idx) + { bits[idx] = true; } + + void clearBit(int idx) + { bits[idx] = false; } +}; +#endif + +} + +QT_END_NAMESPACE + +#endif // QV4UTIL_H |