diff options
Diffstat (limited to 'src/qml/compiler/qqmlirbuilder.cpp')
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 1567 |
1 files changed, 777 insertions, 790 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 558399ad6c..72111b3138 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1,45 +1,9 @@ -/**************************************************************************** -** -** 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" -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> @@ -48,25 +12,13 @@ #include <QCryptographicHash> #include <cmath> -#ifndef V4_BOOTSTRAP -#include <private/qqmlglobal_p.h> -#include <private/qqmltypeloader_p.h> -#include <private/qqmlengine_p.h> -#endif - -#ifdef CONST -#undef CONST -#endif - QT_USE_NAMESPACE -static const quint32 emptyStringIndex = 0; - -#if 0 //ndef V4_BOOTSTRAP -DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); -#endif // V4_BOOTSTRAP +using namespace Qt::StringLiterals; +static const quint32 emptyStringIndex = 0; using namespace QmlIR; +using namespace QQmlJS; #define COMPILE_EXCEPTION(location, desc) \ { \ @@ -74,13 +26,94 @@ using namespace QmlIR; return false; \ } -void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc) +void Object::simplifyRequiredProperties() { + // if a property of the current object was marked as required + // do not store that information in the ExtraData + // but rather mark the property as required + QSet<int> required; + for (auto it = this->requiredPropertyExtraDataBegin(); it != this->requiredPropertyExtraDataEnd(); ++it) + required.insert(it->nameIndex); + if (required.isEmpty()) + return; + for (auto it = this->propertiesBegin(); it != this->propertiesEnd(); ++it) { + auto requiredIt = required.find(it->nameIndex); + if (requiredIt != required.end()) { + it->setIsRequired(true); + required.erase(requiredIt); + } + } + QmlIR::RequiredPropertyExtraData *prev = nullptr; + auto current = this->requiredPropertyExtraDatas->first; + while (current) { + if (required.contains(current->nameIndex)) + prev = current; + else + requiredPropertyExtraDatas->unlink(prev, current); + current = current->next; + } +} + +bool Parameter::initType( + QV4::CompiledData::ParameterType *paramType, + const QString &typeName, int typeNameIndex, + QV4::CompiledData::ParameterType::Flag listFlag) { - inheritedTypeNameIndex = typeNameIndex; + auto builtinType = stringToBuiltinType(typeName); + if (builtinType == QV4::CompiledData::CommonType::Invalid) { + if (typeName.isEmpty()) { + paramType->set(listFlag, 0); + return false; + } + Q_ASSERT(quint32(typeNameIndex) < (1u << 31)); + paramType->set(listFlag, typeNameIndex); + } else { + Q_ASSERT(quint32(builtinType) < (1u << 31)); + paramType->set(listFlag | QV4::CompiledData::ParameterType::Common, + static_cast<quint32>(builtinType)); + } + return true; +} - location.line = loc.startLine; - location.column = loc.startColumn; +QV4::CompiledData::CommonType Parameter::stringToBuiltinType(const QString &typeName) +{ + static const struct TypeNameToType { + const char *name; + size_t nameLength; + QV4::CompiledData::CommonType type; + } propTypeNameToTypes[] = { + { "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]); + for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { + const TypeNameToType *t = propTypeNameToTypes + typeIndex; + if (typeName == QLatin1String(t->name, static_cast<int>(t->nameLength))) { + return t->type; + } + } + 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); + inheritedTypeNameIndex = typeNameIndex; + location = loc; idNameIndex = idIndex; id = -1; indexOfDefaultPropertyOrAlias = -1; @@ -93,16 +126,18 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons bindings = pool->New<PoolList<Binding> >(); functions = pool->New<PoolList<Function> >(); functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >(); + inlineComponents = pool->New<PoolList<InlineComponent>>(); + requiredPropertyExtraDatas = pool->New<PoolList<RequiredPropertyExtraData>>(); declarationsOverride = nullptr; } -QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation) +QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::SourceLocation *errorLocation) { 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); @@ -152,7 +187,7 @@ QString Object::appendSignal(Signal *signal) return QString(); // no error } -QString Object::appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation) +QString Object::appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation) { Object *target = declarationsOverride; if (!target) @@ -162,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"); @@ -176,15 +215,24 @@ QString Object::appendProperty(Property *prop, const QString &propertyName, bool return QString(); // no error } -QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation) +QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation) { Object *target = declarationsOverride; if (!target) target = this; - for (Alias *p = target->aliases->first; p; p = p->next) - if (p->nameIndex == alias->nameIndex) - return tr("Duplicate alias name"); + 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"); @@ -205,22 +253,37 @@ 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) +{ + inlineComponents->append(ic); +} + +void Object::appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraData) +{ + requiredPropertyExtraDatas->append(extraData); } 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); @@ -249,8 +312,8 @@ QString Object::bindingAsString(Document *doc, int scriptIndex) const QQmlJS::AST::Node *node = foe->node; if (QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node)) node = exprStmt->expression; - QQmlJS::AST::SourceLocation start = node->firstSourceLocation(); - QQmlJS::AST::SourceLocation end = node->lastSourceLocation(); + QQmlJS::SourceLocation start = node->firstSourceLocation(); + QQmlJS::SourceLocation end = node->lastSourceLocation(); return doc->code.mid(start.offset, end.offset + end.length - start.offset); } @@ -258,64 +321,11 @@ QStringList Signal::parameterStringList(const QV4::Compiler::StringTableGenerato { QStringList result; result.reserve(parameters->count); - for (SignalParameter *param = parameters->first; param; param = param->next) + for (Parameter *param = parameters->first; param; param = param->next) result << stringPool->stringForIndex(param->nameIndex); return result; } -static void replaceWithSpace(QString &str, int idx, int n) -{ - QChar *data = str.data() + idx; - const QChar space(QLatin1Char(' ')); - for (int ii = 0; ii < n; ++ii) - *data++ = space; -} - -void Document::removeScriptPragmas(QString &script) -{ - const QLatin1String pragma("pragma"); - const QLatin1String library("library"); - - QQmlJS::Lexer l(nullptr); - l.setCode(script, 0); - - int token = l.lex(); - - while (true) { - if (token != QQmlJSGrammar::T_DOT) - return; - - int startOffset = l.tokenOffset(); - int startLine = l.tokenStartLine(); - - token = l.lex(); - - if (token != QQmlJSGrammar::T_PRAGMA || - l.tokenStartLine() != startLine || - script.midRef(l.tokenOffset(), l.tokenLength()) != pragma) - return; - - token = l.lex(); - - if (token != QQmlJSGrammar::T_IDENTIFIER || - l.tokenStartLine() != startLine) - return; - - const QStringRef pragmaValue = script.midRef(l.tokenOffset(), l.tokenLength()); - int endOffset = l.tokenLength() + l.tokenOffset(); - - token = l.lex(); - if (l.tokenStartLine() == startLine) - return; - - if (pragmaValue == library) { - replaceWithSpace(script, startOffset, endOffset - startOffset); - } else { - return; - } - } -} - Document::Document(bool debugMode) : jsModule(debugMode) , program(nullptr) @@ -341,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; } @@ -351,14 +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); - int vmaj; - int vmin; - IRBuilder::extractVersion(QStringRef(&version), &vmaj, &vmin); - import->majorVersion = vmaj; - import->minorVersion = vmin; + 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; } @@ -385,15 +389,16 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen if (!parseResult || !diagnosticMessages.isEmpty()) { // Extract errors from the parser for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { - if (m.isWarning()) { qWarning("%s:%d : %s", qPrintable(url), m.loc.startLine, qPrintable(m.message)); continue; } - recordError(m.loc, m.message); + errors << m; } - return false; + + if (!errors.isEmpty() || !parseResult) + return false; } program = parser.ast(); Q_ASSERT(program); @@ -415,7 +420,7 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen accept(program->headers); if (program->members->next) { - QQmlJS::AST::SourceLocation loc = program->members->next->firstSourceLocation(); + QQmlJS::SourceLocation loc = program->members->next->firstSourceLocation(); recordError(loc, QCoreApplication::translate("QQmlParser", "Unexpected object definition")); return false; } @@ -430,21 +435,11 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen qSwap(_imports, output->imports); qSwap(_pragmas, output->pragmas); qSwap(_objects, output->objects); - 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. + for (auto object: output->objects) + object->simplifyRequiredProperties(); + + return errors.isEmpty(); } bool IRBuilder::visit(QQmlJS::AST::UiArrayMemberList *ast) @@ -471,27 +466,66 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectDefinition *node) QQmlJS::AST::UiQualifiedId *lastId = node->qualifiedTypeNameId; while (lastId->next) lastId = lastId->next; - bool isType = lastId->name.unicode()->isUpper(); + bool isType = lastId->name.data()->isUpper(); if (isType) { int idx = 0; if (!defineQMLObject(&idx, node)) return false; - const QQmlJS::AST::SourceLocation nameLocation = node->qualifiedTypeNameId->identifierToken; + const QQmlJS::SourceLocation nameLocation = node->qualifiedTypeNameId->identifierToken; appendBinding(nameLocation, nameLocation, emptyStringIndex, idx); } else { int idx = 0; - if (!defineQMLObject(&idx, /*qualfied type name id*/nullptr, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object)) + const QQmlJS::SourceLocation location = node->qualifiedTypeNameId->firstSourceLocation(); + if (!defineQMLObject( + &idx, /*qualfied type name id*/nullptr, + { location.startLine, location.startColumn }, node->initializer, + /*declarations should go here*/_object)) { return false; + } appendBinding(node->qualifiedTypeNameId, idx); } return false; } +bool IRBuilder::visit(QQmlJS::AST::UiInlineComponent *ast) +{ + int idx = -1; + if (insideInlineComponent) { + recordError(ast->firstSourceLocation(), QLatin1String("Nested inline components are not supported")); + return false; + } + if (inlineComponentsNames.contains(ast->name.toString())) { + recordError(ast->firstSourceLocation(), QLatin1String("Inline component names must be unique per file")); + return false; + } else { + inlineComponentsNames.insert(ast->name.toString()); + } + { + QScopedValueRollback<bool> rollBack {insideInlineComponent, true}; + if (!defineQMLObject(&idx, ast->component)) + return false; + } + Q_ASSERT(idx > 0); + Object* definedObject = _objects.at(idx); + definedObject->flags |= QV4::CompiledData::Object::IsInlineComponentRoot; + 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.set(location.startLine, location.startColumn); + _object->appendInlineComponent(inlineComponent); + return false; +} + bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node) { int idx = 0; - if (!defineQMLObject(&idx, node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer)) + const QQmlJS::SourceLocation location = node->qualifiedTypeNameId->firstSourceLocation(); + if (!defineQMLObject(&idx, node->qualifiedTypeNameId, + { location.startLine, location.startColumn }, node->initializer)) { return false; + } appendBinding(node->qualifiedId, idx, node->hasOnToken); return false; } @@ -504,7 +538,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiScriptBinding *node) bool IRBuilder::visit(QQmlJS::AST::UiArrayBinding *node) { - const QQmlJS::AST::SourceLocation qualifiedNameLocation = node->qualifiedId->identifierToken; + const QQmlJS::SourceLocation qualifiedNameLocation = node->qualifiedId->identifierToken; Object *object = nullptr; QQmlJS::AST::UiQualifiedId *name = node->qualifiedId; if (!resolveQualifiedId(&name, &object)) @@ -525,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); @@ -569,7 +603,10 @@ void IRBuilder::accept(QQmlJS::AST::Node *node) QQmlJS::AST::Node::accept(node, this); } -bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride) +bool IRBuilder::defineQMLObject( + int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, + const QV4::CompiledData::Location &location, QQmlJS::AST::UiObjectInitializer *initializer, + Object *declarationsOverride) { if (QQmlJS::AST::UiQualifiedId *lastName = qualifiedTypeNameId) { while (lastName->next) @@ -581,12 +618,16 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu } Object *obj = New<Object>(); + _objects.append(obj); *objectIndex = _objects.size() - 1; qSwap(_object, obj); _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location); _object->declarationsOverride = declarationsOverride; + if (insideInlineComponent) { + _object->flags |= QV4::CompiledData::Object::IsPartOfInlineComponent; + } // A new object is also a boundary for property declarations. Property *declaration = nullptr; @@ -601,7 +642,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu if (!errors.isEmpty()) return false; - QQmlJS::AST::SourceLocation loc; + QQmlJS::SourceLocation loc; QString error = sanityCheckFunctionNames(obj, illegalNames, &loc); if (!error.isEmpty()) { recordError(loc, error); @@ -646,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; @@ -661,23 +702,14 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) return false; } - if (node->versionToken.isValid()) { - int major, minor; - extractVersion(textRefAt(node->versionToken), &major, &minor); - import->majorVersion = major; - import->minorVersion = minor; - } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { - recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); - return false; + if (node->version) { + import->version = node->version->version; } else { - // For backward compatibility in how the imports are loaded we - // must otherwise initialize the major and minor version to -1. - import->majorVersion = -1; - import->minorVersion = -1; + // Otherwise initialize the major and minor version to invalid to signal "latest". + 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); @@ -686,27 +718,234 @@ 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; + } + + if (value == "Inassertable"_L1) { + setFlag(Pragma::Assertable, false); + return true; + } + if (value == "Assertable"_L1) { + setFlag(Pragma::Assertable, 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 (QLatin1String("Singleton") == node->name) - { - pragma->type = Pragma::PragmaSingleton; + 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; @@ -739,8 +978,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>>(); @@ -759,8 +997,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; @@ -778,98 +1015,45 @@ bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node) bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) { - static const struct TypeNameToType { - const char *name; - size_t nameLength; - QV4::CompiledData::Property::Type type; - } propTypeNameToTypes[] = { - { "int", strlen("int"), QV4::CompiledData::Property::Int }, - { "bool", strlen("bool"), QV4::CompiledData::Property::Bool }, - { "double", strlen("double"), QV4::CompiledData::Property::Real }, - { "real", strlen("real"), QV4::CompiledData::Property::Real }, - { "string", strlen("string"), QV4::CompiledData::Property::String }, - { "url", strlen("url"), QV4::CompiledData::Property::Url }, - { "color", strlen("color"), QV4::CompiledData::Property::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::Property::DateTime }, - { "rect", strlen("rect"), QV4::CompiledData::Property::Rect }, - { "point", strlen("point"), QV4::CompiledData::Property::Point }, - { "size", strlen("size"), QV4::CompiledData::Property::Size }, - { "font", strlen("font"), QV4::CompiledData::Property::Font }, - { "vector2d", strlen("vector2d"), QV4::CompiledData::Property::Vector2D }, - { "vector3d", strlen("vector3d"), QV4::CompiledData::Property::Vector3D }, - { "vector4d", strlen("vector4d"), QV4::CompiledData::Property::Vector4D }, - { "quaternion", strlen("quaternion"), QV4::CompiledData::Property::Quaternion }, - { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::Property::Matrix4x4 }, - { "variant", strlen("variant"), QV4::CompiledData::Property::Variant }, - { "var", strlen("var"), QV4::CompiledData::Property::Var } - }; - static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / - sizeof(propTypeNameToTypes[0]); - if (node->type == QQmlJS::AST::UiPublicMember::Signal) { Signal *signal = New<Signal>(); - QString signalName = node->name.toString(); + const QString signalName = node->name.toString(); signal->nameIndex = registerString(signalName); - QQmlJS::AST::SourceLocation loc = node->typeToken; - signal->location.line = loc.startLine; - signal->location.column = loc.startColumn; + QQmlJS::SourceLocation loc = node->typeToken; + signal->location.set(loc.startLine, loc.startColumn); - signal->parameters = New<PoolList<SignalParameter> >(); + 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; } - const TypeNameToType *type = nullptr; - for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { - const TypeNameToType *t = propTypeNameToTypes + typeIndex; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = t; - break; - } - } - - SignalParameter *param = New<SignalParameter>(); - - if (!type) { - if (memberType.at(0).isUpper()) { - // Must be a QML object type. - // Lazily determine type during compilation. - param->type = QV4::CompiledData::Property::Custom; - param->customTypeNameIndex = registerString(memberType); - } else { - QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); - errStr.append(memberType); - recordError(node->typeToken, errStr); - return false; - } - } else { - // the parameter is a known basic type - param->type = type->type; - param->customTypeNameIndex = emptyStringIndex; - } - + Parameter *param = New<Parameter>(); param->nameIndex = registerString(p->name.toString()); - param->location.line = p->identifierToken.startLine; - param->location.column = p->identifierToken.startColumn; + 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(p->type->toString()); + recordError(node->typeToken, errStr); + return false; + } signal->parameters->append(param); p = p->next; } - if (signalName.at(0).isUpper()) - COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter")); + for (const QChar &ch : signalName) { + if (ch.isLower()) + break; + if (ch.isUpper()) { + COMPILE_EXCEPTION(node->identifierToken, + tr("Signal names cannot begin with an upper case letter")); + } + } if (illegalNames.contains(signalName)) COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name")); @@ -884,65 +1068,40 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) if (memberType == QLatin1String("alias")) { return appendAlias(node); } else { - const QStringRef &name = node->name; - - bool typeFound = false; - QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var; - - for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { - const TypeNameToType *t = propTypeNameToTypes + ii; - if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) { - type = t->type; - typeFound = true; - } - } + QStringView name = node->name; - if (!typeFound && memberType.at(0).isUpper()) { - const QStringRef &typeModifier = node->typeModifier; + Property *property = New<Property>(); + property->setIsReadOnly(node->isReadonly()); + property->setIsRequired(node->isRequired()); - if (typeModifier.isEmpty()) { - type = QV4::CompiledData::Property::Custom; - } else if (typeModifier == QLatin1String("list")) { - type = QV4::CompiledData::Property::CustomList; - } else { - 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; - } + const QV4::CompiledData::CommonType builtinPropertyType + = Parameter::stringToBuiltinType(memberType); + if (builtinPropertyType != QV4::CompiledData::CommonType::Invalid) + property->setCommonType(builtinPropertyType); + else + property->setTypeNameIndex(registerString(memberType)); - if (!typeFound) { - recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type")); + 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; } - Property *property = New<Property>(); - property->flags = 0; - if (node->isReadonlyMember) - property->flags |= QV4::CompiledData::Property::IsReadOnly; - property->type = type; - if (type >= QV4::CompiledData::Property::Custom) - property->customTypeNameIndex = registerString(memberType); - else - property->customTypeNameIndex = emptyStringIndex; - const QString propName = name.toString(); property->nameIndex = registerString(propName); - QQmlJS::AST::SourceLocation loc = node->firstSourceLocation(); - property->location.line = loc.startLine; - property->location.column = loc.startColumn; + QQmlJS::SourceLocation loc = node->firstSourceLocation(); + property->location.set(loc.startLine, loc.startColumn); - QQmlJS::AST::SourceLocation errorLocation; + QQmlJS::SourceLocation errorLocation; QString error; 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) @@ -969,7 +1128,15 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) { - if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) { + 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; @@ -977,19 +1144,28 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) const int index = _object->functionsAndExpressions->append(foe); Function *f = New<Function>(); - QQmlJS::AST::SourceLocation loc = funDecl->identifierToken; - f->location.line = loc.startLine; - f->location.column = loc.startColumn; + QQmlJS::SourceLocation loc = funDecl->identifierToken; + f->location.set(loc.startLine, loc.startColumn); f->index = index; f->nameIndex = registerString(funDecl->name.toString()); - const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList(); + 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(); f->formals.allocate(pool, formalsCount); int i = 0; - for (const QString &arg : formals) { - f->formals[i] = registerString(arg); + for (const auto &arg : formals) { + Parameter *functionParameter = &f->formals[i]; + functionParameter->nameIndex = registerString(arg.id); + Parameter::initType( + &functionParameter->type, idGenerator, + arg.typeAnnotation.isNull() ? nullptr : arg.typeAnnotation->type); ++i; } @@ -1000,6 +1176,14 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) return false; } +bool IRBuilder::visit(AST::UiRequired *ast) +{ + auto extraData = New<RequiredPropertyExtraData>(); + extraData->nameIndex = registerString(ast->name.toString()); + _object->appendRequiredPropertyExtraData(extraData); + return false; +} + QString IRBuilder::asString(QQmlJS::AST::UiQualifiedId *node) { QString s; @@ -1014,60 +1198,58 @@ QString IRBuilder::asString(QQmlJS::AST::UiQualifiedId *node) return s; } -QStringRef IRBuilder::asStringRef(QQmlJS::AST::Node *node) +QStringView IRBuilder::asStringRef(QQmlJS::AST::Node *node) { if (!node) - return QStringRef(); + return QStringView(); return textRefAt(node->firstSourceLocation(), node->lastSourceLocation()); } -void IRBuilder::extractVersion(QStringRef string, int *maj, int *min) +QTypeRevision IRBuilder::extractVersion(QStringView string) { - *maj = -1; *min = -1; - - if (!string.isEmpty()) { - - int dot = string.indexOf(QLatin1Char('.')); + if (string.isEmpty()) + return QTypeRevision(); - if (dot < 0) { - *maj = string.toInt(); - *min = 0; - } else { - *maj = string.left(dot).toInt(); - *min = string.mid(dot + 1).toInt(); - } - } + const int dot = string.indexOf(QLatin1Char('.')); + return (dot < 0) + ? QTypeRevision::fromMajorVersion(string.toInt()) + : QTypeRevision::fromVersion(string.left(dot).toInt(), string.mid(dot + 1).toInt()); } -QStringRef IRBuilder::textRefAt(const QQmlJS::AST::SourceLocation &first, const QQmlJS::AST::SourceLocation &last) const +QStringView IRBuilder::textRefAt(const QQmlJS::SourceLocation &first, const QQmlJS::SourceLocation &last) const { - return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset); + return QStringView(sourceCode).mid(first.offset, last.offset + last.length - first.offset); } void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode) { - QQmlJS::AST::SourceLocation loc = statement->firstSourceLocation(); - binding->valueLocation.line = loc.startLine; - binding->valueLocation.column = loc.startColumn; - binding->type = QV4::CompiledData::Binding::Type_Invalid; - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; + QQmlJS::SourceLocation loc = statement->firstSourceLocation(); + 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)) { @@ -1076,18 +1258,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->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; @@ -1098,130 +1283,44 @@ 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(const QStringRef &base, AST::ArgumentList *args, QV4::CompiledData::Binding *binding) +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 - - QStringRef 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 - - QStringRef 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 - - QStringRef 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()) ; + }; - QStringRef 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) { - const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; + const QQmlJS::SourceLocation qualifiedNameLocation = name->identifierToken; Object *object = nullptr; if (!resolveQualifiedId(&name, &object)) return; @@ -1236,7 +1335,7 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment) { - const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; + const QQmlJS::SourceLocation qualifiedNameLocation = name->identifierToken; Object *object = nullptr; if (!resolveQualifiedId(&name, &object, isOnAssignment)) return; @@ -1245,15 +1344,14 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, qSwap(_object, object); } -void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, +void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode) { 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()) { @@ -1261,7 +1359,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo } } -void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) +void IRBuilder::appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) { if (stringAt(propertyNameIndex) == QLatin1String("id")) { recordError(nameLocation, tr("Invalid component id specification")); @@ -1271,27 +1369,26 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo 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->flags & QV4::CompiledData::Property::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); @@ -1303,31 +1400,29 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo 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::AST::SourceLocation loc = node->firstSourceLocation(); - alias->location.line = loc.startLine; - alias->location.column = loc.startColumn; + QQmlJS::SourceLocation loc = node->firstSourceLocation(); + alias->location.set(loc.startLine, loc.startColumn); alias->propertyNameIndex = emptyStringIndex; if (!node->statement && !node->binding) COMPILE_EXCEPTION(loc, tr("No property alias location")); - QQmlJS::AST::SourceLocation rhsLoc; + QQmlJS::SourceLocation rhsLoc; if (node->binding) rhsLoc = node->binding->firstSourceLocation(); else if (node->statement) 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; @@ -1344,23 +1439,23 @@ 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); - QQmlJS::AST::SourceLocation errorLocation; + QQmlJS::SourceLocation errorLocation; QString error; 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) @@ -1380,10 +1475,10 @@ Object *IRBuilder::bindingsTarget() const return _object; } -bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST::Statement *value) +bool IRBuilder::setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Statement *value) { - QQmlJS::AST::SourceLocation loc = value->firstSourceLocation(); - QStringRef str; + QQmlJS::SourceLocation loc = value->firstSourceLocation(); + QStringView str; QQmlJS::AST::Node *node = value; if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node)) { @@ -1408,7 +1503,7 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST if (!ch.isLetter() && ch != u) COMPILE_EXCEPTION(loc, tr( "IDs must start with a letter or underscore")); - for (int ii = 1; ii < str.count(); ++ii) { + for (int ii = 1; ii < str.size(); ++ii) { ch = str.at(ii); if (!ch.isLetterOrNumber() && ch != u) COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores")); @@ -1422,8 +1517,7 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST 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; } @@ -1438,13 +1532,13 @@ 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; currentName += QLatin1Char('.') + qualifiedIdElement->name; - if (!qualifiedIdElement->name.unicode()->isUpper()) + if (!qualifiedIdElement->name.data()->isUpper()) COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name")); break; @@ -1454,7 +1548,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O *object = _object; while (qualifiedIdElement->next) { const quint32 propertyNameIndex = registerString(currentName); - const bool isAttachedProperty = qualifiedIdElement->name.unicode()->isUpper(); + const bool isAttachedProperty = qualifiedIdElement->name.data()->isUpper(); Binding *binding = (*object)->findBinding(propertyNameIndex); if (binding) { @@ -1469,22 +1563,22 @@ 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, QQmlJS::AST::SourceLocation(), nullptr, nullptr)) + if (!defineQMLObject(&objIndex, nullptr, binding->location, nullptr, nullptr)) return false; binding->value.objectIndex = objIndex; @@ -1507,7 +1601,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O return true; } -void IRBuilder::recordError(const QQmlJS::AST::SourceLocation &location, const QString &description) +void IRBuilder::recordError(const QQmlJS::SourceLocation &location, const QString &description) { QQmlJS::DiagnosticMessage error; error.loc = location; @@ -1542,7 +1636,7 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement) bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement) { - if (property->type != QV4::CompiledData::Property::Custom) + if (property->isCommonType() || property->isList()) return false; QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); if (!exprStmt) @@ -1553,47 +1647,107 @@ 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); - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; + Unit *jsUnit = nullptr; - QV4::CompiledData::Unit *jsUnit = nullptr; - const bool finalize = !compilationUnit->data; + 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 (!finalize) { - jsUnit = const_cast<QV4::CompiledData::Unit *>(compilationUnit->data); -#ifndef V4_BOOTSTRAP - output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings(); -#endif + 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)) { - if (p->type == Pragma::PragmaSingleton) { - createdUnit->flags |= QV4::CompiledData::Unit::IsSingleton; + for (Pragma *p : std::as_const(output.pragmas)) { + switch (p->type) { + case Pragma::Singleton: + createdUnit->flags |= Unit::IsSingleton; + break; + 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; + } + if (Pragma::ValueTypeBehaviorValues(p->valueTypeBehavior) + .testFlag(Pragma::Assertable)) { + createdUnit->flags |= Unit::ValueTypesAssertable; + } + break; + case Pragma::Translator: + if (createdUnit->translationTableSize) + if (quint32_le *index = createdUnit->translationContextIndex()) + *index = p->translationContextIndex; break; } } - // This unit's memory was allocated with malloc on the heap, so it's - // definitely not suitable for StaticData access. - createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData; -#ifndef V4_BOOTSTRAP if (dependencyHasher) { - QCryptographicHash hash(QCryptographicHash::Md5); - if (dependencyHasher(&hash)) { - QByteArray checksum = hash.result(); - Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)); - memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum)); + const QByteArray checksum = dependencyHasher(); + if (checksum.size() == sizeof(createdUnit->dependencyMD5Checksum)) { + memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), + sizeof(createdUnit->dependencyMD5Checksum)); } } -#else - Q_UNUSED(dependencyHasher); -#endif + createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName); createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl); } @@ -1601,16 +1755,16 @@ 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.count); + nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size(), o->inlineComponentCount(), o->requiredPropertyExtraDataCount()); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) @@ -1630,13 +1784,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); @@ -1644,7 +1798,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); @@ -1652,10 +1806,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; @@ -1685,10 +1839,18 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen objectToWrite->offsetToBindings = nextOffset; nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); - objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count; + objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.size(); objectToWrite->offsetToNamedObjectsInComponent = nextOffset; nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); + objectToWrite->nInlineComponents = o->inlineComponentCount(); + objectToWrite->offsetToInlineComponents = nextOffset; + nextOffset += objectToWrite->nInlineComponents * sizeof (QV4::CompiledData::InlineComponent); + + objectToWrite->nRequiredPropertyExtraData = o->requiredPropertyExtraDataCount(); + objectToWrite->offsetToRequiredPropertyExtraData = nextOffset; + nextOffset += objectToWrite->nRequiredPropertyExtraData * sizeof(QV4::CompiledData::RequiredPropertyExtraData); + quint32_le *functionsTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToFunctions); for (const Function *f = o->firstFunction(); f; f = f->next) *functionsTable++ = o->runtimeFunctionIndices.at(f->index); @@ -1727,7 +1889,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen signalToWrite->nParameters = s->parameters->count; QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite)); - for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) + for (Parameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) *parameterToWrite = *param; int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count); @@ -1737,7 +1899,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; @@ -1752,24 +1913,39 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen *enumValueToWrite = *enumValue; int size = QV4::CompiledData::Enum::calculateSize(e->enumValues->count); - enumTableSize += size; enumPtr += size; } quint32_le *namedObjectInComponentPtr = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); - for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { + for (int i = 0; i < o->namedObjectsInComponent.size(); ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } + + char *inlineComponentPtr = objectPtr + objectToWrite->offsetToInlineComponents; + for (auto it = o->inlineComponentsBegin(); it != o->inlineComponentsEnd(); ++it) { + const InlineComponent *ic = it.ptr; + QV4::CompiledData::InlineComponent *icToWrite = reinterpret_cast<QV4::CompiledData::InlineComponent*>(inlineComponentPtr); + *icToWrite = *ic; + inlineComponentPtr += sizeof(QV4::CompiledData::InlineComponent); + } + + char *requiredPropertyExtraDataPtr = objectPtr + objectToWrite->offsetToRequiredPropertyExtraData; + for (auto it = o->requiredPropertyExtraDataBegin(); it != o->requiredPropertyExtraDataEnd(); ++it) { + const RequiredPropertyExtraData *extraData = it.ptr; + QV4::CompiledData::RequiredPropertyExtraData *extraDataToWrite = reinterpret_cast<QV4::CompiledData::RequiredPropertyExtraData*>(requiredPropertyExtraDataPtr); + *extraDataToWrite = *extraData; + requiredPropertyExtraDataPtr += sizeof(QV4::CompiledData::RequiredPropertyExtraData); + } } - if (finalize) { + 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; jsUnit->unitSize += totalSize; memcpy(jsUnit->qmlUnit(), qmlUnit, totalSize); free(qmlUnit); - jsUnit->generateChecksum(); + QV4::Compiler::JSUnitGenerator::generateUnitChecksum(jsUnit); qmlUnit = jsUnit->qmlUnit(); } @@ -1795,7 +1971,8 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen qDebug() << " " << totalStringSize << "bytes total strings"; } - compilationUnit->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 @@ -1805,45 +1982,42 @@ 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(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, - QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, - const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames) - : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) - , sourceCode(sourceCode) - , jsEngine(jsEngine) - , qmlRoot(qmlRoot) - , stringPool(stringPool) +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 = jsModule; + _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) - return stringPool->stringForIndex(c.nameIndex); + return document->stringAt(c.nameIndex); else return QStringLiteral("%qml-expression-entry"); }; QVector<int> runtimeFunctionIndices(functions.size()); - QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global); + QV4::Compiler::ScanFunctions scan(this, document->code, QV4::Compiler::ContextType::Global); scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding); for (const CompiledFunctionOrExpression &f : functions) { - Q_ASSERT(f.node != qmlRoot); - Q_ASSERT(f.parentNode && f.parentNode != qmlRoot); - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node); + Q_ASSERT(f.node != document->program); + Q_ASSERT(f.parentNode && f.parentNode != document->program); + auto function = f.node->asFunctionDefinition(); if (function) { scan.enterQmlFunction(function); @@ -1852,22 +2026,28 @@ 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(); } scan.leaveEnvironment(); - if (hasError) + if (hasError()) return QVector<int>(); _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 != qmlRoot); + Q_ASSERT(node != document->program); - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(node); + QQmlJS::AST::FunctionExpression *function = node->asFunctionDefinition(); QString name; if (function) @@ -1880,7 +2060,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil body = function->body; } else { // Synthesize source elements. - QQmlJS::MemoryPool *pool = jsEngine->pool(); + QQmlJS::MemoryPool *pool = document->jsParserEngine.pool(); QQmlJS::AST::Statement *stmt = node->statementCast(); if (!stmt) { @@ -1893,223 +2073,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; } -#ifndef V4_BOOTSTRAP - -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const +bool JSCodeGen::generateRuntimeFunctions(QmlIR::Object *object) { - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); + if (object->functionsAndExpressions->count == 0) + return true; - // 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; + QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile; + functionsToCompile.reserve(object->functionsAndExpressions->count); + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; + foe = foe->next) { + functionsToCompile << *foe; } -} - - -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, nullptr, nullptr); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return nullptr; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(QLatin1String("Changed"))) { - QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - - d = property(propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex()); - } - - return nullptr; -} - -IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) - : unit(qmlData) - , output(output) -{ - pool = output->jsParserEngine.pool(); -} - -void IRLoader::load() -{ - output->jsGenerator.stringTable.initializeFromBackingUnit(unit); - const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit(); - - for (quint32 i = 0; i < qmlUnit->nImports; ++i) - output->imports << qmlUnit->importAt(i); - - if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { - QmlIR::Pragma *p = New<QmlIR::Pragma>(); - p->location = QV4::CompiledData::Location(); - p->type = QmlIR::Pragma::PragmaSingleton; - output->pragmas << p; - } - - for (uint i = 0; i < qmlUnit->nObjects; ++i) { - const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i); - QmlIR::Object *object = loadObject(serializedObject); - output->objects.append(object); - } -} - -struct FakeExpression : public QQmlJS::AST::NullExpression -{ - FakeExpression(int start, int length) - : location(start, length) - {} - - virtual QQmlJS::AST::SourceLocation firstSourceLocation() const - { return location; } - - virtual QQmlJS::AST::SourceLocation lastSourceLocation() const - { return location; } - -private: - QQmlJS::AST::SourceLocation location; -}; - -QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) -{ - QmlIR::Object *object = pool->New<QmlIR::Object>(); - object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); - - object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; - object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - object->flags = serializedObject->flags; - object->id = serializedObject->id; - object->location = serializedObject->location; - object->locationOfIdProperty = serializedObject->locationOfIdProperty; - - QVector<int> functionIndices; - functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); - - for (uint i = 0; i < serializedObject->nBindings; ++i) { - QmlIR::Binding *b = pool->New<QmlIR::Binding>(); - *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; - object->bindings->append(b); - if (b->type == QV4::CompiledData::Binding::Type_Script) { - functionIndices.append(b->value.compiledScriptIndex); - b->value.compiledScriptIndex = functionIndices.count() - 1; - - QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); - foe->nameIndex = 0; - - QQmlJS::AST::ExpressionNode *expr; - - if (b->stringIndex != quint32(0)) { - const int start = output->code.length(); - const QString script = output->stringAt(b->stringIndex); - const int length = script.length(); - output->code.append(script); - expr = new (pool) FakeExpression(start, length); - } else - expr = new (pool) QQmlJS::AST::NullExpression(); - foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy - object->functionsAndExpressions->append(foe); - } - } - - Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); - - for (uint i = 0; i < serializedObject->nSignals; ++i) { - const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); - QmlIR::Signal *s = pool->New<QmlIR::Signal>(); - s->nameIndex = serializedSignal->nameIndex; - s->location = serializedSignal->location; - s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); - - for (uint i = 0; i < serializedSignal->nParameters; ++i) { - QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); - *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); - s->parameters->append(p); - } - - object->qmlSignals->append(s); - } - - for (uint i = 0; i < serializedObject->nEnums; ++i) { - const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i); - QmlIR::Enum *e = pool->New<QmlIR::Enum>(); - e->nameIndex = serializedEnum->nameIndex; - e->location = serializedEnum->location; - e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >(); - - for (uint i = 0; i < serializedEnum->nEnumValues; ++i) { - QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>(); - *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i); - e->enumValues->append(v); - } - - object->qmlEnums->append(e); - } - - const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); - for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { - QmlIR::Property *p = pool->New<QmlIR::Property>(); - *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; - object->properties->append(p); - } - - { - const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); - for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { - QmlIR::Alias *a = pool->New<QmlIR::Alias>(); - *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; - object->aliases->append(a); - } - } - - const quint32_le *functionIdx = serializedObject->functionOffsetTable(); - for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { - QmlIR::Function *f = pool->New<QmlIR::Function>(); - const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); - - functionIndices.append(*functionIdx); - f->index = functionIndices.count() - 1; - f->location = compiledFunction->location; - f->nameIndex = compiledFunction->nameIndex; - - const QString name = unit->stringAtInternal(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); + const auto runtimeFunctionIndices = generateJSCodeForFunctionsAndBindings(functionsToCompile); + if (hasError()) + return false; - return object; + object->runtimeFunctionIndices.allocate(document->jsParserEngine.pool(), + runtimeFunctionIndices); + return true; } - -#endif // V4_BOOTSTRAP |