diff options
Diffstat (limited to 'src/qml/compiler/qqmlirbuilder.cpp')
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 972 |
1 files changed, 519 insertions, 453 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index c7343e709c..a81f8fb1d8 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmlirbuilder_p.h" @@ -48,12 +12,10 @@ #include <QCryptographicHash> #include <cmath> -#ifdef CONST -#undef CONST -#endif - QT_USE_NAMESPACE +using namespace Qt::StringLiterals; + static const quint32 emptyStringIndex = 0; using namespace QmlIR; using namespace QQmlJS; @@ -76,7 +38,7 @@ void Object::simplifyRequiredProperties() { for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) { auto requiredIt = required.find(it->nameIndex); if (requiredIt != required.end()) { - it->isRequired = true; + it->setIsRequired(true); required.erase(requiredIt); } } @@ -91,71 +53,48 @@ void Object::simplifyRequiredProperties() { } } -bool Parameter::init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, - const QString &typeName) -{ - return init(this, stringGenerator, stringGenerator->registerString(parameterName), stringGenerator->registerString(typeName)); -} - -bool Parameter::init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator, - int parameterNameIndex, int typeNameIndex) +bool Parameter::initType( + QV4::CompiledData::ParameterType *paramType, + const QString &typeName, int typeNameIndex, + QV4::CompiledData::ParameterType::Flag listFlag) { - param->nameIndex = parameterNameIndex; - return initType(¶m->type, stringGenerator, typeNameIndex); -} - -bool Parameter::initType(QV4::CompiledData::ParameterType *paramType, const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex) -{ - paramType->indexIsBuiltinType = false; - paramType->typeNameIndexOrBuiltinType = 0; - const QString typeName = stringGenerator->stringForIndex(typeNameIndex); auto builtinType = stringToBuiltinType(typeName); - if (builtinType == QV4::CompiledData::BuiltinType::InvalidBuiltin) { - if (typeName.isEmpty() || !typeName.at(0).isUpper()) + if (builtinType == QV4::CompiledData::CommonType::Invalid) { + if (typeName.isEmpty()) { + paramType->set(listFlag, 0); return false; - paramType->indexIsBuiltinType = false; - paramType->typeNameIndexOrBuiltinType = typeNameIndex; + } Q_ASSERT(quint32(typeNameIndex) < (1u << 31)); + paramType->set(listFlag, typeNameIndex); } else { - paramType->indexIsBuiltinType = true; - paramType->typeNameIndexOrBuiltinType = static_cast<quint32>(builtinType); Q_ASSERT(quint32(builtinType) < (1u << 31)); + paramType->set(listFlag | QV4::CompiledData::ParameterType::Common, + static_cast<quint32>(builtinType)); } return true; } -QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typeName) +QV4::CompiledData::CommonType Parameter::stringToBuiltinType(const QString &typeName) { static const struct TypeNameToType { const char *name; size_t nameLength; - QV4::CompiledData::BuiltinType type; + QV4::CompiledData::CommonType type; } propTypeNameToTypes[] = { - { "int", strlen("int"), QV4::CompiledData::BuiltinType::Int }, - { "bool", strlen("bool"), QV4::CompiledData::BuiltinType::Bool }, - { "double", strlen("double"), QV4::CompiledData::BuiltinType::Real }, - { "real", strlen("real"), QV4::CompiledData::BuiltinType::Real }, - { "string", strlen("string"), QV4::CompiledData::BuiltinType::String }, - { "url", strlen("url"), QV4::CompiledData::BuiltinType::Url }, - { "color", strlen("color"), QV4::CompiledData::BuiltinType::Color }, - // Internally QTime, QDate and QDateTime are all supported. - // To be more consistent with JavaScript we expose only - // QDateTime as it matches closely with the Date JS type. - // We also call it "date" to match. - // { "time", strlen("time"), Property::Time }, - // { "date", strlen("date"), Property::Date }, - { "date", strlen("date"), QV4::CompiledData::BuiltinType::DateTime }, - { "rect", strlen("rect"), QV4::CompiledData::BuiltinType::Rect }, - { "point", strlen("point"), QV4::CompiledData::BuiltinType::Point }, - { "size", strlen("size"), QV4::CompiledData::BuiltinType::Size }, - { "font", strlen("font"), QV4::CompiledData::BuiltinType::Font }, - { "vector2d", strlen("vector2d"), QV4::CompiledData::BuiltinType::Vector2D }, - { "vector3d", strlen("vector3d"), QV4::CompiledData::BuiltinType::Vector3D }, - { "vector4d", strlen("vector4d"), QV4::CompiledData::BuiltinType::Vector4D }, - { "quaternion", strlen("quaternion"), QV4::CompiledData::BuiltinType::Quaternion }, - { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::BuiltinType::Matrix4x4 }, - { "variant", strlen("variant"), QV4::CompiledData::BuiltinType::Var }, - { "var", strlen("var"), QV4::CompiledData::BuiltinType::Var } + { "void", strlen("void"), QV4::CompiledData::CommonType::Void }, + { "int", strlen("int"), QV4::CompiledData::CommonType::Int }, + { "bool", strlen("bool"), QV4::CompiledData::CommonType::Bool }, + { "double", strlen("double"), QV4::CompiledData::CommonType::Real }, + { "real", strlen("real"), QV4::CompiledData::CommonType::Real }, + { "string", strlen("string"), QV4::CompiledData::CommonType::String }, + { "url", strlen("url"), QV4::CompiledData::CommonType::Url }, + { "date", strlen("date"), QV4::CompiledData::CommonType::DateTime }, + { "regexp", strlen("regexp"), QV4::CompiledData::CommonType::RegExp }, + { "rect", strlen("rect"), QV4::CompiledData::CommonType::Rect }, + { "point", strlen("point"), QV4::CompiledData::CommonType::Point }, + { "size", strlen("size"), QV4::CompiledData::CommonType::Size }, + { "variant", strlen("variant"), QV4::CompiledData::CommonType::Var }, + { "var", strlen("var"), QV4::CompiledData::CommonType::Var } }; static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / sizeof(propTypeNameToTypes[0]); @@ -166,13 +105,13 @@ QV4::CompiledData::BuiltinType Parameter::stringToBuiltinType(const QString &typ return t->type; } } - return QV4::CompiledData::BuiltinType::InvalidBuiltin; + return QV4::CompiledData::CommonType::Invalid; } void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QV4::CompiledData::Location &loc) { - Q_ASSERT(loc.line > 0 && loc.column > 0); + Q_ASSERT(loc.line() > 0 && loc.column() > 0); inheritedTypeNameIndex = typeNameIndex; location = loc; idNameIndex = idIndex; @@ -197,8 +136,8 @@ QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &il QSet<int> functionNames; for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) { Function *f = functionit.ptr; - errorLocation->startLine = f->location.line; - errorLocation->startColumn = f->location.column; + errorLocation->startLine = f->location.line(); + errorLocation->startColumn = f->location.column(); if (functionNames.contains(f->nameIndex)) return tr("Duplicate method name"); functionNames.insert(f->nameIndex); @@ -258,6 +197,10 @@ QString Object::appendProperty(Property *prop, const QString &propertyName, bool if (p->nameIndex == prop->nameIndex) return tr("Duplicate property name"); + for (Alias *a = target->aliases->first; a; a = a->next) + if (a->nameIndex() == prop->nameIndex) + return tr("Property duplicates alias name"); + if (propertyName.constData()->isUpper()) return tr("Property names cannot begin with an upper case letter"); @@ -278,12 +221,19 @@ QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefau if (!target) target = this; - auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){ - return targetAlias.nameIndex == alias->nameIndex; + const auto aliasWithSameName = std::find_if(target->aliases->begin(), target->aliases->end(), [&alias](const Alias &targetAlias){ + return targetAlias.nameIndex() == alias->nameIndex(); }); if (aliasWithSameName != target->aliases->end()) return tr("Duplicate alias name"); + const auto aliasSameAsProperty = std::find_if(target->properties->begin(), target->properties->end(), [&alias](const Property &targetProp){ + return targetProp.nameIndex == alias->nameIndex(); + }); + + if (aliasSameAsProperty != target->properties->end()) + return tr("Alias has same name as existing property"); + if (aliasName.constData()->isUpper()) return tr("Alias names cannot begin with an upper case letter"); @@ -303,10 +253,11 @@ QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefau void Object::appendFunction(QmlIR::Function *f) { - Object *target = declarationsOverride; - if (!target) - target = this; - target->functions->append(f); + // Unlike properties, a function definition inside a grouped property does not go into + // the surrounding object. It's been broken since the Qt 5 era, and the semantics + // seems super confusing, so it wouldn't make sense to support that. + Q_ASSERT(!declarationsOverride); + functions->append(f); } void Object::appendInlineComponent(InlineComponent *ic) @@ -322,13 +273,17 @@ void Object::appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraDat QString Object::appendBinding(Binding *b, bool isListBinding) { const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0)); - if (!isListBinding && !bindingToDefaultProperty - && b->type != QV4::CompiledData::Binding::Type_GroupProperty - && b->type != QV4::CompiledData::Binding::Type_AttachedProperty - && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) { + if (!isListBinding + && !bindingToDefaultProperty + && b->type() != QV4::CompiledData::Binding::Type_GroupProperty + && b->type() != QV4::CompiledData::Binding::Type_AttachedProperty + && !b->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) { Binding *existing = findBinding(b->propertyNameIndex); - if (existing && existing->isValueBinding() == b->isValueBinding() && !(existing->flags & QV4::CompiledData::Binding::IsOnAssignment)) + if (existing + && existing->isValueBinding() == b->isValueBinding() + && !existing->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) { return tr("Property value set multiple times"); + } } if (bindingToDefaultProperty) insertSorted(b); @@ -396,8 +351,7 @@ void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString import->type = QV4::CompiledData::Import::ImportScript; import->uriIndex = jsGenerator->registerString(jsfile); import->qualifierIndex = jsGenerator->registerString(module); - import->location.line = lineNumber; - import->location.column = column; + import->location.set(lineNumber, column); document->imports << import; } @@ -406,10 +360,9 @@ void ScriptDirectivesCollector::importModule(const QString &uri, const QString & QV4::CompiledData::Import *import = engine->pool()->New<QV4::CompiledData::Import>(); import->type = QV4::CompiledData::Import::ImportLibrary; import->uriIndex = jsGenerator->registerString(uri); - import->version = IRBuilder::extractVersion(QStringView(version)); + import->version = IRBuilder::extractVersion(version); import->qualifierIndex = jsGenerator->registerString(module); - import->location.line = lineNumber; - import->location.column = column; + import->location.set(lineNumber, column); document->imports << import; } @@ -443,7 +396,9 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen errors << m; } - return false; + + if (!errors.isEmpty() || !parseResult) + return false; } program = parser.ast(); Q_ASSERT(program); @@ -487,39 +442,6 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen return errors.isEmpty(); } -bool IRBuilder::isSignalPropertyName(const QString &name) -{ - if (name.length() < 3) return false; - if (!name.startsWith(QLatin1String("on"))) return false; - int ns = name.length(); - for (int i = 2; i < ns; ++i) { - const QChar curr = name.at(i); - if (curr.unicode() == '_') continue; - if (curr.isUpper()) return true; - return false; - } - return false; // consists solely of underscores - invalid. -} - -QString IRBuilder::signalNameFromSignalPropertyName(const QString &signalPropertyName) -{ - Q_ASSERT(signalPropertyName.startsWith(QLatin1String("on"))); - QString signalNameCandidate = signalPropertyName; - signalNameCandidate.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 < signalNameCandidate.size(); ++firstAlphaIndex) { - if (signalNameCandidate.at(firstAlphaIndex).isUpper()) { - signalNameCandidate[firstAlphaIndex] = signalNameCandidate.at(firstAlphaIndex).toLower(); - return signalNameCandidate; - } - } - - Q_UNREACHABLE(); - return QString(); -} - bool IRBuilder::visit(QQmlJS::AST::UiArrayMemberList *ast) { return QQmlJS::AST::Visitor::visit(ast); @@ -586,14 +508,12 @@ bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast) Q_ASSERT(idx > 0); Object* definedObject = _objects.at(idx); definedObject->flags |= QV4::CompiledData::Object::IsInlineComponentRoot; - definedObject->flags |= QV4::CompiledData::Object::InPartOfInlineComponent; - definedObject->isInlineComponent = true; + definedObject->flags |= QV4::CompiledData::Object::IsPartOfInlineComponent; auto inlineComponent = New<InlineComponent>(); inlineComponent->nameIndex = registerString(ast->name.toString()); inlineComponent->objectIndex = idx; auto location = ast->firstSourceLocation(); - inlineComponent->location.line = location.startLine; - inlineComponent->location.column = location.startColumn; + inlineComponent->location.set(location.startLine, location.startColumn); _object->appendInlineComponent(inlineComponent); return false; } @@ -639,7 +559,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiArrayBinding *node) memberList.append(member); member = member->next; } - for (int i = memberList.count() - 1; i >= 0; --i) { + for (int i = memberList.size() - 1; i >= 0; --i) { member = memberList.at(i); QQmlJS::AST::UiObjectDefinition *def = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(member->member); @@ -706,7 +626,7 @@ bool IRBuilder::defineQMLObject( _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location); _object->declarationsOverride = declarationsOverride; if (insideInlineComponent) { - _object->flags |= QV4::CompiledData::Object::InPartOfInlineComponent; + _object->flags |= QV4::CompiledData::Object::IsPartOfInlineComponent; } // A new object is also a boundary for property declarations. @@ -767,7 +687,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) // Check for script qualifier clashes bool isScript = import->type == QV4::CompiledData::Import::ImportScript; - for (int ii = 0; ii < _imports.count(); ++ii) { + for (int ii = 0; ii < _imports.size(); ++ii) { const QV4::CompiledData::Import *other = _imports.at(ii); bool otherIsScript = other->type == QV4::CompiledData::Import::ImportScript; @@ -789,8 +709,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) import->version = QTypeRevision(); } - import->location.line = node->importToken.startLine; - import->location.column = node->importToken.startColumn; + import->location.set(node->importToken.startLine, node->importToken.startColumn); import->uriIndex = registerString(uri); @@ -799,28 +718,225 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) return false; } + +template<typename Argument> +struct PragmaParser +{ + static bool run(IRBuilder *builder, QQmlJS::AST::UiPragma *node, Pragma *pragma) + { + Q_ASSERT(builder); + Q_ASSERT(node); + Q_ASSERT(pragma); + + if (!isUnique(builder)) { + builder->recordError( + node->pragmaToken, QCoreApplication::translate( + "QQmlParser", "Multiple %1 pragmas found").arg(name())); + return false; + } + + pragma->type = type(); + + if (QQmlJS::AST::UiPragmaValueList *bad = assign(pragma, node->values)) { + builder->recordError( + node->pragmaToken, QCoreApplication::translate( + "QQmlParser", "Unknown %1 '%2' in pragma").arg(name(), bad->value)); + return false; + } + + return true; + } + +private: + static constexpr Pragma::PragmaType type() + { + if constexpr (std::is_same_v<Argument, Pragma::ComponentBehaviorValue>) { + return Pragma::ComponentBehavior; + } else if constexpr (std::is_same_v<Argument, Pragma::ListPropertyAssignBehaviorValue>) { + return Pragma::ListPropertyAssignBehavior; + } else if constexpr (std::is_same_v<Argument, Pragma::FunctionSignatureBehaviorValue>) { + return Pragma::FunctionSignatureBehavior; + } else if constexpr (std::is_same_v<Argument, Pragma::NativeMethodBehaviorValue>) { + return Pragma::NativeMethodBehavior; + } else if constexpr (std::is_same_v<Argument, Pragma::ValueTypeBehaviorValue>) { + return Pragma::ValueTypeBehavior; + } + + Q_UNREACHABLE_RETURN(Pragma::PragmaType(-1)); + } + + template<typename F> + static QQmlJS::AST::UiPragmaValueList *iterateValues( + QQmlJS::AST::UiPragmaValueList *input, F &&process) + { + for (QQmlJS::AST::UiPragmaValueList *i = input; i; i = i->next) { + if (!process(i->value)) + return i; + } + return nullptr; + } + + static QQmlJS::AST::UiPragmaValueList *assign( + Pragma *pragma, QQmlJS::AST::UiPragmaValueList *values) + { + // We could use QMetaEnum here to make the code more compact, + // but it's probably more expensive. + + if constexpr (std::is_same_v<Argument, Pragma::ComponentBehaviorValue>) { + return iterateValues(values, [pragma](QStringView value) { + if (value == "Unbound"_L1) { + pragma->componentBehavior = Pragma::Unbound; + return true; + } + if (value == "Bound"_L1) { + pragma->componentBehavior = Pragma::Bound; + return true; + } + return false; + }); + } else if constexpr (std::is_same_v<Argument, Pragma::ListPropertyAssignBehaviorValue>) { + return iterateValues(values, [pragma](QStringView value) { + if (value == "Append"_L1) { + pragma->listPropertyAssignBehavior = Pragma::Append; + return true; + } + if (value == "Replace"_L1) { + pragma->listPropertyAssignBehavior = Pragma::Replace; + return true; + } + if (value == "ReplaceIfNotDefault"_L1) { + pragma->listPropertyAssignBehavior = Pragma::ReplaceIfNotDefault; + return true; + } + return false; + }); + } else if constexpr (std::is_same_v<Argument, Pragma::FunctionSignatureBehaviorValue>) { + return iterateValues(values, [pragma](QStringView value) { + if (value == "Ignored"_L1) { + pragma->functionSignatureBehavior = Pragma::Ignored; + return true; + } + if (value == "Enforced"_L1) { + pragma->functionSignatureBehavior = Pragma::Enforced; + return true; + } + return false; + }); + } else if constexpr (std::is_same_v<Argument, Pragma::NativeMethodBehaviorValue>) { + return iterateValues(values, [pragma](QStringView value) { + if (value == "AcceptThisObject"_L1) { + pragma->nativeMethodBehavior = Pragma::AcceptThisObject; + return true; + } + if (value == "RejectThisObject"_L1) { + pragma->nativeMethodBehavior = Pragma::RejectThisObject; + return true; + } + return false; + }); + } else if constexpr (std::is_same_v<Argument, Pragma::ValueTypeBehaviorValue>) { + pragma->valueTypeBehavior = Pragma::ValueTypeBehaviorValues().toInt(); + return iterateValues(values, [pragma](QStringView value) { + const auto setFlag = [pragma](Pragma::ValueTypeBehaviorValue flag, bool value) { + pragma->valueTypeBehavior + = Pragma::ValueTypeBehaviorValues(pragma->valueTypeBehavior) + .setFlag(flag, value).toInt(); + }; + + if (value == "Reference"_L1) { + setFlag(Pragma::Copy, false); + return true; + } + if (value == "Copy"_L1) { + setFlag(Pragma::Copy, true); + return true; + } + + if (value == "Inaddressable"_L1) { + setFlag(Pragma::Addressable, false); + return true; + } + if (value == "Addressable"_L1) { + setFlag(Pragma::Addressable, true); + return true; + } + + return false; + }); + } + + Q_UNREACHABLE_RETURN(nullptr); + } + + static bool isUnique(IRBuilder *builder) + { + for (const Pragma *prev : builder->_pragmas) { + if (prev->type == type()) + return false; + } + return true; + }; + + static QLatin1StringView name() + { + switch (type()) { + case Pragma::ListPropertyAssignBehavior: + return "list property assign behavior"_L1; + case Pragma::ComponentBehavior: + return "component behavior"_L1; + case Pragma::FunctionSignatureBehavior: + return "function signature behavior"_L1; + case Pragma::NativeMethodBehavior: + return "native method behavior"_L1; + case Pragma::ValueTypeBehavior: + return "value type behavior"_L1; + default: + break; + } + Q_UNREACHABLE_RETURN(QLatin1StringView()); + } +}; + bool IRBuilder::visit(QQmlJS::AST::UiPragma *node) { Pragma *pragma = New<Pragma>(); - // For now the only valid pragma is Singleton, so lets validate the input - if (!node->name.isNull()) - { - if (node->name == QStringLiteral("Singleton")) { - pragma->type = Pragma::PragmaSingleton; - } else if (node->name == QStringLiteral("Strict")) { - pragma->type = Pragma::PragmaStrict; + if (!node->name.isNull()) { + if (node->name == "Singleton"_L1) { + pragma->type = Pragma::Singleton; + } else if (node->name == "Strict"_L1) { + pragma->type = Pragma::Strict; + } else if (node->name == "ComponentBehavior"_L1) { + if (!PragmaParser<Pragma::ComponentBehaviorValue>::run(this, node, pragma)) + return false; + } else if (node->name == "ListPropertyAssignBehavior"_L1) { + if (!PragmaParser<Pragma::ListPropertyAssignBehaviorValue>::run(this, node, pragma)) + return false; + } else if (node->name == "FunctionSignatureBehavior"_L1) { + if (!PragmaParser<Pragma::FunctionSignatureBehaviorValue>::run(this, node, pragma)) + return false; + } else if (node->name == "NativeMethodBehavior"_L1) { + if (!PragmaParser<Pragma::NativeMethodBehaviorValue>::run(this, node, pragma)) + return false; + } else if (node->name == "ValueTypeBehavior"_L1) { + if (!PragmaParser<Pragma::ValueTypeBehaviorValue>::run(this, node, pragma)) + return false; + } else if (node->name == "Translator"_L1) { + pragma->type = Pragma::Translator; + pragma->translationContextIndex = registerString(node->values->value.toString()); + } else { - recordError(node->pragmaToken, QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier")); + recordError(node->pragmaToken, QCoreApplication::translate( + "QQmlParser", "Unknown pragma '%1'").arg(node->name)); return false; } } else { - recordError(node->pragmaToken, QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier")); + recordError(node->pragmaToken, QCoreApplication::translate( + "QQmlParser", "Empty pragma found")); return false; } - pragma->location.line = node->pragmaToken.startLine; - pragma->location.column = node->pragmaToken.startColumn; + pragma->location.set(node->pragmaToken.startLine, node->pragmaToken.startColumn); _pragmas.append(pragma); return false; @@ -853,8 +969,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) if (enumName.at(0).isLower()) COMPILE_EXCEPTION(node->enumToken, tr("Scoped enum names must begin with an upper case letter")); - enumeration->location.line = node->enumToken.startLine; - enumeration->location.column = node->enumToken.startColumn; + enumeration->location.set(node->enumToken.startLine, node->enumToken.startColumn); enumeration->enumValues = New<PoolList<EnumValue>>(); @@ -873,8 +988,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) COMPILE_EXCEPTION(e->valueToken, tr("Enum value out of range")); enumValue->value = e->value; - enumValue->location.line = e->memberToken.startLine; - enumValue->location.column = e->memberToken.startColumn; + enumValue->location.set(e->memberToken.startLine, e->memberToken.startColumn); enumeration->enumValues->append(enumValue); e = e->next; @@ -898,24 +1012,24 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) signal->nameIndex = registerString(signalName); QQmlJS::SourceLocation loc = node->typeToken; - signal->location.line = loc.startLine; - signal->location.column = loc.startColumn; + signal->location.set(loc.startLine, loc.startColumn); signal->parameters = New<PoolList<Parameter> >(); QQmlJS::AST::UiParameterList *p = node->parameters; while (p) { - const QString memberType = asString(p->type); - - if (memberType.isEmpty()) { + if (!p->type) { recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected parameter type")); return false; } Parameter *param = New<Parameter>(); - if (!param->init(jsGenerator, p->name.toString(), memberType)) { + param->nameIndex = registerString(p->name.toString()); + if (!Parameter::initType( + ¶m->type, [this](const QString &str) { return registerString(str); }, + p->type)) { QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); - errStr.append(memberType); + errStr.append(p->type->toString()); recordError(node->typeToken, errStr); return false; } @@ -945,35 +1059,24 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) if (memberType == QLatin1String("alias")) { return appendAlias(node); } else { - const QStringView &name = node->name; + QStringView name = node->name; Property *property = New<Property>(); - property->isReadOnly = node->isReadonlyMember; - property->isRequired = node->isRequired; - - QV4::CompiledData::BuiltinType builtinPropertyType = Parameter::stringToBuiltinType(memberType); - bool typeFound = builtinPropertyType != QV4::CompiledData::BuiltinType::InvalidBuiltin; - if (typeFound) - property->setBuiltinType(builtinPropertyType); - - if (!typeFound && memberType.at(0).isUpper()) { - const QStringView &typeModifier = node->typeModifier; - - property->setCustomType(registerString(memberType)); - if (typeModifier == QLatin1String("list")) { - property->isList = true; - } else if (!typeModifier.isEmpty()) { - recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); - return false; - } - typeFound = true; - } else if (!node->typeModifier.isNull()) { - recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier")); - return false; - } + property->setIsReadOnly(node->isReadonly()); + property->setIsRequired(node->isRequired()); - if (!typeFound) { - recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type")); + const QV4::CompiledData::CommonType builtinPropertyType + = Parameter::stringToBuiltinType(memberType); + if (builtinPropertyType != QV4::CompiledData::CommonType::Invalid) + property->setCommonType(builtinPropertyType); + else + property->setTypeNameIndex(registerString(memberType)); + + QStringView typeModifier = node->typeModifier; + if (typeModifier == QLatin1String("list")) { + property->setIsList(true); + } else if (!typeModifier.isEmpty()) { + recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); return false; } @@ -981,8 +1084,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) property->nameIndex = registerString(propName); QQmlJS::SourceLocation loc = node->firstSourceLocation(); - property->location.line = loc.startLine; - property->location.column = loc.startColumn; + property->location.set(loc.startLine, loc.startColumn); QQmlJS::SourceLocation errorLocation; QString error; @@ -990,7 +1092,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) if (illegalNames.contains(propName)) error = tr("Illegal property name"); else - error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation); + error = _object->appendProperty(property, propName, node->isDefaultMember(), node->defaultToken(), &errorLocation); if (!error.isEmpty()) { if (errorLocation.startLine == 0) @@ -1018,6 +1120,14 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) { if (QQmlJS::AST::FunctionExpression *funDecl = node->sourceElement->asFunctionDefinition()) { + if (_object->declarationsOverride) { + // See Object::appendFunction() for why. + recordError(node->firstSourceLocation(), + QCoreApplication::translate( + "QQmlParser", "Function declaration inside grouped property")); + return false; + } + CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>(); foe->node = funDecl; foe->parentNode = funDecl; @@ -1026,13 +1136,15 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) Function *f = New<Function>(); QQmlJS::SourceLocation loc = funDecl->identifierToken; - f->location.line = loc.startLine; - f->location.column = loc.startColumn; + f->location.set(loc.startLine, loc.startColumn); f->index = index; f->nameIndex = registerString(funDecl->name.toString()); - QString returnTypeName = funDecl->typeAnnotation ? funDecl->typeAnnotation->type->toString() : QString(); - Parameter::initType(&f->returnType, jsGenerator, registerString(returnTypeName)); + const auto idGenerator = [this](const QString &str) { return registerString(str); }; + + Parameter::initType( + &f->returnType, idGenerator, + funDecl->typeAnnotation ? funDecl->typeAnnotation->type : nullptr); const QQmlJS::AST::BoundNames formals = funDecl->formals ? funDecl->formals->formals() : QQmlJS::AST::BoundNames(); int formalsCount = formals.size(); @@ -1040,7 +1152,11 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) int i = 0; for (const auto &arg : formals) { - f->formals[i].init(jsGenerator, arg.id, arg.typeName()); + Parameter *functionParameter = &f->formals[i]; + functionParameter->nameIndex = registerString(arg.id); + Parameter::initType( + &functionParameter->type, idGenerator, + arg.typeAnnotation.isNull() ? nullptr : arg.typeAnnotation->type); ++i; } @@ -1100,26 +1216,31 @@ QStringView IRBuilder::textRefAt(const QQmlJS::SourceLocation &first, const QQml void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode) { QQmlJS::SourceLocation loc = statement->firstSourceLocation(); - binding->valueLocation.line = loc.startLine; - binding->valueLocation.column = loc.startColumn; - binding->type = QV4::CompiledData::Binding::Type_Invalid; - if (_propertyDeclaration && _propertyDeclaration->isReadOnly) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + binding->valueLocation.set(loc.startLine, loc.startColumn); + binding->setType(QV4::CompiledData::Binding::Type_Invalid); + if (_propertyDeclaration && _propertyDeclaration->isReadOnly()) + binding->setFlag(QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration); QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); if (exprStmt) { QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression; if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) { - binding->type = QV4::CompiledData::Binding::Type_String; + binding->setType(QV4::CompiledData::Binding::Type_String); binding->stringIndex = registerString(lit->value.toString()); + } else if (QQmlJS::AST::TemplateLiteral *templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr); + templateLit && templateLit->hasNoSubstitution) { + // A template literal without substitution is just a string. + // With substitution, it could however be an arbitrarily complex expression + binding->setType(QV4::CompiledData::Binding::Type_String); + binding->stringIndex = registerString(templateLit->value.toString()); } else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) { - binding->type = QV4::CompiledData::Binding::Type_Boolean; + binding->setType(QV4::CompiledData::Binding::Type_Boolean); binding->value.b = true; } else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) { - binding->type = QV4::CompiledData::Binding::Type_Boolean; + binding->setType(QV4::CompiledData::Binding::Type_Boolean); binding->value.b = false; } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) { - binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setType(QV4::CompiledData::Binding::Type_Number); binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value)); } else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) { if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) { @@ -1128,21 +1249,21 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST // below. } } else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) { - binding->flags |= QV4::CompiledData::Binding::IsFunctionExpression; + binding->setFlag(QV4::CompiledData::Binding::IsFunctionExpression); } else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { - binding->type = QV4::CompiledData::Binding::Type_Number; + binding->setType(QV4::CompiledData::Binding::Type_Number); binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value)); } } else if (QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr)) { - binding->type = QV4::CompiledData::Binding::Type_Null; + binding->setType(QV4::CompiledData::Binding::Type_Null); binding->value.nullMarker = 0; } } // Do binding instead - if (binding->type == QV4::CompiledData::Binding::Type_Invalid) { - binding->type = QV4::CompiledData::Binding::Type_Script; + if (binding->type() == QV4::CompiledData::Binding::Type_Invalid) { + binding->setType(QV4::CompiledData::Binding::Type_Script); CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>(); expr->node = statement; @@ -1153,125 +1274,39 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST binding->value.compiledScriptIndex = index; // We don't need to store the binding script as string, except for script strings // and types with custom parsers. Those will be added later in the compilation phase. - binding->stringIndex = emptyStringIndex; + // Except that we cannot recover the string when cachegen runs; we need to therefore retain + // "undefined". Any other "special" strings (for the various literals) are already handled above + QQmlJS::AST::Node *nodeForString = statement; + if (exprStmt) + nodeForString = exprStmt->expression; + if (asStringRef(nodeForString) == u"undefined") + binding->stringIndex = registerString(u"undefined"_s); + else + binding->stringIndex = emptyStringIndex; } } void IRBuilder::tryGeneratingTranslationBinding(QStringView base, AST::ArgumentList *args, QV4::CompiledData::Binding *binding) { - if (base == QLatin1String("qsTr")) { - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string - translationData.padding = 0; - - if (!args || !args->expression) - return; // no arguments, stop - - QStringView translation; - if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { - translation = arg1->value; - } else { - return; // first argument is not a string, stop - } - translationData.stringIndex = jsGenerator->registerString(translation.toString()); - - args = args->next; - - if (args) { - QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression); - if (!arg2) - return; // second argument is not a string, stop - translationData.commentIndex = jsGenerator->registerString(arg2->value.toString()); - - args = args->next; - if (args) { - if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { - translationData.number = int(arg3->value); - args = args->next; - } else { - return; // third argument is not a translation number, stop - } - } - } - - if (args) - return; // too many arguments, stop - - binding->type = QV4::CompiledData::Binding::Type_Translation; - binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData); - } else if (base == QLatin1String("qsTrId")) { - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string, but unused - translationData.padding = 0; - - if (!args || !args->expression) - return; // no arguments, stop - - QStringView id; - if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { - id = arg1->value; - } else { - return; // first argument is not a string, stop - } - translationData.stringIndex = jsGenerator->registerString(id.toString()); - - args = args->next; - - if (args) { - if (QQmlJS::AST::NumericLiteral *arg3 = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { - translationData.number = int(arg3->value); - args = args->next; - } else { - return; // third argument is not a translation number, stop - } - } - - if (args) - return; // too many arguments, stop - - binding->type = QV4::CompiledData::Binding::Type_TranslationById; - binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData); - } else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) { - if (!args || !args->expression) - return; // no arguments, stop - - QStringView str; - if (QQmlJS::AST::StringLiteral *arg1 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { - str = arg1->value; - } else { - return; // first argument is not a string, stop - } - - args = args->next; - if (args) - return; // too many arguments, stop - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = jsGenerator->registerString(str.toString()); - } else if (base == QLatin1String("QT_TRANSLATE_NOOP")) { - if (!args || !args->expression) - return; // no arguments, stop - - args = args->next; - if (!args || !args->expression) - return; // no second arguments, stop + const auto registerString = [&](QStringView string) { + return jsGenerator->registerString(string.toString()) ; + }; - QStringView str; - if (QQmlJS::AST::StringLiteral *arg2 = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression)) { - str = arg2->value; - } else { - return; // first argument is not a string, stop + const auto finalizeTranslationData = [&]( + QV4::CompiledData::Binding::Type type, + QV4::CompiledData::TranslationData translationData) { + binding->setType(type); + if (type == QV4::CompiledData::Binding::Type_Translation + || type == QV4::CompiledData::Binding::Type_TranslationById) { + binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData); + } else if (type == QV4::CompiledData::Binding::Type_String) { + binding->stringIndex = translationData.number; } + }; - args = args->next; - if (args) - return; // too many arguments, stop - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = jsGenerator->registerString(str.toString()); - } + tryGeneratingTranslationBindingBase( + base, args, + registerString, registerString, registerString, finalizeTranslationData); } void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode) @@ -1306,9 +1341,8 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio Binding *binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; binding->offset = nameLocation.offset; - binding->location.line = nameLocation.startLine; - binding->location.column = nameLocation.startColumn; - binding->flags = 0; + binding->location.set(nameLocation.startLine, nameLocation.startColumn); + binding->clearFlags(); setBindingValue(binding, value, parentNode); QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); if (!error.isEmpty()) { @@ -1326,27 +1360,26 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio Binding *binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; binding->offset = nameLocation.offset; - binding->location.line = nameLocation.startLine; - binding->location.column = nameLocation.startColumn; + binding->location.set(nameLocation.startLine, nameLocation.startColumn); const Object *obj = _objects.at(objectIndex); binding->valueLocation = obj->location; - binding->flags = 0; + binding->clearFlags(); - if (_propertyDeclaration && _propertyDeclaration->isReadOnly) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + if (_propertyDeclaration && _propertyDeclaration->isReadOnly()) + binding->setFlag(Binding::InitializerForReadOnlyDeclaration); // No type name on the initializer means it must be a group property if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex) - binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + binding->setType(Binding::Type_GroupProperty); else - binding->type = QV4::CompiledData::Binding::Type_Object; + binding->setType(Binding::Type_Object); if (isOnAssignment) - binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + binding->setFlag(Binding::IsOnAssignment); if (isListItem) - binding->flags |= QV4::CompiledData::Binding::IsListItem; + binding->setFlag(Binding::IsListItem); binding->value.objectIndex = objectIndex; QString error = bindingsTarget()->appendBinding(binding, isListItem); @@ -1358,16 +1391,15 @@ void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocatio bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) { Alias *alias = New<Alias>(); - alias->flags = 0; - if (node->isReadonlyMember) - alias->flags |= QV4::CompiledData::Alias::IsReadOnly; + alias->clearFlags(); + if (node->isReadonly()) + alias->setFlag(QV4::CompiledData::Alias::IsReadOnly); const QString propName = node->name.toString(); - alias->nameIndex = registerString(propName); + alias->setNameIndex(registerString(propName)); QQmlJS::SourceLocation loc = node->firstSourceLocation(); - alias->location.line = loc.startLine; - alias->location.column = loc.startColumn; + alias->location.set(loc.startLine, loc.startColumn); alias->propertyNameIndex = emptyStringIndex; @@ -1381,8 +1413,7 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) rhsLoc = node->statement->firstSourceLocation(); else rhsLoc = node->semicolonToken; - alias->referenceLocation.line = rhsLoc.startLine; - alias->referenceLocation.column = rhsLoc.startColumn; + alias->referenceLocation.set(rhsLoc.startLine, rhsLoc.startColumn); QStringList aliasReference; @@ -1399,13 +1430,13 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); } - if (aliasReference.count() < 1 || aliasReference.count() > 3) + if (aliasReference.size() < 1 || aliasReference.size() > 3) COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - alias->idIndex = registerString(aliasReference.first()); + alias->setIdIndex(registerString(aliasReference.first())); QString propertyValue = aliasReference.value(1); - if (aliasReference.count() == 3) + if (aliasReference.size() == 3) propertyValue += QLatin1Char('.') + aliasReference.at(2); alias->propertyNameIndex = registerString(propertyValue); @@ -1415,7 +1446,7 @@ bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node) if (illegalNames.contains(propName)) error = tr("Illegal property name"); else - error = _object->appendAlias(alias, propName, node->isDefaultMember, node->defaultToken, &errorLocation); + error = _object->appendAlias(alias, propName, node->isDefaultMember(), node->defaultToken(), &errorLocation); if (!error.isEmpty()) { if (errorLocation.startLine == 0) @@ -1477,8 +1508,7 @@ bool IRBuilder::setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Sta COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); _object->idNameIndex = registerString(idQString); - _object->locationOfIdProperty.line = idLocation.startLine; - _object->locationOfIdProperty.column = idLocation.startColumn; + _object->locationOfIdProperty.set(idLocation.startLine, idLocation.startColumn); return true; } @@ -1493,7 +1523,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type. QString currentName = qualifiedIdElement->name.toString(); if (qualifiedIdElement->next) { - for (const QV4::CompiledData::Import* import : qAsConst(_imports)) + for (const QV4::CompiledData::Import* import : std::as_const(_imports)) if (import->qualifierIndex != emptyStringIndex && stringAt(import->qualifierIndex) == currentName) { qualifiedIdElement = qualifiedIdElement->next; @@ -1524,19 +1554,19 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O binding = New<Binding>(); binding->propertyNameIndex = propertyNameIndex; binding->offset = qualifiedIdElement->identifierToken.offset; - binding->location.line = qualifiedIdElement->identifierToken.startLine; - binding->location.column = qualifiedIdElement->identifierToken.startColumn; - binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine; - binding->valueLocation.column = qualifiedIdElement->next->identifierToken.startColumn; - binding->flags = 0; + binding->location.set(qualifiedIdElement->identifierToken.startLine, + qualifiedIdElement->identifierToken.startColumn); + binding->valueLocation.set(qualifiedIdElement->next->identifierToken.startLine, + qualifiedIdElement->next->identifierToken.startColumn); + binding->clearFlags(); if (onAssignment) - binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; + binding->setFlag(QV4::CompiledData::Binding::IsOnAssignment); if (isAttachedProperty) - binding->type = QV4::CompiledData::Binding::Type_AttachedProperty; + binding->setType(QV4::CompiledData::Binding::Type_AttachedProperty); else - binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + binding->setType(QV4::CompiledData::Binding::Type_GroupProperty); int objIndex = 0; if (!defineQMLObject(&objIndex, nullptr, binding->location, nullptr, nullptr)) @@ -1597,7 +1627,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) { - if (property->isBuiltinType || property->isList) + if (property->isCommonType() || property->isList()) return false; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); if (!exprStmt) @@ -1608,27 +1638,91 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) { + using namespace QV4::CompiledData; + output.jsGenerator.stringTable.registerString(output.jsModule.fileName); output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl); - QV4::CompiledData::Unit *jsUnit = nullptr; + Unit *jsUnit = nullptr; + + if (!output.javaScriptCompilationUnit) + output.javaScriptCompilationUnit.adopt(new QV4::CompiledData::CompilationUnit); // We may already have unit data if we're loading an ahead-of-time generated cache file. - if (output.javaScriptCompilationUnit.data) { - jsUnit = const_cast<QV4::CompiledData::Unit *>(output.javaScriptCompilationUnit.data); - output.javaScriptCompilationUnit.dynamicStrings = output.jsGenerator.stringTable.allStrings(); + if (output.javaScriptCompilationUnit->unitData()) { + jsUnit = const_cast<Unit *>(output.javaScriptCompilationUnit->unitData()); + output.javaScriptCompilationUnit->dynamicStrings + = output.jsGenerator.stringTable.allStrings(); } else { - QV4::CompiledData::Unit *createdUnit; + Unit *createdUnit; jsUnit = createdUnit = output.jsGenerator.generateUnit(); // enable flag if we encountered pragma Singleton - for (Pragma *p : qAsConst(output.pragmas)) { + for (Pragma *p : std::as_const(output.pragmas)) { switch (p->type) { - case Pragma::PragmaSingleton: - createdUnit->flags |= QV4::CompiledData::Unit::IsSingleton; + case Pragma::Singleton: + createdUnit->flags |= Unit::IsSingleton; break; - case Pragma::PragmaStrict: - createdUnit->flags |= QV4::CompiledData::Unit::IsStrict; + case Pragma::Strict: + createdUnit->flags |= Unit::IsStrict; + break; + case Pragma::ComponentBehavior: + // ### Qt7: Change the default to Bound by reverting the meaning of the flag. + switch (p->componentBehavior) { + case Pragma::Bound: + createdUnit->flags |= Unit::ComponentsBound; + break; + case Pragma::Unbound: + // this is the default + break; + } + break; + case Pragma::ListPropertyAssignBehavior: + switch (p->listPropertyAssignBehavior) { + case Pragma::Replace: + createdUnit->flags |= Unit::ListPropertyAssignReplace; + break; + case Pragma::ReplaceIfNotDefault: + createdUnit->flags |= Unit::ListPropertyAssignReplaceIfNotDefault; + break; + case Pragma::Append: + // this is the default + break; + } + break; + case Pragma::FunctionSignatureBehavior: + switch (p->functionSignatureBehavior) { + case Pragma::Enforced: + break; + case Pragma::Ignored: + createdUnit->flags |= Unit::FunctionSignaturesIgnored; + break; + } + break; + case Pragma::NativeMethodBehavior: + switch (p->nativeMethodBehavior) { + case Pragma::AcceptThisObject: + createdUnit->flags |= Unit::NativeMethodsAcceptThisObject; + break; + case Pragma::RejectThisObject: + // this is the default; + break; + } + break; + case Pragma::ValueTypeBehavior: + if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior) + .testFlag(Pragma::Copy)) { + createdUnit->flags |= Unit::ValueTypesCopied; + } + if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior) + .testFlag(Pragma::Addressable)) { + createdUnit->flags |= Unit::ValueTypesAddressable; + } + break; + case Pragma::Translator: + if (createdUnit->translationTableSize) + if (quint32_le *index = createdUnit->translationContextIndex()) + *index = p->translationContextIndex; break; } } @@ -1648,14 +1742,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen // No more new strings after this point, we're calculating offsets. output.jsGenerator.stringTable.freeze(); - const uint importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); - const uint objectOffsetTableSize = output.objects.count() * sizeof(quint32); + const uint importSize = uint(sizeof(QV4::CompiledData::Import)) * output.imports.size(); + const uint objectOffsetTableSize = output.objects.size() * uint(sizeof(quint32)); QHash<const Object*, quint32> objectOffsets; const unsigned int objectOffset = sizeof(QV4::CompiledData::QmlUnit) + importSize; uint nextOffset = objectOffset + objectOffsetTableSize; - for (Object *o : qAsConst(output.objects)) { + for (Object *o : std::as_const(output.objects)) { objectOffsets.insert(o, nextOffset); nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount(), o->requiredPropertyExtraDataCount()); @@ -1677,13 +1771,13 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen memset(data, 0, totalSize); QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data); qmlUnit->offsetToImports = sizeof(*qmlUnit); - qmlUnit->nImports = output.imports.count(); + qmlUnit->nImports = output.imports.size(); qmlUnit->offsetToObjects = objectOffset; - qmlUnit->nObjects = output.objects.count(); + qmlUnit->nObjects = output.objects.size(); // write imports char *importPtr = data + qmlUnit->offsetToImports; - for (const QV4::CompiledData::Import *imp : qAsConst(output.imports)) { + for (const QV4::CompiledData::Import *imp : std::as_const(output.imports)) { QV4::CompiledData::Import *importToWrite = reinterpret_cast<QV4::CompiledData::Import*>(importPtr); *importToWrite = *imp; importPtr += sizeof(QV4::CompiledData::Import); @@ -1691,7 +1785,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen // write objects quint32_le *objectTable = reinterpret_cast<quint32_le*>(data + qmlUnit->offsetToObjects); - for (int i = 0; i < output.objects.count(); ++i) { + for (int i = 0; i < output.objects.size(); ++i) { const Object *o = output.objects.at(i); char * const objectPtr = data + objectOffsets.value(o); *objectTable++ = objectOffsets.value(o); @@ -1699,10 +1793,10 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr); objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex; objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias; - objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias; - objectToWrite->flags = o->flags; + objectToWrite->setHasAliasAsDefaultProperty(o->defaultPropertyIsAlias); + objectToWrite->setFlags(QV4::CompiledData::Object::Flags(o->flags)); objectToWrite->idNameIndex = o->idNameIndex; - objectToWrite->id = o->id; + objectToWrite->setObjectId(o->id); objectToWrite->location = o->location; objectToWrite->locationOfIdProperty = o->locationOfIdProperty; @@ -1792,7 +1886,6 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen nextOffset += signalTableSize; quint32_le *enumOffsetTable = reinterpret_cast<quint32_le*>(objectPtr + objectToWrite->offsetToEnums); - quint32 enumTableSize = 0; char *enumPtr = objectPtr + nextOffset; for (const Enum *e = o->firstEnum(); e; e = e->next) { *enumOffsetTable++ = enumPtr - objectPtr; @@ -1807,7 +1900,6 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen *enumValueToWrite = *enumValue; int size = QV4::CompiledData::Enum::calculateSize(e->enumValues->count); - enumTableSize += size; enumPtr += size; } @@ -1833,7 +1925,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen } } - if (!output.javaScriptCompilationUnit.data) { + if (!output.javaScriptCompilationUnit->unitData()) { // Combine the qml data into the general unit data. jsUnit = static_cast<QV4::CompiledData::Unit *>(realloc(jsUnit, jsUnit->unitSize + totalSize)); jsUnit->offsetToQmlUnit = jsUnit->unitSize; @@ -1866,8 +1958,8 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen qDebug() << " " << totalStringSize << "bytes total strings"; } - output.javaScriptCompilationUnit.setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, - output.jsModule.finalUrl); + output.javaScriptCompilationUnit->setUnitData( + jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl); } char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const @@ -1877,22 +1969,27 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding continue; QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); *bindingToWrite = *b; - if (b->type == QV4::CompiledData::Binding::Type_Script) + if (b->type() == QV4::CompiledData::Binding::Type_Script) bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex); bindingPtr += sizeof(QV4::CompiledData::Binding); } return bindingPtr; } -JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames) - : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/false), document(document) +JSCodeGen::JSCodeGen(Document *document, const QSet<QString> &globalNames, + QV4::Compiler::CodegenWarningInterface *iface, + bool storeSourceLocations) + : QV4::Compiler::Codegen(&document->jsGenerator, /*strict mode*/ false, iface, + storeSourceLocations), + document(document) { m_globalNames = globalNames; _module = &document->jsModule; _fileNameIsUrl = true; } -QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions) +QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings( + const QList<CompiledFunctionOrExpression> &functions) { auto qmlName = [&](const CompiledFunctionOrExpression &c) { if (c.nameIndex != 0) @@ -1916,6 +2013,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f)); } + /* We do not want to visit the whole function, as we already called enterQmlFunction + However, there might be a function defined as a default argument of the function. + That needs to be considered, too, so we call handleTopLevelFunctionFormals to + deal with them. + */ + scan.handleTopLevelFunctionFormals(function); scan(function ? function->body : f.node); scan.leaveEnvironment(); } @@ -1926,7 +2029,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil _context = nullptr; - for (int i = 0; i < functions.count(); ++i) { + for (int i = 0; i < functions.size(); ++i) { const CompiledFunctionOrExpression &qmlFunction = functions.at(i); QQmlJS::AST::Node *node = qmlFunction.node; Q_ASSERT(node != document->program); @@ -1957,67 +2060,30 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil } int idx = defineFunction(name, function ? function : qmlFunction.parentNode, - function ? function->formals : nullptr, - body); + function ? function->formals : nullptr, body); runtimeFunctionIndices[i] = idx; } return runtimeFunctionIndices; } -bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots) +bool JSCodeGen::generateRuntimeFunctions(QmlIR::Object *object) { - for (int i = 0; i < componentRoots.count(); ++i) { - if (!compileComponent(componentRoots.at(i))) - return false; - } - - return compileComponent(/*root object*/0); -} - -bool JSCodeGen::compileComponent(int contextObject) -{ - const QmlIR::Object *obj = document->objects.at(contextObject); - if (obj->flags & QV4::CompiledData::Object::IsComponent && !obj->isInlineComponent) { - 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; - } - for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) - compileComponent(it->objectIndex); - - return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject); -} - -bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) -{ - QmlIR::Object *object = document->objects.at(objectIndex); - if (object->flags & QV4::CompiledData::Object::IsComponent && !object->isInlineComponent) + if (object->functionsAndExpressions->count == 0) return true; - if (object->functionsAndExpressions->count > 0) { - QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) - functionsToCompile << *foe; - const QVector<int> runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile); - if (hasError()) - return false; - - object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(), - runtimeFunctionIndices); + QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; + functionsToCompile.reserve(object->functionsAndExpressions->count); + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; + foe = foe->next) { + functionsToCompile << *foe; } - 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; - } + const auto runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile); + if (hasError()) + return false; + object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(), + runtimeFunctionIndices); return true; } |