From 6c76ee30ce1662ca8f8368de5ebd1e6bcfdf0d9e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 20 May 2019 17:05:50 +0200 Subject: 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 --- src/3rdparty/masm/masm.pri | 2 - src/particles/qquickparticlesystem_p.h | 1 + src/qml/compiler/compiler.pri | 31 +- src/qml/compiler/qqmlirloader.cpp | 205 --- src/qml/compiler/qqmlirloader_p.h | 81 -- src/qml/compiler/qqmlpropertycachecreator.cpp | 101 -- src/qml/compiler/qqmlpropertycachecreator_p.h | 843 ------------ src/qml/compiler/qqmlpropertyresolver.cpp | 92 -- src/qml/compiler/qqmlpropertyresolver_p.h | 87 -- src/qml/compiler/qqmlpropertyvalidator.cpp | 729 ---------- src/qml/compiler/qqmlpropertyvalidator_p.h | 91 -- src/qml/compiler/qqmltypecompiler.cpp | 1429 -------------------- src/qml/compiler/qqmltypecompiler_p.h | 347 ----- src/qml/compiler/qv4alloca_p.h | 108 ++ src/qml/compiler/qv4calldata_p.h | 123 ++ src/qml/compiler/qv4compilationunitmapper.cpp | 62 - src/qml/compiler/qv4compilationunitmapper_p.h | 85 -- src/qml/compiler/qv4compilationunitmapper_unix.cpp | 108 -- src/qml/compiler/qv4compilationunitmapper_win.cpp | 128 -- src/qml/compiler/qv4compiler.cpp | 29 +- src/qml/compiler/qv4executablecompilationunit.cpp | 809 ----------- src/qml/compiler/qv4executablecompilationunit_p.h | 324 ----- src/qml/compiler/qv4staticvalue_p.h | 548 ++++++++ src/qml/compiler/qv4stringtoarrayindex_p.h | 96 ++ src/qml/compiler/qv4util_p.h | 195 +++ src/qml/jsruntime/jsruntime.pri | 33 +- src/qml/jsruntime/qv4alloca_p.h | 108 -- src/qml/jsruntime/qv4calldata_p.h | 123 -- src/qml/jsruntime/qv4compilationunitmapper.cpp | 62 + src/qml/jsruntime/qv4compilationunitmapper_p.h | 85 ++ .../jsruntime/qv4compilationunitmapper_unix.cpp | 108 ++ src/qml/jsruntime/qv4compilationunitmapper_win.cpp | 128 ++ src/qml/jsruntime/qv4executablecompilationunit.cpp | 809 +++++++++++ src/qml/jsruntime/qv4executablecompilationunit_p.h | 324 +++++ src/qml/jsruntime/qv4staticvalue_p.h | 548 -------- src/qml/jsruntime/qv4string_p.h | 2 +- src/qml/jsruntime/qv4stringtoarrayindex_p.h | 96 -- src/qml/jsruntime/qv4util_p.h | 196 --- src/qml/memory/memory.pri | 6 +- src/qml/qml/qml.pri | 14 +- src/qml/qml/qqmlirloader.cpp | 205 +++ src/qml/qml/qqmlirloader_p.h | 81 ++ src/qml/qml/qqmlpropertycachecreator.cpp | 101 ++ src/qml/qml/qqmlpropertycachecreator_p.h | 843 ++++++++++++ src/qml/qml/qqmlpropertyresolver.cpp | 92 ++ src/qml/qml/qqmlpropertyresolver_p.h | 87 ++ src/qml/qml/qqmlpropertyvalidator.cpp | 729 ++++++++++ src/qml/qml/qqmlpropertyvalidator_p.h | 91 ++ src/qml/qml/qqmltypecompiler.cpp | 1429 ++++++++++++++++++++ src/qml/qml/qqmltypecompiler_p.h | 347 +++++ src/qmldevtools/qmldevtools.pro | 5 +- 51 files changed, 6643 insertions(+), 6663 deletions(-) delete mode 100644 src/qml/compiler/qqmlirloader.cpp delete mode 100644 src/qml/compiler/qqmlirloader_p.h delete mode 100644 src/qml/compiler/qqmlpropertycachecreator.cpp delete mode 100644 src/qml/compiler/qqmlpropertycachecreator_p.h delete mode 100644 src/qml/compiler/qqmlpropertyresolver.cpp delete mode 100644 src/qml/compiler/qqmlpropertyresolver_p.h delete mode 100644 src/qml/compiler/qqmlpropertyvalidator.cpp delete mode 100644 src/qml/compiler/qqmlpropertyvalidator_p.h delete mode 100644 src/qml/compiler/qqmltypecompiler.cpp delete mode 100644 src/qml/compiler/qqmltypecompiler_p.h create mode 100644 src/qml/compiler/qv4alloca_p.h create mode 100644 src/qml/compiler/qv4calldata_p.h delete mode 100644 src/qml/compiler/qv4compilationunitmapper.cpp delete mode 100644 src/qml/compiler/qv4compilationunitmapper_p.h delete mode 100644 src/qml/compiler/qv4compilationunitmapper_unix.cpp delete mode 100644 src/qml/compiler/qv4compilationunitmapper_win.cpp delete mode 100644 src/qml/compiler/qv4executablecompilationunit.cpp delete mode 100644 src/qml/compiler/qv4executablecompilationunit_p.h create mode 100644 src/qml/compiler/qv4staticvalue_p.h create mode 100644 src/qml/compiler/qv4stringtoarrayindex_p.h create mode 100644 src/qml/compiler/qv4util_p.h delete mode 100644 src/qml/jsruntime/qv4alloca_p.h delete mode 100644 src/qml/jsruntime/qv4calldata_p.h create mode 100644 src/qml/jsruntime/qv4compilationunitmapper.cpp create mode 100644 src/qml/jsruntime/qv4compilationunitmapper_p.h create mode 100644 src/qml/jsruntime/qv4compilationunitmapper_unix.cpp create mode 100644 src/qml/jsruntime/qv4compilationunitmapper_win.cpp create mode 100644 src/qml/jsruntime/qv4executablecompilationunit.cpp create mode 100644 src/qml/jsruntime/qv4executablecompilationunit_p.h delete mode 100644 src/qml/jsruntime/qv4staticvalue_p.h delete mode 100644 src/qml/jsruntime/qv4stringtoarrayindex_p.h delete mode 100644 src/qml/jsruntime/qv4util_p.h create mode 100644 src/qml/qml/qqmlirloader.cpp create mode 100644 src/qml/qml/qqmlirloader_p.h create mode 100644 src/qml/qml/qqmlpropertycachecreator.cpp create mode 100644 src/qml/qml/qqmlpropertycachecreator_p.h create mode 100644 src/qml/qml/qqmlpropertyresolver.cpp create mode 100644 src/qml/qml/qqmlpropertyresolver_p.h create mode 100644 src/qml/qml/qqmlpropertyvalidator.cpp create mode 100644 src/qml/qml/qqmlpropertyvalidator_p.h create mode 100644 src/qml/qml/qqmltypecompiler.cpp create mode 100644 src/qml/qml/qqmltypecompiler_p.h diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri index 0e63ac2ce5..4d15abcba0 100644 --- a/src/3rdparty/masm/masm.pri +++ b/src/3rdparty/masm/masm.pri @@ -77,7 +77,6 @@ SOURCES += $$PWD/disassembler/ARM64Disassembler.cpp SOURCES += $$PWD/disassembler/ARM64/A64DOpcode.cpp HEADERS += $$PWD/disassembler/ARM64/A64DOpcode.h -!qmldevtools_build { SOURCES += $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ $$PWD/yarr/YarrCanonicalizeUnicode.cpp \ $$PWD/yarr/YarrInterpreter.cpp \ @@ -94,7 +93,6 @@ HEADERS += $$PWD/yarr/Yarr.h \ $$PWD/yarr/YarrPattern.h \ $$PWD/yarr/YarrSyntaxChecker.h \ $$PWD/yarr/YarrUnicodeProperties.h -} # # Generate RegExpJitTables.h diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index 81cdb0e6da..4a9fecb504 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -60,6 +60,7 @@ #include #include #include +#include #include "qtquickparticlesglobal_p.h" QT_BEGIN_NAMESPACE 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 - -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(); - 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(); - 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 functionIndices; - functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); - - for (uint i = 0; i < serializedObject->nBindings; ++i) { - QmlIR::Binding *b = pool->New(); - *static_cast(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(); - 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(); - s->nameIndex = serializedSignal->nameIndex; - s->location = serializedSignal->location; - s->parameters = pool->New >(); - - for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New(); - *static_cast(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(); - e->nameIndex = serializedEnum->nameIndex; - e->location = serializedEnum->location; - e->enumValues = pool->New >(); - - for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { - QmlIR::EnumValue *v = pool->New(); - *static_cast(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(); - *static_cast(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(); - *static_cast(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(); - 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/qqmlirloader_p.h b/src/qml/compiler/qqmlirloader_p.h deleted file mode 100644 index aa303c923f..0000000000 --- a/src/qml/compiler/qqmlirloader_p.h +++ /dev/null @@ -1,81 +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 QQMLIRLOADER_P_H -#define QQMLIRLOADER_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 -#include - -QT_BEGIN_NAMESPACE - -namespace QmlIR { -struct Document; -struct Object; -} - -struct Q_QML_PRIVATE_EXPORT QQmlIRLoader { - QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); - - void load(); - -private: - QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); - - template _Tp *New() { return pool->New<_Tp>(); } - - const QV4::CompiledData::Unit *unit; - QmlIR::Document *output; - QQmlJS::MemoryPool *pool; -}; - -QT_END_NAMESPACE - -#endif // QQMLIRLOADER_P_H 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 - -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 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(); -} - -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 -#include -#include -#include - -QT_BEGIN_NAMESPACE - -struct QQmlBindingInstantiationContext { - QQmlBindingInstantiationContext() {} - QQmlBindingInstantiationContext(int referencingObjectIndex, - const QV4::CompiledData::Binding *instantiatingBinding, - const QString &instantiatingPropertyName, - QQmlPropertyCache *referencingObjectPropertyCache); - - bool resolveInstantiatingProperty(); - QQmlRefPointer instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; - - int referencingObjectIndex = -1; - const QV4::CompiledData::Binding *instantiatingBinding = nullptr; - QString instantiatingPropertyName; - QQmlRefPointer referencingObjectPropertyCache; - QQmlPropertyData *instantiatingProperty = nullptr; -}; - -struct QQmlPendingGroupPropertyBindings : public QVector -{ - void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; -}; - -struct QQmlPropertyCacheCreatorBase -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) -public: - static QAtomicInt classIndexCounter; -}; - -template -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 propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; - QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer &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 -inline QQmlPropertyCacheCreator::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 -inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() -{ - QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(/*root object*/0, context); -} - -template -inline QQmlCompileError QQmlPropertyCacheCreator::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 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 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 -inline QQmlRefPointer QQmlPropertyCacheCreator::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 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 -inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer &baseTypeCache) -{ - QQmlRefPointer 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 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 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 names; - names.reserve(paramCount); - QVarLengthArray 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 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 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 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 -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 *objectsWithAliases) const; - - int objectForId(const CompiledObject &component, int id) const; - - QQmlPropertyCacheVector *propertyCaches; - const ObjectContainer *objectContainer; -}; - -template -inline QQmlPropertyCacheAliasCreator::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) - : propertyCaches(propertyCaches) - , objectContainer(objectContainer) -{ - -} - -template -inline void QQmlPropertyCacheAliasCreator::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 -inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) -{ - QVector 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 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 -inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAliasesRecursively(int objectIndex, QVector *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 -inline QQmlCompileError QQmlPropertyCacheAliasCreator::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 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 -inline QQmlCompileError QQmlPropertyCacheAliasCreator::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 -inline int QQmlPropertyCacheAliasCreator::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(strlen("Changed"))); - - d = property(propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex()); - } - - return nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlpropertyresolver_p.h b/src/qml/compiler/qqmlpropertyresolver_p.h deleted file mode 100644 index df857f242e..0000000000 --- a/src/qml/compiler/qqmlpropertyresolver_p.h +++ /dev/null @@ -1,87 +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 QQMLPROPERTYRESOLVER_P_H -#define QQMLPROPERTYRESOLVER_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 -#include -#include - -QT_BEGIN_NAMESPACE - -struct Q_QML_EXPORT QQmlPropertyResolver -{ - QQmlPropertyResolver(const QQmlRefPointer &cache) - : cache(cache) - {} - - QQmlPropertyData *property(int index) const - { - return cache->property(index); - } - - enum RevisionCheck { - CheckRevision, - IgnoreRevision - }; - - QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, - RevisionCheck check = CheckRevision) const; - - // This code must match the semantics of QQmlPropertyPrivate::findSignalByName - QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; - - QQmlRefPointer cache; -}; - -QT_END_NAMESPACE - -#endif // QQMLPROPERTYRESOLVER_P_H 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 -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer &compilationUnit) - : enginePrivate(enginePrivate) - , compilationUnit(compilationUnit) - , imports(imports) - , qmlUnit(compilationUnit->unitData()) - , propertyCaches(compilationUnit->propertyCaches) - , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) -{ - bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); -} - -QVector QQmlPropertyValidator::validate() -{ - return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); -} - -typedef QVarLengthArray 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 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(); - - QQmlCustomParser *customParser = nullptr; - if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) - customParser = typeRef->type.customParser(); - } - - QList 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 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()) - 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 parserErrors = customParser->errors(); - if (!parserErrors.isEmpty()) - return parserErrors; - } - - (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - - QVector 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 >()) { - 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 >()) { - 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 >()) { - 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 >()) { - 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 >()) { - if (!binding->evaluatesToString()) { - return warnOrError(tr("Invalid property assignment: string or array of strings expected")); - } - break; - } else if (property->propType() == qMetaTypeId()) { - break; - } else if (property->propType() == qMetaTypeId()) { - 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 QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const -{ - QVector errors; - errors.append(QQmlCompileError(location, description)); - return errors; -} - -QVector QQmlPropertyValidator::recordError(const QQmlCompileError &error) const -{ - QVector 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 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()) { - // 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("")) - .arg(propertyName)); - } else if (property->propType() == qMetaTypeId()) { - 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 - -QT_BEGIN_NAMESPACE - -class QQmlPropertyValidator -{ - Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) -public: - QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer &compilationUnit); - - QVector validate(); - -private: - QVector 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 recordError(const QV4::CompiledData::Location &location, const QString &description) const; - Q_REQUIRED_RESULT QVector 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 compilationUnit; - const QQmlImports &imports; - const QV4::CompiledData::Unit *qmlUnit; - const QQmlPropertyCacheVector &propertyCaches; - - QVector * 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 -#include -#include -#include -#include -#include - -#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 &typeNameCache, - QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) - : resolvedTypes(resolvedTypeCache) - , engine(engine) - , typeData(typeData) - , dependencyHasher(dependencyHasher) - , typeNameCache(typeNameCache) - , document(parsedQML) -{ -} - -QQmlRefPointer 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 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 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(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 *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(); - 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 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 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 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 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::ConstIterator entry = customSignals.constFind(propertyName); - if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) { - QString alternateName = propertyName.mid(0, propertyName.length() - static_cast(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(foe->node)) { - if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast(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(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: - // * . - // * .. - - 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(); - 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(); - 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(); - *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 aliasCacheCreator(&propertyCaches, compiler); - - bool atLeastOneAliasResolved; - do { - atLeastOneAliasResolved = false; - QVector 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 &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 functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) - functionsToCompile << *foe; - const QVector runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); - const QList 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 -#include -#include -#include -#include -#include - -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 &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::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } - QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } - QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr; - // --- - - QQmlRefPointer compile(); - - QList 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 *qmlObjects() const; - void setPropertyCaches(QQmlPropertyCacheVector &&caches); - const QQmlPropertyCacheVector *propertyCaches() const; - QQmlPropertyCacheVector &&takePropertyCaches(); - void setComponentRoots(const QVector &roots) { m_componentRoots = roots; } - const QVector &componentRoots() const { return m_componentRoots; } - QQmlJS::MemoryPool *memoryPool(); - QStringRef newStringRef(const QString &string); - const QV4::Compiler::StringTableGenerator *stringPool() const; - - const QHash &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 errors; - QQmlEnginePrivate *engine; - QQmlTypeData *typeData; - const QV4::CompiledData::DependentTypesHasher &dependencyHasher; - QQmlRefPointer typeNameCache; - QmlIR::Document *document; - // index is string index of type name (use obj->inheritedTypeNameIndex) - QHash customParsers; - - // index in first hash is component index, vector inside contains object indices of objects with id property - QVector 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 &qmlObjects; - const QQmlImports *imports; - const QHash &customParsers; - const QSet &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 &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 &qmlObjects; - const QHash &customParsers; -}; - -// Annotate properties bound to aliases with a flag -class QQmlAliasAnnotator : public QQmlCompilePass -{ -public: - QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler); - - void annotateBindingsToAliases(); -private: - const QVector &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -class QQmlScriptStringScanner : public QQmlCompilePass -{ -public: - QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler); - - void scan(); - -private: - const QVector &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 *qmlObjects; - - // indices of the objects that are actually Component {} - QVector componentRoots; - - // Deliberate choice of map over hash here to ensure stable generated output. - QMap _idToObjectIndex; - QVector _objectsWithAliases; - - QQmlPropertyCacheVector propertyCaches; -}; - -class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass -{ -public: - QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); - - bool scanObject(); - -private: - bool scanObject(int objectIndex); - - QVector *qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; - const QHash &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 &customParsers; - const QVector &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 &qmlObjects; - const QQmlPropertyCacheVector * const propertyCaches; -}; - -QT_END_NAMESPACE - -#endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qv4alloca_p.h b/src/qml/compiler/qv4alloca_p.h new file mode 100644 index 0000000000..65c3e4d65a --- /dev/null +++ b/src/qml/compiler/qv4alloca_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 QV4_ALLOCA_H +#define QV4_ALLOCA_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 + +#if QT_CONFIG(alloca_h) +# include +#elif QT_CONFIG(alloca_malloc_h) +# include +// This does not matter unless compiling in strict standard mode. +# ifdef Q_CC_MSVC +# define alloca _alloca +# endif +#else +# include +#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) + +#if QT_CONFIG(alloca) + +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast(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: + void *m_data; +}; +QT_END_NAMESPACE + +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = nullptr + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast(_qt_alloca_##name.data()) + +#endif + +#endif diff --git a/src/qml/compiler/qv4calldata_p.h b/src/qml/compiler/qv4calldata_p.h new file mode 100644 index 0000000000..5a5280cb86 --- /dev/null +++ b/src/qml/compiler/qv4calldata_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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 QV4CALLDATA_P_H +#define QV4CALLDATA_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 + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct CallData +{ + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + NewTarget = 4, + Argc = 5, + + 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(); + } + + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } + + inline ReturnedValue argument(int i) const { + return i < argc() ? args[i].asReturnedValue() + : StaticValue::undefinedValue().asReturnedValue(); + } + + StaticValue args[1]; + + static Q_DECL_CONSTEXPR int HeaderSize() + { + return offsetof(CallData, args) / sizeof(QV4::StaticValue); + } + + template + Value *argValues(); + + template + const Value *argValues() const; +}; + +Q_STATIC_ASSERT(std::is_standard_layout::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 // 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 -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QV4; - -CompilationUnitMapper::CompilationUnitMapper() - : dataPtr(nullptr) -{ - -} - -CompilationUnitMapper::~CompilationUnitMapper() -{ - close(); -} - -QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h deleted file mode 100644 index 80f914c141..0000000000 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ /dev/null @@ -1,85 +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$ -** -****************************************************************************/ - -#ifndef QV4COMPILATIONUNITMAPPER_H -#define QV4COMPILATIONUNITMAPPER_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 -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -namespace CompiledData { -struct Unit; -} - -class CompilationUnitMapper -{ -public: - CompilationUnitMapper(); - ~CompilationUnitMapper(); - - CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); - void close(); - -private: -#if defined(Q_OS_UNIX) - size_t length; -#endif - void *dataPtr; -}; - -} - -QT_END_NAMESPACE - -#endif // QV4COMPILATIONUNITMAPPER_H 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 -#include -#include -#include -#include - -#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(&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(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(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(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 -#include -#include -#include - -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(cacheFileName.constData()), - GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, - OPEN_EXISTING, nullptr); -#else - CreateFile(reinterpret_cast(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(&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(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(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 #include #include -#include #include +// 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(unit); quint32_le *stringTable = reinterpret_cast(dataStart + unit->offsetToStringTable); - char *stringData = reinterpret_cast(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); + char *stringData = reinterpret_cast(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(WTF::roundUpToMultipleOf(8, sizeof(*function))); + quint32 currentOffset = static_cast(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(b); - quint32 currentOffset = static_cast(WTF::roundUpToMultipleOf(8, sizeof(*block))); + quint32 currentOffset = static_cast(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(WTF::roundUpToMultipleOf(16, nextOffset)); + nextOffset = static_cast(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(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast(roundUpToMultipleOf(8, nextOffset)); unit.translationTableSize = translations.count(); unit.offsetToTranslationTable = nextOffset; nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData); - nextOffset = static_cast(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast(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(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast(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(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast(roundUpToMultipleOf(8, nextOffset)); unit.moduleRequestTableSize = module->moduleRequests.count(); unit.offsetToModuleRequestTable = nextOffset; nextOffset += unit.moduleRequestTableSize * sizeof(uint); - nextOffset = static_cast(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast(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(WTF::roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast(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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -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(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 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 a(scope, engine->newArrayObject(t->size)); - Scoped 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(scope, engine->memoryManager->allocate(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 *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()); - 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 *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 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 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(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 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 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 -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(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 -#include -#include -#include -#include - -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 -{ - 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; - - static QQmlRefPointer create( - CompiledData::CompilationUnit &&compilationUnit) - { - return QQmlRefPointer( - new ExecutableCompilationUnit(std::move(compilationUnit)), - QQmlRefPointer::Adopt); - } - - static QQmlRefPointer create() - { - return QQmlRefPointer( - new ExecutableCompilationUnit, - QQmlRefPointer::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 runtimeFunctions; - QVector runtimeBlocks; - mutable QVector templateObjects; - mutable QQmlNullableValue m_url; - mutable QQmlNullableValue m_finalUrl; - - // QML specific fields - QQmlPropertyCacheVector propertyCaches; - QQmlRefPointer rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } - - QQmlRefPointer typeNameCache; - - // index is object index. This allows fast access to the - // property data when initializing bindings, avoiding expensive - // lookups by string (property name). - QVector 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 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> 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 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 resolveSet; - return resolveExportRecursively(exportName, &resolveSet); - } - - QStringList exportedNames() const - { - QStringList names; - QVector 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 *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 *exportNameSet, - bool includeDefaultExport = true) const; -}; - -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType type; - QQmlRefPointer typePropertyCache; - QQmlRefPointer 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 propertyCache() const; - QQmlRefPointer 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 +#include +#include + +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((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast(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(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast(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 + StaticValue &operator=(const Value &); + + template + const Value &asValue() const; + + template + 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(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(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::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::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(toInt32(d)); + } +}; +Q_STATIC_ASSERT(std::is_trivial::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 + 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(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/qv4stringtoarrayindex_p.h b/src/qml/compiler/qv4stringtoarrayindex_p.h new file mode 100644 index 0000000000..61bd988d1e --- /dev/null +++ b/src/qml/compiler/qv4stringtoarrayindex_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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 QV4STRINGTOARRAYINDEX_P_H +#define QV4STRINGTOARRAYINDEX_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 +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +inline uint charToUInt(const QChar *ch) { return ch->unicode(); } +inline uint charToUInt(const char *ch) { return static_cast(*ch); } + +template +uint stringToArrayIndex(const T *ch, const T *end) +{ + uint i = charToUInt(ch) - '0'; + if (i > 9) + return std::numeric_limits::max(); + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return std::numeric_limits::max(); + + while (ch < end) { + uint x = charToUInt(ch) - '0'; + if (x > 9) + return std::numeric_limits::max(); + if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x + return std::numeric_limits::max(); + ++ch; + } + return i; +} + +inline uint stringToArrayIndex(const QString &str) +{ + return stringToArrayIndex(str.constData(), str.constData() + str.length()); +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#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 +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND) +// Sanity: +class BitVector +{ + std::vector bits; + +public: + BitVector(int size = 0, bool value = false) + : bits(size, value) + {} + + void clear() + { bits = std::vector(bits.size(), false); } + + void reserve(int size) + { bits.reserve(size); } + + int size() const + { + Q_ASSERT(bits.size() < INT_MAX); + return static_cast(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::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())) + pos = std::distance(bits.begin(), + std::find(bits.begin(), bits.begin() + start, value)); + + pos = qMin(pos, static_cast(size())); + + Q_ASSERT(pos <= static_cast(size())); + Q_ASSERT(pos < INT_MAX); + + return static_cast(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 diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 74e893e570..32acc6affc 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -1,7 +1,6 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD -!qmldevtools_build { SOURCES += \ $$PWD/qv4engine.cpp \ $$PWD/qv4context.cpp \ @@ -60,13 +59,15 @@ SOURCES += \ $$PWD/qv4module.cpp \ $$PWD/qv4promiseobject.cpp \ $$PWD/qv4runtime.cpp \ - $$PWD/qv4value.cpp + $$PWD/qv4value.cpp \ + $$PWD/qv4compilationunitmapper.cpp \ + $$PWD/qv4executablecompilationunit.cpp \ + $$PWD/qv4executableallocator.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp HEADERS += \ $$PWD/qv4global_p.h \ - $$PWD/qv4alloca_p.h \ $$PWD/qv4engine_p.h \ $$PWD/qv4enginebase_p.h \ $$PWD/qv4context_p.h \ @@ -133,7 +134,11 @@ HEADERS += \ $$PWD/qv4module_p.h \ $$PWD/qv4promiseobject_p.h \ $$PWD/qv4runtime_p.h \ - $$PWD/qv4value_p.h + $$PWD/qv4value_p.h \ + $$PWD/qv4compilationunitmapper_p.h \ + $$PWD/qv4executablecompilationunit_p.h \ + $$PWD/qv4functiontable_p.h \ + $$PWD/qv4runtimeapi_p.h qtConfig(qml-sequence-object) { HEADERS += \ @@ -143,24 +148,10 @@ qtConfig(qml-sequence-object) { $$PWD/qv4sequenceobject.cpp } -} - +unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp +else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -HEADERS += \ - $$PWD/qv4calldata_p.h \ - $$PWD/qv4runtimeapi_p.h \ - $$PWD/qv4stringtoarrayindex_p.h \ - $$PWD/qv4util_p.h \ - $$PWD/qv4functiontable_p.h \ - $$PWD/qv4staticvalue_p.h - -SOURCES += \ - $$PWD/qv4executableallocator.cpp - -qmldevtools_build { - SOURCES += \ - $$PWD/qv4functiontable_noop.cpp -} else:win32 { +win32 { !winrt:equals(QT_ARCH, x86_64) { SOURCES += \ $$PWD/qv4functiontable_win64.cpp diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h deleted file mode 100644 index 65c3e4d65a..0000000000 --- a/src/qml/jsruntime/qv4alloca_p.h +++ /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$ -** -****************************************************************************/ - -#ifndef QV4_ALLOCA_H -#define QV4_ALLOCA_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 - -#if QT_CONFIG(alloca_h) -# include -#elif QT_CONFIG(alloca_malloc_h) -# include -// This does not matter unless compiling in strict standard mode. -# ifdef Q_CC_MSVC -# define alloca _alloca -# endif -#else -# include -#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) - -#if QT_CONFIG(alloca) - -#define Q_ALLOCA_DECLARE(type, name) \ - type *name = 0 - -#define Q_ALLOCA_ASSIGN(type, name, size) \ - name = static_cast(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: - void *m_data; -}; -QT_END_NAMESPACE - -#define Q_ALLOCA_DECLARE(type, name) \ - Qt_AllocaWrapper _qt_alloca_##name; \ - type *name = nullptr - -#define Q_ALLOCA_ASSIGN(type, name, size) \ - _qt_alloca_##name.allocate(size); \ - name = static_cast(_qt_alloca_##name.data()) - -#endif - -#endif diff --git a/src/qml/jsruntime/qv4calldata_p.h b/src/qml/jsruntime/qv4calldata_p.h deleted file mode 100644 index 5a5280cb86..0000000000 --- a/src/qml/jsruntime/qv4calldata_p.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** 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 QV4CALLDATA_P_H -#define QV4CALLDATA_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 - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct CallData -{ - enum Offsets { - Function = 0, - Context = 1, - Accumulator = 2, - This = 3, - NewTarget = 4, - Argc = 5, - - 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(); - } - - void setArgc(int argc) { - Q_ASSERT(argc >= 0); - _argc.setInt_32(argc); - } - - inline ReturnedValue argument(int i) const { - return i < argc() ? args[i].asReturnedValue() - : StaticValue::undefinedValue().asReturnedValue(); - } - - StaticValue args[1]; - - static Q_DECL_CONSTEXPR int HeaderSize() - { - return offsetof(CallData, args) / sizeof(QV4::StaticValue); - } - - template - Value *argValues(); - - template - const Value *argValues() const; -}; - -Q_STATIC_ASSERT(std::is_standard_layout::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 // QV4CALLDATA_P_H diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp new file mode 100644 index 0000000000..350f6f9485 --- /dev/null +++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** 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 +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +CompilationUnitMapper::CompilationUnitMapper() + : dataPtr(nullptr) +{ + +} + +CompilationUnitMapper::~CompilationUnitMapper() +{ + close(); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h new file mode 100644 index 0000000000..80f914c141 --- /dev/null +++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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 QV4COMPILATIONUNITMAPPER_H +#define QV4COMPILATIONUNITMAPPER_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 +#include + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace CompiledData { +struct Unit; +} + +class CompilationUnitMapper +{ +public: + CompilationUnitMapper(); + ~CompilationUnitMapper(); + + CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); + void close(); + +private: +#if defined(Q_OS_UNIX) + size_t length; +#endif + void *dataPtr; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4COMPILATIONUNITMAPPER_H diff --git a/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp new file mode 100644 index 0000000000..6768bc9596 --- /dev/null +++ b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include + +#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(&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(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(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(dataPtr)->flags & CompiledData::Unit::StaticData)) + munmap(dataPtr, length); + } + dataPtr = nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4compilationunitmapper_win.cpp b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp new file mode 100644 index 0000000000..779c1288fe --- /dev/null +++ b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +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(cacheFileName.constData()), + GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, + OPEN_EXISTING, nullptr); +#else + CreateFile(reinterpret_cast(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(&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(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(dataPtr)->flags & CompiledData::Unit::StaticData)) + UnmapViewOfFile(dataPtr); + } + dataPtr = nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp new file mode 100644 index 0000000000..c68f6a7cbf --- /dev/null +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -0,0 +1,809 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +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(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 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 a(scope, engine->newArrayObject(t->size)); + Scoped 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(scope, engine->memoryManager->allocate(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 *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()); + 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 *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 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 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(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 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 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 +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(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/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h new file mode 100644 index 0000000000..4e3aadf28a --- /dev/null +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include + +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 +{ + 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; + + static QQmlRefPointer create( + CompiledData::CompilationUnit &&compilationUnit) + { + return QQmlRefPointer( + new ExecutableCompilationUnit(std::move(compilationUnit)), + QQmlRefPointer::Adopt); + } + + static QQmlRefPointer create() + { + return QQmlRefPointer( + new ExecutableCompilationUnit, + QQmlRefPointer::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 runtimeFunctions; + QVector runtimeBlocks; + mutable QVector templateObjects; + mutable QQmlNullableValue m_url; + mutable QQmlNullableValue m_finalUrl; + + // QML specific fields + QQmlPropertyCacheVector propertyCaches; + QQmlRefPointer rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } + + QQmlRefPointer typeNameCache; + + // index is object index. This allows fast access to the + // property data when initializing bindings, avoiding expensive + // lookups by string (property name). + QVector 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 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> 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 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 resolveSet; + return resolveExportRecursively(exportName, &resolveSet); + } + + QStringList exportedNames() const + { + QStringList names; + QVector 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 *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 *exportNameSet, + bool includeDefaultExport = true) const; +}; + +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType type; + QQmlRefPointer typePropertyCache; + QQmlRefPointer 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 propertyCache() const; + QQmlRefPointer 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/jsruntime/qv4staticvalue_p.h b/src/qml/jsruntime/qv4staticvalue_p.h deleted file mode 100644 index c6b4bdb158..0000000000 --- a/src/qml/jsruntime/qv4staticvalue_p.h +++ /dev/null @@ -1,548 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include - -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((d << 1) >> 53) == 0; - } - - int exponent() const { - return static_cast((d << 1) >> 53) - 1023; - } - - quint64 significant() const { - quint64 m = (d << 12) >> 12; - if (!isDenormal()) - m |= (static_cast(1) << 52); - return m; - } - - static int toInt32(double d) { - int i = static_cast(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(significant() >> -e); - } else { - if (e > 31) - return 0; - return sign() * (static_cast(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 - StaticValue &operator=(const Value &); - - template - const Value &asValue() const; - - template - 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(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(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::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::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(toInt32(d)); - } -}; -Q_STATIC_ASSERT(std::is_trivial::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 - 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(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/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 7888809490..52fe09cd72 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -54,7 +54,7 @@ #include "qv4managed_p.h" #include #include "qv4enginebase_p.h" -#include "qv4stringtoarrayindex_p.h" +#include QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4stringtoarrayindex_p.h b/src/qml/jsruntime/qv4stringtoarrayindex_p.h deleted file mode 100644 index 61bd988d1e..0000000000 --- a/src/qml/jsruntime/qv4stringtoarrayindex_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** 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 QV4STRINGTOARRAYINDEX_P_H -#define QV4STRINGTOARRAYINDEX_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 -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -inline uint charToUInt(const QChar *ch) { return ch->unicode(); } -inline uint charToUInt(const char *ch) { return static_cast(*ch); } - -template -uint stringToArrayIndex(const T *ch, const T *end) -{ - uint i = charToUInt(ch) - '0'; - if (i > 9) - return std::numeric_limits::max(); - ++ch; - // reject "01", "001", ... - if (i == 0 && ch != end) - return std::numeric_limits::max(); - - while (ch < end) { - uint x = charToUInt(ch) - '0'; - if (x > 9) - return std::numeric_limits::max(); - if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x - return std::numeric_limits::max(); - ++ch; - } - return i; -} - -inline uint stringToArrayIndex(const QString &str) -{ - return stringToArrayIndex(str.constData(), str.constData() + str.length()); -} - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4STRINGTOARRAYINDEX_P_H diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h deleted file mode 100644 index 073832937d..0000000000 --- a/src/qml/jsruntime/qv4util_p.h +++ /dev/null @@ -1,196 +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$ -** -****************************************************************************/ -#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 "qv4global_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND) -// Sanity: -class BitVector -{ - std::vector bits; - -public: - BitVector(int size = 0, bool value = false) - : bits(size, value) - {} - - void clear() - { bits = std::vector(bits.size(), false); } - - void reserve(int size) - { bits.reserve(size); } - - int size() const - { - Q_ASSERT(bits.size() < INT_MAX); - return static_cast(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::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())) - pos = std::distance(bits.begin(), - std::find(bits.begin(), bits.begin() + start, value)); - - pos = qMin(pos, static_cast(size())); - - Q_ASSERT(pos <= static_cast(size())); - Q_ASSERT(pos < INT_MAX); - - return static_cast(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 diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri index 7956e4a9a1..0bc8e90d27 100644 --- a/src/qml/memory/memory.pri +++ b/src/qml/memory/memory.pri @@ -1,15 +1,11 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$OUT_PWD -!qmldevtools_build { SOURCES += \ $$PWD/qv4mm.cpp \ HEADERS += \ $$PWD/qv4mm_p.h \ $$PWD/qv4mmdefs_p.h \ - $$PWD/qv4writebarrier_p.h -} - -HEADERS += \ + $$PWD/qv4writebarrier_p.h \ $$PWD/qv4heap_p.h diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 9f79bfacdf..c625743f61 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -52,7 +52,12 @@ SOURCES += \ $$PWD/qqmlfileselector.cpp \ $$PWD/qqmlobjectcreator.cpp \ $$PWD/qqmldelayedcallqueue.cpp \ - $$PWD/qqmlloggingcategory.cpp + $$PWD/qqmlloggingcategory.cpp \ + $$PWD/qqmlirloader.cpp \ + $$PWD/qqmlpropertyresolver.cpp \ + $$PWD/qqmltypecompiler.cpp \ + $$PWD/qqmlpropertycachecreator.cpp \ + $$PWD/qqmlpropertyvalidator.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -132,7 +137,12 @@ HEADERS += \ $$PWD/qqmlfileselector.h \ $$PWD/qqmlobjectcreator_p.h \ $$PWD/qqmldelayedcallqueue_p.h \ - $$PWD/qqmlloggingcategory_p.h + $$PWD/qqmlloggingcategory_p.h \ + $$PWD/qqmlirloader_p.h \ + $$PWD/qqmlpropertyresolver_p.h \ + $$PWD/qqmltypecompiler_p.h \ + $$PWD/qqmlpropertycachecreator_p.h \ + $$PWD/qqmlpropertyvalidator_p.h qtConfig(qml-xml-http-request) { HEADERS += \ diff --git a/src/qml/qml/qqmlirloader.cpp b/src/qml/qml/qqmlirloader.cpp new file mode 100644 index 0000000000..c2eb6da2fa --- /dev/null +++ b/src/qml/qml/qqmlirloader.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** 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 + +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(); + 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(); + 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 functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New(); + *static_cast(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(); + 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(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::SignalParameter *p = pool->New(); + *static_cast(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(); + e->nameIndex = serializedEnum->nameIndex; + e->location = serializedEnum->location; + e->enumValues = pool->New >(); + + for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { + QmlIR::EnumValue *v = pool->New(); + *static_cast(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(); + *static_cast(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(); + *static_cast(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(); + 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/qml/qqmlirloader_p.h b/src/qml/qml/qqmlirloader_p.h new file mode 100644 index 0000000000..aa303c923f --- /dev/null +++ b/src/qml/qml/qqmlirloader_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 QQMLIRLOADER_P_H +#define QQMLIRLOADER_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 +#include + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +struct Object; +} + +struct Q_QML_PRIVATE_EXPORT QQmlIRLoader { + QQmlIRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + +QT_END_NAMESPACE + +#endif // QQMLIRLOADER_P_H diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp new file mode 100644 index 0000000000..bd4f2a0612 --- /dev/null +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** 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 + +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 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(); +} + +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/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h new file mode 100644 index 0000000000..28eea27675 --- /dev/null +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -0,0 +1,843 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct QQmlBindingInstantiationContext { + QQmlBindingInstantiationContext() {} + QQmlBindingInstantiationContext(int referencingObjectIndex, + const QV4::CompiledData::Binding *instantiatingBinding, + const QString &instantiatingPropertyName, + QQmlPropertyCache *referencingObjectPropertyCache); + + bool resolveInstantiatingProperty(); + QQmlRefPointer instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const; + + int referencingObjectIndex = -1; + const QV4::CompiledData::Binding *instantiatingBinding = nullptr; + QString instantiatingPropertyName; + QQmlRefPointer referencingObjectPropertyCache; + QQmlPropertyData *instantiatingProperty = nullptr; +}; + +struct QQmlPendingGroupPropertyBindings : public QVector +{ + void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const; +}; + +struct QQmlPropertyCacheCreatorBase +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase) +public: + static QAtomicInt classIndexCounter; +}; + +template +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 propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const; + QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer &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 +inline QQmlPropertyCacheCreator::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 +inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() +{ + QQmlBindingInstantiationContext context; + return buildMetaObjectRecursively(/*root object*/0, context); +} + +template +inline QQmlCompileError QQmlPropertyCacheCreator::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 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 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 +inline QQmlRefPointer QQmlPropertyCacheCreator::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 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 +inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer &baseTypeCache) +{ + QQmlRefPointer 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 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 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 names; + names.reserve(paramCount); + QVarLengthArray 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 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 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 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 +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 *objectsWithAliases) const; + + int objectForId(const CompiledObject &component, int id) const; + + QQmlPropertyCacheVector *propertyCaches; + const ObjectContainer *objectContainer; +}; + +template +inline QQmlPropertyCacheAliasCreator::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer) + : propertyCaches(propertyCaches) + , objectContainer(objectContainer) +{ + +} + +template +inline void QQmlPropertyCacheAliasCreator::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 +inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) +{ + QVector 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 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 +inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAliasesRecursively(int objectIndex, QVector *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 +inline QQmlCompileError QQmlPropertyCacheAliasCreator::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 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 +inline QQmlCompileError QQmlPropertyCacheAliasCreator::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 +inline int QQmlPropertyCacheAliasCreator::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/qml/qqmlpropertyresolver.cpp b/src/qml/qml/qqmlpropertyresolver.cpp new file mode 100644 index 0000000000..90eaca0b90 --- /dev/null +++ b/src/qml/qml/qqmlpropertyresolver.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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(strlen("Changed"))); + + d = property(propName, notInRevision); + if (d) + return cache->signal(d->notifyIndex()); + } + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertyresolver_p.h b/src/qml/qml/qqmlpropertyresolver_p.h new file mode 100644 index 0000000000..df857f242e --- /dev/null +++ b/src/qml/qml/qqmlpropertyresolver_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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 QQMLPROPERTYRESOLVER_P_H +#define QQMLPROPERTYRESOLVER_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 +#include +#include + +QT_BEGIN_NAMESPACE + +struct Q_QML_EXPORT QQmlPropertyResolver +{ + QQmlPropertyResolver(const QQmlRefPointer &cache) + : cache(cache) + {} + + QQmlPropertyData *property(int index) const + { + return cache->property(index); + } + + enum RevisionCheck { + CheckRevision, + IgnoreRevision + }; + + QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, + RevisionCheck check = CheckRevision) const; + + // This code must match the semantics of QQmlPropertyPrivate::findSignalByName + QQmlPropertyData *signal(const QString &name, bool *notInRevision) const; + + QQmlRefPointer cache; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYRESOLVER_P_H diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp new file mode 100644 index 0000000000..71d5318652 --- /dev/null +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer &compilationUnit) + : enginePrivate(enginePrivate) + , compilationUnit(compilationUnit) + , imports(imports) + , qmlUnit(compilationUnit->unitData()) + , propertyCaches(compilationUnit->propertyCaches) + , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject) +{ + bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); +} + +QVector QQmlPropertyValidator::validate() +{ + return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); +} + +typedef QVarLengthArray 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 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(); + + QQmlCustomParser *customParser = nullptr; + if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { + if (typeRef->type.isValid()) + customParser = typeRef->type.customParser(); + } + + QList 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 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()) + 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 parserErrors = customParser->errors(); + if (!parserErrors.isEmpty()) + return parserErrors; + } + + (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; + + QVector 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 >()) { + 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 >()) { + 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 >()) { + 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 >()) { + 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 >()) { + if (!binding->evaluatesToString()) { + return warnOrError(tr("Invalid property assignment: string or array of strings expected")); + } + break; + } else if (property->propType() == qMetaTypeId()) { + break; + } else if (property->propType() == qMetaTypeId()) { + 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 QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const +{ + QVector errors; + errors.append(QQmlCompileError(location, description)); + return errors; +} + +QVector QQmlPropertyValidator::recordError(const QQmlCompileError &error) const +{ + QVector 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 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()) { + // 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("")) + .arg(propertyName)); + } else if (property->propType() == qMetaTypeId()) { + 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/qml/qqmlpropertyvalidator_p.h b/src/qml/qml/qqmlpropertyvalidator_p.h new file mode 100644 index 0000000000..8244b2df21 --- /dev/null +++ b/src/qml/qml/qqmlpropertyvalidator_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 + +QT_BEGIN_NAMESPACE + +class QQmlPropertyValidator +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator) +public: + QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer &compilationUnit); + + QVector validate(); + +private: + QVector 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 recordError(const QV4::CompiledData::Location &location, const QString &description) const; + Q_REQUIRED_RESULT QVector 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 compilationUnit; + const QQmlImports &imports; + const QV4::CompiledData::Unit *qmlUnit; + const QQmlPropertyCacheVector &propertyCaches; + + QVector * const bindingPropertyDataPerObject; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROPERTYVALIDATOR_P_H diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp new file mode 100644 index 0000000000..66320b8db9 --- /dev/null +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -0,0 +1,1429 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include + +#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 &typeNameCache, + QV4::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) + : resolvedTypes(resolvedTypeCache) + , engine(engine) + , typeData(typeData) + , dependencyHasher(dependencyHasher) + , typeNameCache(typeNameCache) + , document(parsedQML) +{ +} + +QQmlRefPointer 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 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 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(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 *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(); + 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 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 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 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 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::ConstIterator entry = customSignals.constFind(propertyName); + if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) { + QString alternateName = propertyName.mid(0, propertyName.length() - static_cast(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(foe->node)) { + if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast(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(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: + // * . + // * .. + + 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(); + 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(); + 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(); + *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 aliasCacheCreator(&propertyCaches, compiler); + + bool atLeastOneAliasResolved; + do { + atLeastOneAliasResolved = false; + QVector 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 &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 functionsToCompile; + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) + functionsToCompile << *foe; + const QVector runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); + const QList 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/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h new file mode 100644 index 0000000000..f588909c42 --- /dev/null +++ b/src/qml/qml/qqmltypecompiler_p.h @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include + +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 &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::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); } + QmlIR::PoolList::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); } + QV4::ResolvedTypeReferenceMap *resolvedTypes = nullptr; + // --- + + QQmlRefPointer compile(); + + QList 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 *qmlObjects() const; + void setPropertyCaches(QQmlPropertyCacheVector &&caches); + const QQmlPropertyCacheVector *propertyCaches() const; + QQmlPropertyCacheVector &&takePropertyCaches(); + void setComponentRoots(const QVector &roots) { m_componentRoots = roots; } + const QVector &componentRoots() const { return m_componentRoots; } + QQmlJS::MemoryPool *memoryPool(); + QStringRef newStringRef(const QString &string); + const QV4::Compiler::StringTableGenerator *stringPool() const; + + const QHash &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 errors; + QQmlEnginePrivate *engine; + QQmlTypeData *typeData; + const QV4::CompiledData::DependentTypesHasher &dependencyHasher; + QQmlRefPointer typeNameCache; + QmlIR::Document *document; + // index is string index of type name (use obj->inheritedTypeNameIndex) + QHash customParsers; + + // index in first hash is component index, vector inside contains object indices of objects with id property + QVector 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 &qmlObjects; + const QQmlImports *imports; + const QHash &customParsers; + const QSet &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 &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 &qmlObjects; + const QHash &customParsers; +}; + +// Annotate properties bound to aliases with a flag +class QQmlAliasAnnotator : public QQmlCompilePass +{ +public: + QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler); + + void annotateBindingsToAliases(); +private: + const QVector &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; +}; + +class QQmlScriptStringScanner : public QQmlCompilePass +{ +public: + QQmlScriptStringScanner(QQmlTypeCompiler *typeCompiler); + + void scan(); + +private: + const QVector &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 *qmlObjects; + + // indices of the objects that are actually Component {} + QVector componentRoots; + + // Deliberate choice of map over hash here to ensure stable generated output. + QMap _idToObjectIndex; + QVector _objectsWithAliases; + + QQmlPropertyCacheVector propertyCaches; +}; + +class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass +{ +public: + QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); + + bool scanObject(); + +private: + bool scanObject(int objectIndex); + + QVector *qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; + const QHash &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 &customParsers; + const QVector &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 &qmlObjects; + const QQmlPropertyCacheVector * const propertyCaches; +}; + +QT_END_NAMESPACE + +#endif // QQMLTYPECOMPILER_P_H diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index 45029400b9..4227312f3f 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -1,7 +1,7 @@ option(host_build) TARGET = QtQmlDevTools QT = core-private -CONFIG += minimal_syncqt internal_module qmldevtools_build generated_privates +CONFIG += minimal_syncqt internal_module generated_privates MODULE_INCNAME = QtQml INCLUDEPATH += $$OUT_PWD/../qml @@ -12,11 +12,8 @@ intel_icc: WERROR += -ww2415 clang:if(greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)): \ WERROR += -Wno-error=unused-const-variable -include(../3rdparty/masm/masm-defs.pri) include(../qml/parser/parser.pri) -include(../qml/jsruntime/jsruntime.pri) include(../qml/compiler/compiler.pri) -include(../qml/memory/memory.pri) include(../qml/qmldirparser/qmldirparser.pri) load(qt_module) -- cgit v1.2.3