diff options
Diffstat (limited to 'src/qml/compiler')
-rw-r--r-- | src/qml/compiler/compiler.pri | 29 | ||||
-rw-r--r-- | src/qml/compiler/qqmlirbuilder.cpp | 1201 | ||||
-rw-r--r-- | src/qml/compiler/qqmlirbuilder_p.h | 480 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator.cpp | 92 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator_p.h | 81 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler.cpp | 40 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler_p.h | 47 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 901 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 188 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 312 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler_p.h | 61 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext.cpp | 86 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 96 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontrolflow_p.h | 51 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerglobal_p.h | 40 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions.cpp | 164 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions_p.h | 66 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 292 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 85 | ||||
-rw-r--r-- | src/qml/compiler/qv4util_p.h | 41 |
20 files changed, 2490 insertions, 1863 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri deleted file mode 100644 index 4d6926d420..0000000000 --- a/src/qml/compiler/compiler.pri +++ /dev/null @@ -1,29 +0,0 @@ -INCLUDEPATH += $$PWD -INCLUDEPATH += $$OUT_PWD - -HEADERS += \ - $$PWD/qv4bytecodegenerator_p.h \ - $$PWD/qv4compiler_p.h \ - $$PWD/qv4compilercontext_p.h \ - $$PWD/qv4compilercontrolflow_p.h \ - $$PWD/qv4compilerglobal_p.h \ - $$PWD/qv4compilerscanfunctions_p.h \ - $$PWD/qv4codegen_p.h \ - $$PWD/qqmlirbuilder_p.h \ - $$PWD/qv4instr_moth_p.h \ - $$PWD/qv4bytecodehandler_p.h \ - $$PWD/qv4util_p.h - -SOURCES += \ - $$PWD/qv4bytecodegenerator.cpp \ - $$PWD/qv4compiler.cpp \ - $$PWD/qv4compilercontext.cpp \ - $$PWD/qv4compilerscanfunctions.cpp \ - $$PWD/qv4codegen.cpp \ - $$PWD/qqmlirbuilder.cpp \ - $$PWD/qv4instr_moth.cpp \ - $$PWD/qv4bytecodehandler.cpp - -gcc { - equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing -} diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 940d61ba97..72111b3138 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; @@ -64,71 +26,75 @@ using namespace QQmlJS; return false; \ } -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) -{ - param->nameIndex = parameterNameIndex; - return initType(¶m->type, stringGenerator, typeNameIndex); +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 QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex) +bool Parameter::initType( + QV4::CompiledData::ParameterType *paramType, + const QString &typeName, int typeNameIndex, + QV4::CompiledData::ParameterType::Flag listFlag) { - 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::Variant }, - { "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]); @@ -139,16 +105,15 @@ 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 QQmlJS::AST::SourceLocation &loc) +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.line = loc.startLine; - location.column = loc.startColumn; - + location = loc; idNameIndex = idIndex; id = -1; indexOfDefaultPropertyOrAlias = -1; @@ -161,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); @@ -220,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) @@ -230,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"); @@ -244,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"); @@ -273,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); @@ -317,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); } @@ -356,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; } @@ -366,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; } @@ -401,13 +390,15 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen // Extract errors from the parser for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(url), m.line, qPrintable(m.message)); + qWarning("%s:%d : %s", qPrintable(url), m.loc.startLine, qPrintable(m.message)); continue; } errors << m; } - return false; + + if (!errors.isEmpty() || !parseResult) + return false; } program = parser.ast(); Q_ASSERT(program); @@ -429,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; } @@ -444,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) @@ -485,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; } @@ -518,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)) @@ -539,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); @@ -583,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) @@ -595,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; @@ -615,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); @@ -660,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; @@ -676,20 +703,13 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node) } if (node->version) { - import->majorVersion = node->version->majorVersion; - import->minorVersion = node->version->minorVersion; - } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { - recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); - return false; + 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); @@ -698,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; @@ -751,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>>(); @@ -771,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; @@ -795,25 +1020,25 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) 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<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; } @@ -843,52 +1068,40 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node) if (memberType == QLatin1String("alias")) { return appendAlias(node); } else { - const QStringRef &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 QStringRef &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; } 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) @@ -916,6 +1129,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; @@ -923,14 +1144,16 @@ 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()); - 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(); @@ -938,7 +1161,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; } @@ -949,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; @@ -963,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(const QStringRef &string, int *maj, int *min) +QTypeRevision IRBuilder::extractVersion(QStringView string) { - *maj = -1; *min = -1; - - if (!string.isEmpty()) { + if (string.isEmpty()) + return QTypeRevision(); - int dot = string.indexOf(QLatin1Char('.')); - - 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->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)) { @@ -1025,21 +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->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; @@ -1050,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; @@ -1188,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; @@ -1197,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()) { @@ -1213,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")); @@ -1223,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->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); @@ -1255,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; @@ -1296,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) @@ -1332,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)) { @@ -1360,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")); @@ -1374,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; } @@ -1390,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; @@ -1406,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) { @@ -1421,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; @@ -1459,11 +1601,10 @@ 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.line = location.startLine; - error.column = location.startColumn; + error.loc = location; error.message = description; errors << error; } @@ -1495,7 +1636,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) @@ -1506,23 +1647,95 @@ 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)) { - 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; } } @@ -1542,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.size()); + 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) @@ -1571,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); @@ -1585,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); @@ -1593,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; @@ -1630,6 +1843,14 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen 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); @@ -1678,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; @@ -1693,7 +1913,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; } @@ -1701,9 +1920,25 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen 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 (!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; @@ -1736,8 +1971,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 @@ -1747,22 +1982,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) @@ -1786,6 +2026,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(); } @@ -1796,7 +2042,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); @@ -1827,65 +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; } -bool JSCodeGen::generateCodeForComponents(const QVector<quint32> &componentRoots) -{ - 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) { - 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; - } - - return compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject); -} - -bool JSCodeGen::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) +bool JSCodeGen::generateRuntimeFunctions(QmlIR::Object *object) { - QmlIR::Object *object = document->objects.at(objectIndex); - if (object->flags & QV4::CompiledData::Object::IsComponent) + 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; } diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 4279f5b768..ffd3ad72f7 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -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 #ifndef QQMLIRBUILDER_P_H #define QQMLIRBUILDER_P_H @@ -158,6 +122,13 @@ struct PoolList } struct Iterator { + // turn Iterator into a proper iterator + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T *; + using reference = T &; + T *ptr; explicit Iterator(T *p) : ptr(p) {} @@ -178,8 +149,15 @@ struct PoolList return *ptr; } - void operator++() { + Iterator& operator++() { + ptr = ptr->next; + return *this; + } + + Iterator operator++(int) { + Iterator that {ptr}; ptr = ptr->next; + return that; } bool operator==(const Iterator &rhs) const { @@ -189,10 +167,15 @@ struct PoolList bool operator!=(const Iterator &rhs) const { return ptr != rhs.ptr; } + + operator T *() { return ptr; } + operator const T *() const { return ptr; } }; Iterator begin() { return Iterator(first); } Iterator end() { return Iterator(nullptr); } + + using iterator = Iterator; }; struct Object; @@ -220,13 +203,36 @@ struct Parameter : public QV4::CompiledData::Parameter { Parameter *next; - bool init(QV4::Compiler::JSUnitGenerator *stringGenerator, const QString ¶meterName, const QString &typeName); - static bool init(QV4::CompiledData::Parameter *param, const QV4::Compiler::JSUnitGenerator *stringGenerator, - int parameterNameIndex, int typeNameIndex); - static bool initType(QV4::CompiledData::ParameterType *paramType, - const QV4::Compiler::JSUnitGenerator *stringGenerator, int typeNameIndex); + template<typename IdGenerator> + static bool initType( + QV4::CompiledData::ParameterType *type, const IdGenerator &idGenerator, + const QQmlJS::AST::Type *annotation) + { + using Flag = QV4::CompiledData::ParameterType::Flag; + + if (!annotation) + return initType(type, QString(), idGenerator(QString()), Flag::NoFlag); + + const QString typeId = annotation->typeId->toString(); + const QString typeArgument = + annotation->typeArgument ? annotation->typeArgument->toString() : QString(); + + if (typeArgument.isEmpty()) + return initType(type, typeId, idGenerator(typeId), Flag::NoFlag); + + if (typeId == QLatin1String("list")) + return initType(type, typeArgument, idGenerator(typeArgument), Flag::List); - static QV4::CompiledData::BuiltinType stringToBuiltinType(const QString &typeName); + const QString annotationString = annotation->toString(); + return initType(type, annotationString, idGenerator(annotationString), Flag::NoFlag); + } + + static QV4::CompiledData::CommonType stringToBuiltinType(const QString &typeName); + +private: + static bool initType( + QV4::CompiledData::ParameterType *paramType, const QString &typeName, + int typeNameIndex, QV4::CompiledData::ParameterType::Flag listFlag); }; struct Signal @@ -259,11 +265,21 @@ struct Binding : public QV4::CompiledData::Binding Binding *next; }; +struct InlineComponent : public QV4::CompiledData::InlineComponent +{ + InlineComponent *next; +}; + struct Alias : public QV4::CompiledData::Alias { Alias *next; }; +struct RequiredPropertyExtraData : public QV4::CompiledData::RequiredPropertyExtraData +{ + RequiredPropertyExtraData *next; +}; + struct Function { QV4::CompiledData::Location location; @@ -280,7 +296,7 @@ struct Function Function *next; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression +struct Q_QML_COMPILER_EXPORT CompiledFunctionOrExpression { CompiledFunctionOrExpression() {} @@ -291,7 +307,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT CompiledFunctionOrExpression CompiledFunctionOrExpression *next = nullptr; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT Object +struct Q_QML_COMPILER_EXPORT Object { Q_DECLARE_TR_FUNCTIONS(Object) public: @@ -317,6 +333,11 @@ public: int bindingCount() const { return bindings->count; } const Function *firstFunction() const { return functions->first; } int functionCount() const { return functions->count; } + const InlineComponent *inlineComponent() const { return inlineComponents->first; } + int inlineComponentCount() const { return inlineComponents->count; } + const RequiredPropertyExtraData *requiredPropertyExtraData() const {return requiredPropertyExtraDatas->first; } + int requiredPropertyExtraDataCount() const { return requiredPropertyExtraDatas->count; } + void simplifyRequiredProperties(); PoolList<Binding>::Iterator bindingsBegin() const { return bindings->begin(); } PoolList<Binding>::Iterator bindingsEnd() const { return bindings->end(); } @@ -330,18 +351,24 @@ public: PoolList<Signal>::Iterator signalsEnd() const { return qmlSignals->end(); } PoolList<Function>::Iterator functionsBegin() const { return functions->begin(); } PoolList<Function>::Iterator functionsEnd() const { return functions->end(); } + PoolList<InlineComponent>::Iterator inlineComponentsBegin() const { return inlineComponents->begin(); } + PoolList<InlineComponent>::Iterator inlineComponentsEnd() const { return inlineComponents->end(); } + PoolList<RequiredPropertyExtraData>::Iterator requiredPropertyExtraDataBegin() const {return requiredPropertyExtraDatas->begin(); } + PoolList<RequiredPropertyExtraData>::Iterator requiredPropertyExtraDataEnd() const {return requiredPropertyExtraDatas->end(); } // If set, then declarations for this object (and init bindings for these) should go into the // specified object. Used for declarations inside group properties. Object *declarationsOverride; - void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation()); + void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QV4::CompiledData::Location &location); QString appendEnum(Enum *enumeration); QString appendSignal(Signal *signal); - QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); - QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation); + QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation); + QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::SourceLocation &defaultToken, QQmlJS::SourceLocation *errorLocation); void appendFunction(QmlIR::Function *f); + void appendInlineComponent(InlineComponent *ic); + void appendRequiredPropertyExtraData(RequiredPropertyExtraData *extraData); QString appendBinding(Binding *b, bool isListBinding); Binding *findBinding(quint32 nameIndex) const; @@ -356,6 +383,10 @@ public: int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); } const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } + bool hasFlag(QV4::CompiledData::Object::Flag flag) const { return flags & flag; } + qint32 objectId() const { return id; } + bool hasAliasAsDefaultProperty() const { return defaultPropertyIsAlias; } + private: friend struct ::QQmlIRLoader; @@ -365,19 +396,72 @@ private: PoolList<Signal> *qmlSignals; PoolList<Binding> *bindings; PoolList<Function> *functions; + PoolList<InlineComponent> *inlineComponents; + PoolList<RequiredPropertyExtraData> *requiredPropertyExtraDatas; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT Pragma +struct Q_QML_COMPILER_EXPORT Pragma { - enum PragmaType { - PragmaSingleton = 0x1 + enum PragmaType + { + Singleton, + Strict, + ListPropertyAssignBehavior, + ComponentBehavior, + FunctionSignatureBehavior, + NativeMethodBehavior, + ValueTypeBehavior, + Translator, + }; + + enum ListPropertyAssignBehaviorValue + { + Append, + Replace, + ReplaceIfNotDefault, + }; + + enum ComponentBehaviorValue + { + Unbound, + Bound + }; + + enum FunctionSignatureBehaviorValue + { + Ignored, + Enforced + }; + + enum NativeMethodBehaviorValue + { + AcceptThisObject, + RejectThisObject + }; + + enum ValueTypeBehaviorValue + { + Copy = 0x1, + Addressable = 0x2, + Assertable = 0x4, + }; + Q_DECLARE_FLAGS(ValueTypeBehaviorValues, ValueTypeBehaviorValue); + + PragmaType type; + + union { + ListPropertyAssignBehaviorValue listPropertyAssignBehavior; + ComponentBehaviorValue componentBehavior; + FunctionSignatureBehaviorValue functionSignatureBehavior; + NativeMethodBehaviorValue nativeMethodBehavior; + ValueTypeBehaviorValues::Int valueTypeBehavior; + uint translationContextIndex; }; - quint32 type; QV4::CompiledData::Location location; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT Document +struct Q_QML_COMPILER_EXPORT Document { Document(bool debugMode); QString code; @@ -389,13 +473,22 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT Document QVector<Object*> objects; QV4::Compiler::JSUnitGenerator jsGenerator; - QV4::CompiledData::CompilationUnit javaScriptCompilationUnit; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit; + + bool isSingleton() const { + return std::any_of(pragmas.constBegin(), pragmas.constEnd(), [](const Pragma *pragma) { + return pragma->type == Pragma::Singleton; + }); + } int registerString(const QString &str) { return jsGenerator.registerString(str); } QString stringAt(int index) const { return jsGenerator.stringForIndex(index); } + + int objectCount() const {return objects.size();} + Object* objectAt(int i) const {return objects.at(i);} }; -class Q_QMLCOMPILER_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives +class Q_QML_COMPILER_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives { QmlIR::Document *document; QQmlJS::Engine *engine; @@ -409,15 +502,13 @@ public: void importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column) override; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT IRBuilder : public QQmlJS::AST::Visitor +struct Q_QML_COMPILER_EXPORT IRBuilder : public QQmlJS::AST::Visitor { Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) public: IRBuilder(const QSet<QString> &illegalNames); bool generateFromQml(const QString &code, const QString &url, Document *output); - static bool isSignalPropertyName(const QString &name); - using QQmlJS::AST::Visitor::visit; using QQmlJS::AST::Visitor::endVisit; @@ -433,57 +524,71 @@ public: bool visit(QQmlJS::AST::UiArrayBinding *ast) override; bool visit(QQmlJS::AST::UiObjectBinding *ast) override; bool visit(QQmlJS::AST::UiObjectDefinition *ast) override; + bool visit(QQmlJS::AST::UiInlineComponent *ast) override; bool visit(QQmlJS::AST::UiEnumDeclaration *ast) override; bool visit(QQmlJS::AST::UiPublicMember *ast) override; bool visit(QQmlJS::AST::UiScriptBinding *ast) override; bool visit(QQmlJS::AST::UiSourceElement *ast) override; + bool visit(QQmlJS::AST::UiRequired *ast) override; void throwRecursionDepthError() override { - recordError(QQmlJS::AST::SourceLocation(), + recordError(QQmlJS::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } void accept(QQmlJS::AST::Node *node); // returns index in _objects - bool defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride = nullptr); - bool defineQMLObject(int *objectIndex, QQmlJS::AST::UiObjectDefinition *node, Object *declarationsOverride = nullptr) - { return defineQMLObject(objectIndex, node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, declarationsOverride); } + bool defineQMLObject( + int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, + const QV4::CompiledData::Location &location, + QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride = nullptr); + + bool defineQMLObject( + int *objectIndex, QQmlJS::AST::UiObjectDefinition *node, + Object *declarationsOverride = nullptr) + { + const QQmlJS::SourceLocation location = node->qualifiedTypeNameId->firstSourceLocation(); + return defineQMLObject( + objectIndex, node->qualifiedTypeNameId, + { location.startLine, location.startColumn }, node->initializer, + declarationsOverride); + } static QString asString(QQmlJS::AST::UiQualifiedId *node); - QStringRef asStringRef(QQmlJS::AST::Node *node); - static void extractVersion(const QStringRef &string, int *maj, int *min); - QStringRef textRefAt(const QQmlJS::AST::SourceLocation &loc) const - { return QStringRef(&sourceCode, loc.offset, loc.length); } - QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first, - const QQmlJS::AST::SourceLocation &last) const; + QStringView asStringRef(QQmlJS::AST::Node *node); + static QTypeRevision extractVersion(QStringView string); + QStringView textRefAt(const QQmlJS::SourceLocation &loc) const + { return QStringView(sourceCode).mid(loc.offset, loc.length); } + QStringView textRefAt(const QQmlJS::SourceLocation &first, + const QQmlJS::SourceLocation &last) const; void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode); - void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); + void tryGeneratingTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding); void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode); void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, - const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + void appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, + const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode); - void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, - const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, + void appendBinding(const QQmlJS::SourceLocation &qualifiedNameLocation, + const QQmlJS::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false); bool appendAlias(QQmlJS::AST::UiPublicMember *node); Object *bindingsTarget() const; - bool setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST::Statement *value); + bool setId(const QQmlJS::SourceLocation &idLocation, QQmlJS::AST::Statement *value); // resolves qualified name (font.pixelSize for example) and returns the last name along // with the object any right-hand-side of a binding should apply to. bool resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, Object **object, bool onAssignment = false); - void recordError(const QQmlJS::AST::SourceLocation &location, const QString &description); + void recordError(const QQmlJS::SourceLocation &location, const QString &description); quint32 registerString(const QString &str) const { return jsGenerator->registerString(str); } template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } @@ -493,11 +598,12 @@ public: static bool isStatementNodeScript(QQmlJS::AST::Statement *statement); static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement); - QString sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation); + QString sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::SourceLocation *errorLocation); QList<QQmlJS::DiagnosticMessage> errors; QSet<QString> illegalNames; + QSet<QString> inlineComponentsNames; QList<const QV4::CompiledData::Import *> _imports; QList<Pragma*> _pragmas; @@ -511,9 +617,11 @@ public: QQmlJS::MemoryPool *pool; QString sourceCode; QV4::Compiler::JSUnitGenerator *jsGenerator; + + bool insideInlineComponent = false; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT QmlUnitGenerator +struct Q_QML_COMPILER_EXPORT QmlUnitGenerator { void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher()); @@ -522,21 +630,223 @@ private: char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen +struct Q_QML_COMPILER_EXPORT JSCodeGen : public QV4::Compiler::Codegen { - JSCodeGen(Document *document, const QSet<QString> &globalNames); + JSCodeGen(Document *document, const QSet<QString> &globalNames, + QV4::Compiler::CodegenWarningInterface *iface = + QV4::Compiler::defaultCodegenWarningInterface(), + bool storeSourceLocations = false); // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions - QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); + QVector<int> + generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions); - bool generateCodeForComponents(const QVector<quint32> &componentRoots); - bool compileComponent(int contextObject); - bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); + bool generateRuntimeFunctions(QmlIR::Object *object); private: Document *document; }; +// RegisterStringN ~= std::function<int(QStringView)> +// FinalizeTranlationData ~= std::function<void(QV4::CompiledData::Binding::ValueType, QV4::CompiledData::TranslationData)> +/* + \internal + \a base: name of the potential translation function + \a args: arguments to the function call + \a registerMainString: Takes the first argument passed to the translation function, and it's + result will be stored in a TranslationData's stringIndex for translation bindings and in numbeIndex + for string bindings. + \a registerCommentString: Takes the comment argument passed to some of the translation functions. + Result will be stored in a TranslationData's commentIndex + \a finalizeTranslationData: Takes the type of the binding and the previously set up TranslationData + */ +template< + typename RegisterMainString, + typename RegisterCommentString, + typename RegisterContextString, + typename FinalizeTranslationData> +void tryGeneratingTranslationBindingBase(QStringView base, QQmlJS::AST::ArgumentList *args, + RegisterMainString registerMainString, + RegisterCommentString registerCommentString, + RegisterContextString registerContextString, + FinalizeTranslationData finalizeTranslationData + ) +{ + if (base == QLatin1String("qsTr")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + + // empty string + translationData.commentIndex = 0; + + // No context (not empty string) + translationData.contextIndex = QV4::CompiledData::TranslationData::NoContextIndex; + + 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 = registerMainString(translation); + + 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 = registerCommentString(arg2->value); + + 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 + + finalizeTranslationData(QV4::CompiledData::Binding::Type_Translation, translationData); + } else if (base == QLatin1String("qsTrId")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + + // empty string, but unused + translationData.commentIndex = 0; + + // No context (not empty string) + translationData.contextIndex = QV4::CompiledData::TranslationData::NoContextIndex; + + 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 = registerMainString(id); + + 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 + + finalizeTranslationData(QV4::CompiledData::Binding::Type_TranslationById, 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 + + QV4::CompiledData::TranslationData translationData; + translationData.number = registerMainString(str); + finalizeTranslationData(QV4::CompiledData::Binding::Type_String, translationData); + } 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 + + 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 + } + + args = args->next; + if (args) + return; // too many arguments, stop + + QV4::CompiledData::TranslationData fakeTranslationData; + fakeTranslationData.number = registerMainString(str); + finalizeTranslationData(QV4::CompiledData::Binding::Type_String, fakeTranslationData); + } else if (base == QLatin1String("qsTranslate")) { + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string + + if (!args || !args->next) + return; // less than 2 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.contextIndex = registerContextString(translation); + + args = args->next; + Q_ASSERT(args); + + QQmlJS::AST::StringLiteral *arg2 + = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression); + if (!arg2) + return; // second argument is not a string, stop + translationData.stringIndex = registerMainString(arg2->value); + + args = args->next; + if (args) { + QQmlJS::AST::StringLiteral *arg3 + = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(args->expression); + if (!arg3) + return; // third argument is not a string, stop + translationData.commentIndex = registerCommentString(arg3->value); + + args = args->next; + if (args) { + if (QQmlJS::AST::NumericLiteral *arg4 + = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(args->expression)) { + translationData.number = int(arg4->value); + args = args->next; + } else { + return; // fourth argument is not a translation number, stop + } + } + } + + if (args) + return; // too many arguments, stop + + finalizeTranslationData(QV4::CompiledData::Binding::Type_Translation, translationData); + } +} + } // namespace QmlIR QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index 7df1614ffe..b88c83512f 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 <private/qv4bytecodegenerator_p.h> #include <private/qv4compilercontext_p.h> @@ -45,9 +9,15 @@ QT_USE_NAMESPACE using namespace QV4; using namespace Moth; -void BytecodeGenerator::setLocation(const QQmlJS::AST::SourceLocation &loc) +void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc) { currentLine = static_cast<int>(loc.startLine); + currentSourceLocation = loc; +} + +void BytecodeGenerator::incrementStatement() +{ + ++currentStatement; } int BytecodeGenerator::newRegister() @@ -164,26 +134,36 @@ void BytecodeGenerator::finalize(Compiler::Context *context) // collect content and line numbers QByteArray code; - QVector<CompiledData::CodeOffsetToLine> lineNumbers; + QVector<CompiledData::CodeOffsetToLineAndStatement> lineAndStatementNumbers; + currentLine = -1; + currentStatement = -1; + Q_UNUSED(startLine); - for (const auto &i : qAsConst(instructions)) { - if (i.line != currentLine) { - currentLine = i.line; - CompiledData::CodeOffsetToLine entry; + for (qsizetype i = 0; i < instructions.size(); i++) { + if (instructions[i].line != currentLine || instructions[i].statement != currentStatement) { + currentLine = instructions[i].line; + currentStatement = instructions[i].statement; + CompiledData::CodeOffsetToLineAndStatement entry; entry.codeOffset = code.size(); entry.line = currentLine; - lineNumbers.append(entry); + entry.statement = currentStatement; + lineAndStatementNumbers.append(entry); } - code.append(reinterpret_cast<const char *>(i.packed), i.size); + + if (m_sourceLocationTable) + m_sourceLocationTable->entries[i].offset = static_cast<quint32>(code.size()); + + code.append(reinterpret_cast<const char *>(instructions[i].packed), instructions[i].size); } context->code = code; - context->lineNumberMapping = lineNumbers; + context->lineAndStatementNumberMapping = lineAndStatementNumbers; + context->sourceLocationTable = std::move(m_sourceLocationTable); - for (const auto &li : _labelInfos) { + context->labelInfo.reserve(context->labelInfo.size() + _labelInfos.size()); + for (const auto &li : _labelInfos) context->labelInfo.push_back(instructions.at(labels.at(li.labelIndex)).position); - } } int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) { @@ -215,6 +195,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instru currentLine = -currentLine; addInstruction(Instruction::Debug()); currentLine = -currentLine; + currentSourceLocation = QQmlJS::SourceLocation(); } QT_WARNING_POP } @@ -225,7 +206,16 @@ QT_WARNING_POP int s = argCount*sizeof(int); if (offsetOfOffset != -1) offsetOfOffset += Instr::encodedLength(type); - I instr{type, static_cast<short>(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" }; + I instr { + type, + static_cast<short>(s + Instr::encodedLength(type)), + 0, + currentLine, + currentStatement, + offsetOfOffset, + -1, + "\0\0" + }; uchar *code = instr.packed; code = Instr::pack(code, Instr::wideInstructionType(type)); @@ -235,6 +225,8 @@ QT_WARNING_POP } instructions.append(instr); + if (m_sourceLocationTable) + m_sourceLocationTable->entries.append({ 0, currentSourceLocation }); return pos; } diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 8c509dd9f1..fa14754f85 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #ifndef QV4BYTECODEGENERATOR_P_H #define QV4BYTECODEGENERATOR_P_H @@ -52,14 +16,16 @@ // #include <private/qv4instr_moth_p.h> #include <private/qv4compileddata_p.h> +#include <private/qv4compilercontext_p.h> +#include <private/qqmljssourcelocation_p.h> + +#include <memory> QT_BEGIN_NAMESPACE namespace QQmlJS { -namespace AST { class SourceLocation; } -} namespace QV4 { @@ -71,8 +37,12 @@ namespace Moth { class BytecodeGenerator { public: - BytecodeGenerator(int line, bool debug) - : startLine(line), debugMode(debug) {} + BytecodeGenerator(int line, bool debug, bool storeSourceLocation = false) + : startLine(line), debugMode(debug) + { + if (storeSourceLocation) + m_sourceLocationTable.reset(new QV4::Compiler::Context::SourceLocationTable {}); + } struct Label { enum LinkMode { @@ -88,7 +58,7 @@ public: link(); } - void link() { + void link() const { Q_ASSERT(index >= 0); Q_ASSERT(generator->labels[index] == -1); generator->labels[index] = generator->instructions.size(); @@ -188,13 +158,27 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpNotUndefined() { - Instruction::JumpNotUndefined data; + Instruction::JumpNotUndefined data{}; return addJumpInstruction(data); } Q_REQUIRED_RESULT Jump jumpNoException() { - Instruction::JumpNoException data; + Instruction::JumpNoException data{}; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpOptionalLookup(int index) + { + Instruction::GetOptionalLookup data{}; + data.index = index; + return addJumpInstruction(data); + } + + Q_REQUIRED_RESULT Jump jumpOptionalProperty(int name) + { + Instruction::LoadOptionalProperty data{}; + data.name = name; return addJumpInstruction(data); } @@ -244,7 +228,8 @@ QT_WARNING_POP - void setLocation(const QQmlJS::AST::SourceLocation &loc); + void setLocation(const QQmlJS::SourceLocation &loc); + void incrementStatement(); ExceptionHandler *exceptionHandler() const { return currentExceptionHandler; @@ -295,6 +280,7 @@ private: short size; uint position; int line; + int statement; int offsetForJump; int linkedLabel; unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type @@ -313,6 +299,9 @@ public: private: int startLine = 0; int currentLine = 0; + int currentStatement = 0; + QQmlJS::SourceLocation currentSourceLocation; + std::unique_ptr<QV4::Compiler::Context::SourceLocationTable> m_sourceLocationTable; bool debugMode = false; int lastInstrType = -1; diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index f9f755b8c0..6ee027a6b7 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 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 <private/qv4bytecodehandler_p.h> diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index f1e7c99447..0a74fc52ed 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 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 #ifndef QV4BYTECODEHANDLER_P_H #define QV4BYTECODEHANDLER_P_H @@ -50,6 +14,8 @@ // // We mean it. // + +#include <private/qtqmlcompilerglobal_p.h> #include <private/qv4instr_moth_p.h> QT_BEGIN_NAMESPACE @@ -90,12 +56,15 @@ namespace Moth { #define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \ INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) -class ByteCodeHandler +class Q_QML_COMPILER_EXPORT ByteCodeHandler { + Q_DISABLE_COPY_MOVE(ByteCodeHandler) public: + ByteCodeHandler() = default; virtual ~ByteCodeHandler(); void decode(const char *code, uint len); + void reset() { _currentOffset = _nextOffset = 0; } int currentInstructionOffset() const { return _currentOffset; } int nextInstructionOffset() const { return _nextOffset; } diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index c43ea64e2e..8a81bc98f9 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1,49 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 "qv4codegen_p.h" -#include "qv4util_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QStringList> #include <QtCore/QStack> #include <QtCore/qurl.h> +#include <QtCore/qloggingcategory.h> #include <QScopeGuard> #include <private/qqmljsast_p.h> #include <private/qqmljslexer_p.h> @@ -57,20 +21,33 @@ #include <private/qqmljsdiagnosticmessage_p.h> #include <cmath> -#include <iostream> - -static const bool disable_lookups = false; #ifdef CONST #undef CONST #endif -QT_USE_NAMESPACE +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +Q_STATIC_LOGGING_CATEGORY(lcQmlUsedBeforeDeclared, "qt.qml.usedbeforedeclared"); +Q_STATIC_LOGGING_CATEGORY(lcQmlInjectedParameter, "qt.qml.injectedparameter"); + using namespace QV4; using namespace QV4::Compiler; using namespace QQmlJS; using namespace QQmlJS::AST; +void CodegenWarningInterface::reportVarUsedBeforeDeclaration( + const QString &name, const QString &fileName, QQmlJS::SourceLocation declarationLocation, + QQmlJS::SourceLocation accessLocation) +{ + qCWarning(lcQmlUsedBeforeDeclared).nospace().noquote() + << fileName << ":" << accessLocation.startLine << ":" << accessLocation.startColumn + << " Variable \"" << name << "\" is used before its declaration at " + << declarationLocation.startLine << ":" << declarationLocation.startColumn << "."; +} + static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator, const Statement *body, const SourceLocation &fallback) { @@ -90,14 +67,39 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene } } -Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) - : _module(nullptr) - , _returnAddress(-1) - , _context(nullptr) - , _labelledStatement(nullptr) - , jsUnitGenerator(jsUnitGenerator) - , _strictMode(strict) - , _fileNameIsUrl(false) +void Codegen::generateThrowException(const QString &type, const QString &text) +{ + RegisterScope scope(this); + Instruction::Construct construct; + if (text.isEmpty()) { + construct.argc = 0; + construct.argv = 0; + } else { + construct.argc = 1; + Instruction::LoadRuntimeString load; + load.stringId = registerString(text); + bytecodeGenerator->addInstruction(load); + construct.argv = Reference::fromAccumulator(this).storeOnStack().stackSlot(); + } + Reference r = referenceForName(type, false); + r = r.storeOnStack(); + construct.func = r.stackSlot(); + bytecodeGenerator->addInstruction(construct); + Instruction::ThrowException throwException; + bytecodeGenerator->addInstruction(throwException); +} + +Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict, + CodegenWarningInterface *iface, bool storeSourceLocations) + : _module(nullptr), + _returnAddress(-1), + _context(nullptr), + _labelledStatement(nullptr), + jsUnitGenerator(jsUnitGenerator), + _strictMode(strict), + storeSourceLocations(storeSourceLocations), + _fileNameIsUrl(false), + _interface(iface) { jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); pushExpr(); @@ -391,6 +393,7 @@ void Codegen::statement(Statement *ast) { RegisterScope scope(this); + bytecodeGenerator->incrementStatement(); bytecodeGenerator->setLocation(ast->firstSourceLocation()); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); @@ -406,6 +409,7 @@ void Codegen::statement(ExpressionNode *ast) } else { RegisterScope scope(this); + bytecodeGenerator->incrementStatement(); pushExpr(Result(nx)); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); qSwap(_volatileMemoryLocations, vLocs); @@ -708,9 +712,8 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle RegisterScope scope(this); Reference iterator = Reference::fromStackSlot(this); - Reference iteratorValue = Reference::fromStackSlot(this); - Reference iteratorDone = Reference::fromStackSlot(this); - Reference::storeConstOnStack(this, Encode(false), iteratorDone.stackSlot()); + QVarLengthArray<Reference> iteratorValues; + Reference ignored; array.loadInAccumulator(); Instruction::GetIterator iteratorObjInstr; @@ -718,45 +721,76 @@ void Codegen::destructureElementList(const Codegen::Reference &array, PatternEle bytecodeGenerator->addInstruction(iteratorObjInstr); iterator.storeConsumeAccumulator(); + BytecodeGenerator::Label done = bytecodeGenerator->newLabel(); + Reference needsClose = Reference::storeConstOnStack(this, Encode(false)); + + for (PatternElementList *p = bindingList; p; p = p->next) { + PatternElement *e = p->element; + for (Elision *elision = p->elision; elision; elision = elision->next) { + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + if (!ignored.isValid()) + ignored = Reference::fromStackSlot(this); + next.value = ignored.stackSlot(); + bytecodeGenerator->addJumpInstruction(next).link(done); + } + + if (!e) + continue; + + if (e->type != PatternElement::RestElement) { + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + iteratorValues.push_back(Reference::fromStackSlot(this)); + next.value = iteratorValues.back().stackSlot(); + bytecodeGenerator->addJumpInstruction(next).link(done); + } + } + + // If we've iterated through all the patterns without exhausing the iterator, it needs + // to be closed. But we don't close it here because: + // a, closing might throw an exception and we want to assign the values before we handle that + // b, there might be a rest element that could still continue iterating + Reference::fromConst(this, Encode(true)).storeOnStack(needsClose.stackSlot()); + + done.link(); + bytecodeGenerator->checkException(); + { - auto cleanup = [this, iterator, iteratorDone]() { + ControlFlowUnwindCleanup flow(this, [&]() { + BytecodeGenerator::Label skipClose = bytecodeGenerator->newLabel(); + needsClose.loadInAccumulator(); + bytecodeGenerator->jumpFalse().link(skipClose); iterator.loadInAccumulator(); Instruction::IteratorClose close; - close.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(close); - }; - - ControlFlowUnwindCleanup flow(this, cleanup); + skipClose.link(); + }); + auto it = iteratorValues.constBegin(); for (PatternElementList *p = bindingList; p; p = p->next) { PatternElement *e = p->element; - for (Elision *elision = p->elision; elision; elision = elision->next) { - iterator.loadInAccumulator(); - Instruction::IteratorNext next; - next.value = iteratorValue.stackSlot(); - next.done = iteratorDone.stackSlot(); - bytecodeGenerator->addInstruction(next); - } if (!e) continue; - RegisterScope scope(this); - iterator.loadInAccumulator(); - if (e->type == PatternElement::RestElement) { - Reference::fromConst(this, Encode(true)).storeOnStack(iteratorDone.stackSlot()); + Q_ASSERT(it == iteratorValues.constEnd()); + + // The rest element is guaranteed to exhaust the iterator + Reference::fromConst(this, Encode(false)).storeOnStack(needsClose.stackSlot()); + + iterator.loadInAccumulator(); bytecodeGenerator->addInstruction(Instruction::DestructureRestElement()); - initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition); + initializeAndDestructureBindingElement( + e, Reference::fromAccumulator(this), isDefinition); } else { - Instruction::IteratorNext next; - next.value = iteratorValue.stackSlot(); - next.done = iteratorDone.stackSlot(); - bytecodeGenerator->addInstruction(next); - initializeAndDestructureBindingElement(e, iteratorValue, isDefinition); - if (hasError()) - return; + Q_ASSERT(it != iteratorValues.constEnd()); + initializeAndDestructureBindingElement(e, *it++, isDefinition); } + + if (hasError()) + return; } } } @@ -775,86 +809,72 @@ void Codegen::destructurePattern(Pattern *p, const Reference &rhs) bool Codegen::visit(ArgumentList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(CaseBlock *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(CaseClause *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(CaseClauses *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(Catch *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(DefaultClause *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(Elision *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(Finally *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(FormalParameterList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(Program *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(PatternElement *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(PatternElementList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(PatternProperty *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(PatternPropertyList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(ExportDeclaration *ast) @@ -895,68 +915,62 @@ bool Codegen::visit(TypeAnnotation *ast) bool Codegen::visit(StatementList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiArrayMemberList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiImport *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiHeaderItemList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); +} + +bool Codegen::visit(UiPragmaValueList *) +{ + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiPragma *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiObjectInitializer *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiObjectMemberList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiParameterList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiProgram *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(UiQualifiedId *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(VariableDeclarationList *) { - Q_UNREACHABLE(); - return false; + Q_UNREACHABLE_RETURN(false); } bool Codegen::visit(ClassExpression *ast) @@ -1182,7 +1196,6 @@ bool Codegen::visit(ArrayPattern *ast) RegisterScope scope(this); Reference iterator = Reference::fromStackSlot(this); - Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack(); Reference lhsValue = Reference::fromStackSlot(this); // There should be a temporal block, so that variables declared in lhs shadow outside vars. @@ -1202,24 +1215,23 @@ bool Codegen::visit(ArrayPattern *ast) BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label done = bytecodeGenerator->newLabel(); { - auto cleanup = [this, iterator, iteratorDone]() { + auto cleanup = [this, iterator, done]() { iterator.loadInAccumulator(); Instruction::IteratorClose close; - close.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(close); + done.link(); }; - ControlFlowLoop flow(this, &end, &in, cleanup); + ControlFlowLoop flow(this, &end, &in, std::move(cleanup)); in.link(); bytecodeGenerator->addLoopStart(in); iterator.loadInAccumulator(); Instruction::IteratorNext next; next.value = lhsValue.stackSlot(); - next.done = iteratorDone.stackSlot(); - bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(next).link(done); lhsValue.loadInAccumulator(); pushAccumulator(); @@ -1252,13 +1264,23 @@ bool Codegen::visit(ArrayMemberExpression *ast) if (hasError()) return false; + const bool isTailOfChain = traverseOptionalChain(ast); + TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); + + auto writeSkip = [&]() { + base.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::CmpEqNull()); + auto jumpToUndefined = bytecodeGenerator->jumpTrue(); + m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined)); + }; + if (hasError()) return false; if (base.isSuper()) { Reference index = expression(ast->expression).storeOnStack(); - setExprResult(Reference::fromSuperProperty(index)); + optionalChainFinalizer(Reference::fromSuperProperty(index), isTailOfChain); return false; } base = base.storeOnStack(); @@ -1268,17 +1290,32 @@ bool Codegen::visit(ArrayMemberExpression *ast) QString s = str->value.toString(); uint arrayIndex = stringToArrayIndex(s); if (arrayIndex == UINT_MAX) { - setExprResult(Reference::fromMember(base, str->value.toString())); + auto ref = Reference::fromMember(base, s, ast->expression->firstSourceLocation(), + ast->isOptional, + &m_optionalChainsStates.top().jumpsToPatch); + setExprResult(ref); + optionalChainFinalizer(ref, isTailOfChain); return false; } + + if (ast->isOptional) + writeSkip(); + Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex)); - setExprResult(Reference::fromSubscript(base, index)); + optionalChainFinalizer(Reference::fromSubscript(base, index), isTailOfChain); return false; } + + + if (ast->isOptional) + writeSkip(); + Reference index = expression(ast->expression); + if (hasError()) return false; - setExprResult(Reference::fromSubscript(base, index)); + + optionalChainFinalizer(Reference::fromSubscript(base, index), isTailOfChain); return false; } @@ -1371,6 +1408,34 @@ bool Codegen::visit(BinaryExpression *ast) setExprResult(Reference::fromAccumulator(this)); } return false; + } else if (ast->op == QSOperator::Coalesce) { + + Reference left = expression(ast->left); + if (hasError()) + return false; + + BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel(); + + Instruction::CmpEqNull cmp; + + left = left.storeOnStack(); + left.loadInAccumulator(); + bytecodeGenerator->addInstruction(cmp); + + bytecodeGenerator->jumpTrue().link(iftrue); + + blockTailCalls.unblock(); + + left.loadInAccumulator(); + BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); + iftrue.link(); + + Reference right = expression(ast->right); + right.loadInAccumulator(); + jump_endif.link(); + setExprResult(Reference::fromAccumulator(this)); + + return false; } else if (ast->op == QSOperator::Assign) { if (AST::Pattern *p = ast->left->patternCast()) { RegisterScope scope(this); @@ -1446,7 +1511,7 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError()) return false; - binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator(); + binopHelper(ast, baseOp(ast->op), tempLeft, right).loadInAccumulator(); setExprResult(left.storeRetainAccumulator()); break; @@ -1459,12 +1524,13 @@ bool Codegen::visit(BinaryExpression *ast) Reference right = expression(ast->right); if (hasError()) return false; - setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left)); + setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), right, left)); break; } - // intentional fall-through! + Q_FALLTHROUGH(); case QSOperator::In: case QSOperator::InstanceOf: + case QSOperator::As: case QSOperator::Equal: case QSOperator::NotEqual: case QSOperator::Ge: @@ -1493,18 +1559,20 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError()) return false; - setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right)); + setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), left, right)); break; } - } // switch return false; } -Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right) +Codegen::Reference Codegen::binopHelper(BinaryExpression *ast, QSOperator::Op oper, Reference &left, + Reference &right) { + auto loc = combine(ast->left->firstSourceLocation(), ast->right->lastSourceLocation()); + bytecodeGenerator->setLocation(loc); switch (oper) { case QSOperator::Add: { left = left.storeOnStack(); @@ -1661,6 +1729,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re bytecodeGenerator->addInstruction(binop); break; } + case QSOperator::As: { + Instruction::As as; + left = left.storeOnStack(); + right.loadInAccumulator(); + as.lhs = left.stackSlot(); + bytecodeGenerator->addInstruction(as); + break; + } case QSOperator::In: { Instruction::CmpIn binop; left = left.storeOnStack(); @@ -1872,23 +1948,40 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe return Reference(); } +Codegen::Reference Codegen::loadSubscriptForCall(const Codegen::Reference &base) +{ + // Retrieve the function to be called before generating the arguments. + // Generating the arguments might change the array. + base.elementSubscript.loadInAccumulator(); + Codegen::Instruction::LoadElement load; + load.base = base.elementBase; + bytecodeGenerator->addInstruction(load); + return Reference::fromAccumulator(this); +} + bool Codegen::visit(CallExpression *ast) { if (hasError()) return false; + const bool isTailOfChain = traverseOptionalChain(ast); + RegisterScope scope(this); TailCallBlocker blockTailCalls(this); - Reference base = expression(ast->base); + Reference expr = expression(ast->base); + Reference base = expr; if (hasError()) return false; switch (base.type) { case Reference::Member: - case Reference::Subscript: base = base.asLValue(); break; + case Reference::Subscript: + base.element = loadSubscriptForCall(base).storeOnStack().stackSlot(); + base.subscriptLoadedForCall = true; + break; case Reference::Name: break; case Reference::Super: @@ -1901,9 +1994,23 @@ bool Codegen::visit(CallExpression *ast) break; } + if (expr.hasSavedCallBaseSlot) { + // Hack to preserve `this` context in optional chain calls. See optionalChainFinalizer(). + base.hasSavedCallBaseSlot = true; + base.savedCallBaseSlot = expr.savedCallBaseSlot; + base.savedCallPropertyNameIndex = expr.savedCallPropertyNameIndex; + } + int thisObject = bytecodeGenerator->newRegister(); int functionObject = bytecodeGenerator->newRegister(); + if (ast->isOptional || m_optionalChainsStates.top().actuallyHasOptionals) { + base.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::CmpEqNull()); + auto jumpToUndefined = bytecodeGenerator->jumpTrue(); + m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined)); + } + auto calldata = pushArgs(ast->arguments); if (hasError()) return false; @@ -1915,78 +2022,108 @@ bool Codegen::visit(CallExpression *ast) baseObject.storeOnStack(thisObject); baseObject = Reference::fromStackSlot(this, thisObject); } - if (!base.isStackSlot()) { - base.storeOnStack(functionObject); - base = Reference::fromStackSlot(this, functionObject); - } + + const int func = [&]() { + if (base.type == Reference::Subscript) + return base.element; + + if (!base.isStackSlot()) { + base.storeOnStack(functionObject); + base = Reference::fromStackSlot(this, functionObject); + } + + return base.stackSlot(); + }(); if (calldata.hasSpread) { Instruction::CallWithSpread call; - call.func = base.stackSlot(); + call.func = func; call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else { Instruction::TailCall call; - call.func = base.stackSlot(); + call.func = func; call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } - setExprResult(Reference::fromAccumulator(this)); + optionalChainFinalizer(Reference::fromAccumulator(this), isTailOfChain); return false; - } - handleCall(base, calldata, functionObject, thisObject); + handleCall(base, calldata, functionObject, thisObject, ast->isOptional); + optionalChainFinalizer(Reference::fromAccumulator(this), isTailOfChain); return false; } -void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject) +void Codegen::endVisit(CallExpression *ast) { + m_seenOptionalChainNodes.remove(ast); +} + +void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject, bool optional) +{ + if (base.sourceLocation.isValid()) + bytecodeGenerator->setLocation(base.sourceLocation); + //### Do we really need all these call instructions? can's we load the callee in a temp? - if (base.type == Reference::Member) { - if (!disable_lookups && useFastLookups) { + if (base.type == Reference::Member || base.hasSavedCallBaseSlot) { + if (useFastLookups) { Instruction::CallPropertyLookup call; - call.base = base.propertyBase.stackSlot(); - call.lookupIndex = registerGetterLookup(base.propertyNameIndex); + if (base.hasSavedCallBaseSlot) { + call.base = base.savedCallBaseSlot; + call.lookupIndex = registerGetterLookup( + base.savedCallPropertyNameIndex, JSUnitGenerator::LookupForCall); + } else { + call.base = base.propertyBase.stackSlot(); + call.lookupIndex = registerGetterLookup( + base.propertyNameIndex, JSUnitGenerator::LookupForCall); + } call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else { Instruction::CallProperty call; - call.base = base.propertyBase.stackSlot(); - call.name = base.propertyNameIndex; + if (base.hasSavedCallBaseSlot) { + call.base = base.savedCallBaseSlot; + call.name = base.savedCallPropertyNameIndex; + } else { + call.base = base.propertyBase.stackSlot(); + call.name = base.propertyNameIndex; + } call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::Subscript) { - Instruction::CallElement call; - call.base = base.elementBase; - call.index = base.elementSubscript.stackSlot(); + Instruction::CallWithReceiver call; + call.thisObject = base.elementBase.stackSlot(); + call.name = base.element; call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else if (base.type == Reference::Name) { - if (base.name == QStringLiteral("eval")) { + if (base.name == QStringLiteral("eval") && !optional) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); - } else if (!disable_lookups && useFastLookups && base.global) { + } else if (useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; - call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); + call.index = registerQmlContextPropertyGetterLookup( + base.nameAsIndex(), JSUnitGenerator::LookupForCall); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else { Instruction::CallGlobalLookup call; - call.index = registerGlobalGetterLookup(base.nameAsIndex()); + call.index = registerGlobalGetterLookup( + base.nameAsIndex(), JSUnitGenerator::LookupForCall); call.argc = calldata.argc; call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); @@ -2022,8 +2159,6 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } - - setExprResult(Reference::fromAccumulator(this)); } Codegen::Arguments Codegen::pushArgs(ArgumentList *args) @@ -2057,8 +2192,10 @@ Codegen::Arguments Codegen::pushArgs(ArgumentList *args) break; if (!argc && !it->next && !hasSpread) { // avoid copy for functions taking a single argument - if (e.isStackSlot()) + if (e.isStackSlot()) { + e.tdzCheck(); return { 1, e.stackSlot(), hasSpread }; + } } (void) e.storeOnStack(calldata + argc); ++argc; @@ -2131,12 +2268,18 @@ bool Codegen::visit(DeleteExpression *ast) if (hasError()) return false; + const bool isTailOfChain = traverseOptionalChain(ast); + RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); if (hasError()) return false; + const bool chainActuallyHasOptionals = m_optionalChainsStates.top().actuallyHasOptionals; + if (chainActuallyHasOptionals) + Q_ASSERT(expr.type == Reference::Member || expr.type == Reference::Subscript); + switch (expr.type) { case Reference::SuperProperty: // ### this should throw a reference error at runtime. @@ -2144,7 +2287,7 @@ bool Codegen::visit(DeleteExpression *ast) case Reference::StackSlot: if (!expr.stackSlotIsLocalOrArgument) break; - // fall through + Q_FALLTHROUGH(); case Reference::ScopedLocal: // Trying to delete a function argument might throw. if (_context->isStrict) { @@ -2167,6 +2310,14 @@ bool Codegen::visit(DeleteExpression *ast) case Reference::Member: { //### maybe add a variant where the base can be in the accumulator? expr = expr.asLValue(); + + if (chainActuallyHasOptionals) { + expr.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::CmpEqNull()); + auto jumpToUndefined = bytecodeGenerator->jumpTrue(); + m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined)); + } + Instruction::LoadRuntimeString instr; instr.stringId = expr.propertyNameIndex; bytecodeGenerator->addInstruction(instr); @@ -2176,17 +2327,29 @@ bool Codegen::visit(DeleteExpression *ast) del.base = expr.propertyBase.stackSlot(); del.index = index.stackSlot(); bytecodeGenerator->addInstruction(del); - setExprResult(Reference::fromAccumulator(this)); + auto ref = Reference::fromAccumulator(this); + + optionalChainFinalizer(ref, isTailOfChain, true); return false; } case Reference::Subscript: { //### maybe add a variant where the index can be in the accumulator? expr = expr.asLValue(); + + if (chainActuallyHasOptionals) { + expr.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::CmpEqNull()); + auto jumpToUndefined = bytecodeGenerator->jumpTrue(); + m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined)); + } + Instruction::DeleteProperty del; del.base = expr.elementBase; del.index = expr.elementSubscript.stackSlot(); bytecodeGenerator->addInstruction(del); - setExprResult(Reference::fromAccumulator(this)); + auto ref = Reference::fromAccumulator(this); + + optionalChainFinalizer(ref, isTailOfChain, true); return false; } default: @@ -2197,6 +2360,10 @@ bool Codegen::visit(DeleteExpression *ast) return false; } +void Codegen::endVisit(DeleteExpression *ast) { + m_seenOptionalChainNodes.remove(ast); +} + bool Codegen::visit(FalseLiteral *) { if (hasError()) @@ -2215,12 +2382,115 @@ bool Codegen::visit(SuperLiteral *) return false; } +bool Codegen::traverseOptionalChain(Node *node) +{ + if (m_seenOptionalChainNodes.contains(node)) + return false; + + const auto isOptionalChainableNode = [](const Node *node) { + return node->kind == Node::Kind_FieldMemberExpression || + node->kind == Node::Kind_CallExpression || + node->kind == Node::Kind_ArrayMemberExpression || + node->kind == Node::Kind_DeleteExpression; + }; + m_optionalChainsStates.emplace(); + while (isOptionalChainableNode(node)) { + m_seenOptionalChainNodes.insert(node); + + switch (node->kind) { + case Node::Kind_FieldMemberExpression: { + auto *fme = AST::cast<FieldMemberExpression *>(node); + m_optionalChainsStates.top().actuallyHasOptionals |= fme->isOptional; + node = fme->base; + break; + } + case Node::Kind_CallExpression: { + auto *ce = AST::cast<CallExpression *>(node); + m_optionalChainsStates.top().actuallyHasOptionals |= ce->isOptional; + node = ce->base; + break; + } + case Node::Kind_ArrayMemberExpression: { + auto *ame = AST::cast<ArrayMemberExpression *>(node); + m_optionalChainsStates.top().actuallyHasOptionals |= ame->isOptional; + node = ame->base; + break; + } + case Node::Kind_DeleteExpression: + node = AST::cast<DeleteExpression *>(node)->expression; + break; + default: + Q_UNREACHABLE(); + } + } + + return true; +} + +void Codegen::optionalChainFinalizer(const Reference &expressionResult, bool tailOfChain, + bool isDeleteExpression) +{ + auto &chainState = m_optionalChainsStates.top(); + if (!tailOfChain) { + setExprResult(expressionResult); + return; + } else if (!chainState.actuallyHasOptionals) { + setExprResult(expressionResult); + m_optionalChainsStates.pop(); + return; + } + + auto savedBaseSlot = -1; + if (expressionResult.type == Reference::Member) + savedBaseSlot = expressionResult.propertyBase.storeOnStack().stackSlot(); + expressionResult.loadInAccumulator(); + + std::optional<Moth::BytecodeGenerator::Jump> jumpToDone; + if (!isDeleteExpression) // Delete expressions always return true, avoid the extra jump + jumpToDone.emplace(bytecodeGenerator->jump()); + + for (auto &jump : chainState.jumpsToPatch) + jump.link(); + + if (isDeleteExpression) + bytecodeGenerator->addInstruction(Instruction::LoadTrue()); + else + bytecodeGenerator->addInstruction(Instruction::LoadUndefined()); + + if (jumpToDone.has_value()) + jumpToDone.value().link(); + + auto ref = Reference::fromAccumulator(this); + if (expressionResult.type == Reference::Member) { + /* Because the whole optional chain is handled at once with a chain finalizer instead of + * instruction by instruction, the result of the chain (either undefined or the result of + * the optional operation) is stored in the accumulator. This works fine except for one + * edge case where the `this` context is required in a call + * (see tst_ecmascripttests: language/expressions/optional-chaining/optional-call-preserves-this.js). + * + * In order to preserve the `this` context in the call, the call base and the property name + * index need to be available as with a Member reference. However, since the result must be + * in the accumulator the resulting reference is of type Accumulator. Therefore, the call + * base and the property name index are `glued` to an accumulator reference to make it work + * when deciding which call instruction to use later on. + */ + ref.hasSavedCallBaseSlot = true; + ref.savedCallBaseSlot = savedBaseSlot; + ref.savedCallPropertyNameIndex = expressionResult.propertyNameIndex; + } + setExprResult(ref); + m_optionalChainsStates.pop(); +} + bool Codegen::visit(FieldMemberExpression *ast) { if (hasError()) return false; + const bool isTailOfChain = traverseOptionalChain(ast); + TailCallBlocker blockTailCalls(this); + if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("new")) { // new.target @@ -2230,16 +2500,18 @@ bool Codegen::visit(FieldMemberExpression *ast) Reference r = referenceForName(QStringLiteral("new.target"), false); r.isReadonly = true; setExprResult(r); + return false; } - Reference r = Reference::fromStackSlot(this, CallData::NewTarget); - setExprResult(r); + auto ref = Reference::fromStackSlot(this, CallData::NewTarget); + optionalChainFinalizer(ref, isTailOfChain); return false; } } Reference base = expression(ast->base); + if (hasError()) return false; if (base.isSuper()) { @@ -2247,13 +2519,23 @@ bool Codegen::visit(FieldMemberExpression *ast) load.stringId = registerString(ast->name.toString()); bytecodeGenerator->addInstruction(load); Reference property = Reference::fromAccumulator(this).storeOnStack(); - setExprResult(Reference::fromSuperProperty(property)); + + optionalChainFinalizer(Reference::fromSuperProperty(property), isTailOfChain); return false; } - setExprResult(Reference::fromMember(base, ast->name.toString())); + + auto ref = Reference::fromMember(base, ast->name.toString(), ast->lastSourceLocation(), + ast->isOptional, &m_optionalChainsStates.top().jumpsToPatch); + + optionalChainFinalizer(ref, isTailOfChain); return false; } +void Codegen::endVisit(FieldMemberExpression *ast) +{ + m_seenOptionalChainNodes.remove(ast); +} + bool Codegen::visit(TaggedTemplate *ast) { if (hasError()) @@ -2271,9 +2553,12 @@ bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) int functionObject = -1, thisObject = -1; switch (base.type) { case Reference::Member: - case Reference::Subscript: base = base.asLValue(); break; + case Reference::Subscript: + base.element = loadSubscriptForCall(base).storeOnStack().stackSlot(); + base.subscriptLoadedForCall = true; + break; case Reference::Name: break; case Reference::SuperProperty: @@ -2296,6 +2581,7 @@ bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast) --calldata.argv; handleCall(base, calldata, functionObject, thisObject); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2336,12 +2622,32 @@ bool Codegen::visit(FunctionExpression *ast) Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, const SourceLocation &accessLocation) { Context::ResolvedName resolved = _context->resolveName(name, accessLocation); + bool throwsReferenceError = false; if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack || resolved.type == Context::ResolvedName::Import) { if (resolved.isArgOrEval && isLhs) // ### add correct source location throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode")); + + if (resolved.declarationLocation.isValid() && accessLocation.isValid() + && resolved.declarationLocation.begin() > accessLocation.end()) { + Q_ASSERT(_interface); + _interface->reportVarUsedBeforeDeclaration( + name, url().toLocalFile(), resolved.declarationLocation, accessLocation); + if (resolved.type == Context::ResolvedName::Stack && resolved.requiresTDZCheck) + throwsReferenceError = true; + } + + if (resolved.isInjected && accessLocation.isValid()) { + qCWarning(lcQmlInjectedParameter).nospace().noquote() + << url().toString() << ":" << accessLocation.startLine + << ":" << accessLocation.startColumn << " Parameter \"" << name + << "\" is not declared." + << " Injection of parameters into signal handlers is deprecated." + << " Use JavaScript functions with formal parameters instead."; + } + Reference r; switch (resolved.type) { case Context::ResolvedName::Local: @@ -2358,12 +2664,15 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co r.isReferenceToConst = resolved.isConst; r.requiresTDZCheck = resolved.requiresTDZCheck; r.name = name; // used to show correct name at run-time when TDZ check fails. + r.sourceLocation = accessLocation; + r.throwsReferenceError = throwsReferenceError; return r; } Reference r = Reference::fromName(this, name); r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal); r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal; + r.sourceLocation = accessLocation; if (!r.global && !r.qmlGlobal && m_globalNames.contains(name)) r.global = true; return r; @@ -2781,12 +3090,17 @@ bool Codegen::visit(ThisExpression *) if (hasError()) return false; - if (_context->isArrowFunction) { - Reference r = referenceForName(QStringLiteral("this"), false); - r.isReadonly = true; - setExprResult(r); - return false; + for (Context *parentContext = _context; parentContext; parentContext = parentContext->parent) { + if (parentContext->isArrowFunction) { + Reference r = referenceForName(QStringLiteral("this"), false); + r.isReadonly = true; + setExprResult(r); + return false; + } + if (parentContext->contextType != ContextType::Block) + break; } + setExprResult(Reference::fromThis(this)); return false; } @@ -2891,6 +3205,17 @@ bool Codegen::visit(YieldExpression *ast) return false; } + auto innerMostCurentFunctionContext = _context; + while (innerMostCurentFunctionContext && innerMostCurentFunctionContext->contextType != ContextType::Function) + innerMostCurentFunctionContext = innerMostCurentFunctionContext->parent; + + Q_ASSERT(innerMostCurentFunctionContext); // yield outside function would have been rejected by parser + + if (!innerMostCurentFunctionContext->isGenerator) { + throwSyntaxError(ast->firstSourceLocation(), u"Yield is only valid in generator functions"_s); + return false; + } + RegisterScope scope(this); TailCallBlocker blockTailCalls(this); Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined()); @@ -2925,15 +3250,15 @@ bool Codegen::visit(YieldExpression *ast) Instruction::IteratorNextForYieldStar next; next.object = lhsValue.stackSlot(); next.iterator = iterator.stackSlot(); - bytecodeGenerator->addInstruction(next); - - BytecodeGenerator::Jump done = bytecodeGenerator->jumpTrue(); + BytecodeGenerator::Jump done = bytecodeGenerator->addJumpInstruction(next); bytecodeGenerator->jumpNotUndefined().link(loop); + lhsValue.loadInAccumulator(); emitReturn(acc); done.link(); + bytecodeGenerator->checkException(); lhsValue.loadInAccumulator(); setExprResult(acc); @@ -2979,8 +3304,7 @@ static bool endsWithReturn(Module *module, Node *node) return false; } -int Codegen::defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, +int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body) { enterContext(ast); @@ -2991,7 +3315,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _context->name = name.isEmpty() ? currentExpr().result().name : name; _module->functions.append(_context); - _context->functionIndex = _module->functions.count() - 1; + _context->functionIndex = _module->functions.size() - 1; Context *savedFunctionContext = _functionContext; _functionContext = _context; @@ -3000,7 +3324,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, if (_context->contextType == ContextType::Global || _context->contextType == ContextType::ScriptImportedByQML) { _module->blocks.append(_context); - _context->blockIndex = _module->blocks.count() - 1; + _context->blockIndex = _module->blocks.size() - 1; } if (_module->debugMode) // allow the debugger to see overwritten arguments _context->argumentsCanEscape = true; @@ -3011,9 +3335,10 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, // at all, because if the onSignal is a signal handler, the user is actually making it explicit // that the binding is a function, so we should execute that. However, we don't know that during // AOT compilation, so mark the surrounding function as only-returning-a-closure. - _context->returnsClosure = body && body->statement && cast<ExpressionStatement *>(body->statement) && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression); + _context->returnsClosure = body && cast<ExpressionStatement *>(body->statement) + && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression); - BytecodeGenerator bytecode(_context->line, _module->debugMode); + BytecodeGenerator bytecode(_context->line, _module->debugMode, storeSourceLocations); BytecodeGenerator *savedBytecodeGenerator; savedBytecodeGenerator = bytecodeGenerator; bytecodeGenerator = &bytecode; @@ -3040,7 +3365,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, // register the lexical scope for global code if (!_context->parent && _context->requiresExecutionContext) { _module->blocks.append(_context); - _context->blockIndex = _module->blocks.count() - 1; + _context->blockIndex = _module->blocks.size() - 1; } TailCallBlocker maybeBlockTailCalls(this, _context->canHaveTailCalls()); @@ -3114,8 +3439,9 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue; - QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(), - _context->line, _context->lineNumberMapping); + qDebug().noquote() << QV4::Moth::dumpBytecode( + _context->code, _context->locals.size(), _context->arguments.size(), + _context->line, _context->lineAndStatementNumberMapping); qDebug(); } } @@ -3276,7 +3602,6 @@ bool Codegen::visit(ForEachStatement *ast) TailCallBlocker blockTailCalls(this); Reference iterator = Reference::fromStackSlot(this); - Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack(); Reference lhsValue = Reference::fromStackSlot(this); // There should be a temporal block, so that variables declared in lhs shadow outside vars. @@ -3297,25 +3622,28 @@ bool Codegen::visit(ForEachStatement *ast) BytecodeGenerator::Label in = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); + BytecodeGenerator::Label done; { - auto cleanup = [ast, iterator, iteratorDone, this]() { - if (ast->type == ForEachType::Of) { + std::function<void()> cleanup; + if (ast->type == ForEachType::Of) { + done = bytecodeGenerator->newLabel(); + cleanup = [iterator, this, done]() { iterator.loadInAccumulator(); Instruction::IteratorClose close; - close.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(close); - } - }; - ControlFlowLoop flow(this, &end, &in, cleanup); + done.link(); + }; + } else { + done = end; + } + ControlFlowLoop flow(this, &end, &in, std::move(cleanup)); bytecodeGenerator->addLoopStart(in); in.link(); iterator.loadInAccumulator(); Instruction::IteratorNext next; next.value = lhsValue.stackSlot(); - next.done = iteratorDone.stackSlot(); - bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(next).link(done); // each iteration gets it's own context, as per spec { @@ -3631,7 +3959,8 @@ void Codegen::handleTryCatch(TryStatement *ast) void Codegen::handleTryFinally(TryStatement *ast) { RegisterScope scope(this); - ControlFlowFinally finally(this, ast->finallyExpression); + const bool hasCatchBlock = ast->catchExpression; + ControlFlowFinally finally(this, ast->finallyExpression, hasCatchBlock); TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before finally is generated if (ast->catchExpression) { @@ -3785,8 +4114,7 @@ void Codegen::throwError(ErrorType errorType, const SourceLocation &loc, const Q _errorType = errorType; _error.message = detail; - _error.line = loc.startLine; - _error.column = loc.startColumn; + _error.loc = loc; } void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) @@ -3804,14 +4132,16 @@ QQmlJS::DiagnosticMessage Codegen::error() const return _error; } -QV4::CompiledData::CompilationUnit Codegen::generateCompilationUnit( +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Codegen::generateCompilationUnit( bool generateUnitData) { - return QV4::CompiledData::CompilationUnit( - generateUnitData ? jsUnitGenerator->generateUnit() : nullptr); + return QQmlRefPointer<QV4::CompiledData::CompilationUnit>( + new QV4::CompiledData::CompilationUnit( + generateUnitData ? jsUnitGenerator->generateUnit() : nullptr), + QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt); } -CompiledData::CompilationUnit Codegen::compileModule( +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Codegen::compileModule( bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics) { @@ -3826,7 +4156,7 @@ CompiledData::CompilationUnit Codegen::compileModule( *diagnostics = parser.diagnosticMessages(); if (!parsed) - return CompiledData::CompilationUnit(); + return QQmlRefPointer<CompiledData::CompilationUnit>(); QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode()); if (!moduleNode) { @@ -3847,7 +4177,7 @@ CompiledData::CompilationUnit Codegen::compileModule( if (cg.hasError()) { if (diagnostics) *diagnostics << cg.error(); - return CompiledData::CompilationUnit(); + return QQmlRefPointer<CompiledData::CompilationUnit>(); } return cg.generateCompilationUnit(); @@ -3934,14 +4264,14 @@ public: } private: - void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) { + void collectIdentifiers(QList<QStringView> &ids, AST::Node *node) { class Collector: public QQmlJS::AST::Visitor { private: - QVector<QStringView> &ids; + QList<QStringView> &ids; VolatileMemoryLocationScanner *parent; public: - Collector(QVector<QStringView> &ids, VolatileMemoryLocationScanner *parent) : + Collector(QList<QStringView> &ids, VolatileMemoryLocationScanner *parent) : QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent) {} @@ -4036,7 +4366,9 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const case Member: return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex; case Subscript: - return elementBase == other.elementBase && elementSubscript == other.elementSubscript; + return elementBase == other.elementBase && other.subscriptLoadedForCall + ? (subscriptLoadedForCall && element == other.element) + : (!subscriptLoadedForCall && elementSubscript == other.elementSubscript); case Import: return index == other.index; case Const: @@ -4069,7 +4401,7 @@ Codegen::Reference Codegen::Reference::asLValue() const case Accumulator: Q_UNREACHABLE(); case Super: - codegen->throwSyntaxError(AST::SourceLocation(), QStringLiteral("Super lvalues not implemented.")); + codegen->throwSyntaxError(SourceLocation(), QStringLiteral("Super lvalues not implemented.")); return *this; case Member: if (!propertyBase.isStackSlot()) { @@ -4153,6 +4485,28 @@ Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const return slot; } +void Codegen::Reference::tdzCheck(bool requiresCheck, bool throwsReferenceError) const { + if (throwsReferenceError) { + codegen->generateThrowException(QStringLiteral("ReferenceError"), + name + QStringLiteral(" is not defined")); + return; + } + if (!requiresCheck) + return; + Instruction::DeadTemporalZoneCheck check; + check.name = codegen->registerString(name); + codegen->bytecodeGenerator->addInstruction(check); +} + +void Codegen::Reference::tdzCheckStackSlot(Moth::StackSlot slot, bool requiresCheck, bool throwsReferenceError) const { + if (!requiresCheck) + return; + Instruction::LoadReg load; + load.reg = slot; + codegen->bytecodeGenerator->addInstruction(load); + tdzCheck(true, throwsReferenceError); +} + Codegen::Reference Codegen::Reference::storeRetainAccumulator() const { if (storeWipesAccumulator()) { @@ -4189,24 +4543,21 @@ bool Codegen::Reference::storeWipesAccumulator() const void Codegen::Reference::storeAccumulator() const { + if (throwsReferenceError) { + codegen->generateThrowException(QStringLiteral("ReferenceError"), + name + QStringLiteral(" is not defined")); + return; + } + if (isReferenceToConst) { // throw a type error - RegisterScope scope(codegen); - Reference r = codegen->referenceForName(QStringLiteral("TypeError"), false); - r = r.storeOnStack(); - Instruction::Construct construct; - construct.func = r.stackSlot(); - construct.argc = 0; - construct.argv = 0; - codegen->bytecodeGenerator->addInstruction(construct); - Instruction::ThrowException throwException; - codegen->bytecodeGenerator->addInstruction(throwException); + codegen->generateThrowException(QStringLiteral("TypeError")); return; } + switch (type) { case Super: - Q_UNREACHABLE(); - return; + Q_UNREACHABLE_RETURN(); case SuperProperty: Instruction::StoreSuperProperty store; store.property = property.stackSlot(); @@ -4244,7 +4595,7 @@ void Codegen::Reference::storeAccumulator() const } } return; case Member: - if (!disable_lookups && codegen->useFastLookups) { + if (codegen->useFastLookups) { Instruction::SetLookup store; store.base = propertyBase.stackSlot(); store.index = codegen->registerSetterLookup(propertyNameIndex); @@ -4274,30 +4625,13 @@ void Codegen::Reference::storeAccumulator() const void Codegen::Reference::loadInAccumulator() const { - auto tdzCheck = [this](bool requiresCheck){ - if (!requiresCheck) - return; - Instruction::DeadTemporalZoneCheck check; - check.name = codegen->registerString(name); - codegen->bytecodeGenerator->addInstruction(check); - }; - auto tdzCheckStackSlot = [this, tdzCheck](Moth::StackSlot slot, bool requiresCheck){ - if (!requiresCheck) - return; - Instruction::LoadReg load; - load.reg = slot; - codegen->bytecodeGenerator->addInstruction(load); - tdzCheck(true); - }; - switch (type) { case Accumulator: return; case Super: - Q_UNREACHABLE(); - return; + Q_UNREACHABLE_RETURN(); case SuperProperty: - tdzCheckStackSlot(property, subscriptRequiresTDZCheck); + tdzCheckStackSlot(property, subscriptRequiresTDZCheck, false); Instruction::LoadSuperProperty load; load.property = property.stackSlot(); codegen->bytecodeGenerator->addInstruction(load); @@ -4321,7 +4655,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str StaticValue p = StaticValue::fromReturnedValue(constant); if (p.isNumber()) { double d = p.asDouble(); - int i = static_cast<int>(d); + int i = QJSNumberCoercion::toInteger(d); if (d == i && (d != 0 || !std::signbit(d))) { if (!i) { Instruction::LoadZero load; @@ -4344,7 +4678,7 @@ QT_WARNING_POP Instruction::LoadReg load; load.reg = stackSlot(); codegen->bytecodeGenerator->addInstruction(load); - tdzCheck(requiresTDZCheck); + tdzCheck(requiresTDZCheck, throwsReferenceError); } return; case ScopedLocal: { if (!scope) { @@ -4357,7 +4691,7 @@ QT_WARNING_POP load.scope = scope; codegen->bytecodeGenerator->addInstruction(load); } - tdzCheck(requiresTDZCheck); + tdzCheck(requiresTDZCheck, throwsReferenceError); return; } case Name: @@ -4375,14 +4709,20 @@ QT_WARNING_POP return; } } - if (!disable_lookups && global) { + + if (sourceLocation.isValid()) + codegen->bytecodeGenerator->setLocation(sourceLocation); + + if (global) { if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; - load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); + load.index = codegen->registerQmlContextPropertyGetterLookup( + nameAsIndex(), JSUnitGenerator::LookupForStorage); codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadGlobalLookup load; - load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); + load.index = codegen->registerGlobalGetterLookup( + nameAsIndex(), JSUnitGenerator::LookupForStorage); codegen->bytecodeGenerator->addInstruction(load); } } else { @@ -4393,27 +4733,44 @@ QT_WARNING_POP return; case Member: propertyBase.loadInAccumulator(); - tdzCheck(requiresTDZCheck); - if (!disable_lookups && codegen->useFastLookups) { - Instruction::GetLookup load; - load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addInstruction(load); + tdzCheck(requiresTDZCheck, throwsReferenceError); + + if (sourceLocation.isValid()) + codegen->bytecodeGenerator->setLocation(sourceLocation); + + if (codegen->useFastLookups) { + if (optionalChainJumpsToPatch && isOptional) { + auto jump = codegen->bytecodeGenerator->jumpOptionalLookup( + codegen->registerGetterLookup( + propertyNameIndex, JSUnitGenerator::LookupForStorage)); + optionalChainJumpsToPatch->emplace_back(std::move(jump)); + } else { + Instruction::GetLookup load; + load.index = codegen->registerGetterLookup( + propertyNameIndex, JSUnitGenerator::LookupForStorage); + codegen->bytecodeGenerator->addInstruction(load); + } } else { - Instruction::LoadProperty load; - load.name = propertyNameIndex; - codegen->bytecodeGenerator->addInstruction(load); + if (optionalChainJumpsToPatch && isOptional) { + auto jump = codegen->bytecodeGenerator->jumpOptionalProperty(propertyNameIndex); + optionalChainJumpsToPatch->emplace_back(std::move(jump)); + } else { + Instruction::LoadProperty load; + load.name = propertyNameIndex; + codegen->bytecodeGenerator->addInstruction(load); + } } return; case Import: { Instruction::LoadImport load; load.index = index; codegen->bytecodeGenerator->addInstruction(load); - tdzCheck(requiresTDZCheck); + tdzCheck(requiresTDZCheck, throwsReferenceError); } return; case Subscript: { - tdzCheckStackSlot(elementBase, requiresTDZCheck); + tdzCheckStackSlot(elementBase, requiresTDZCheck, throwsReferenceError); elementSubscript.loadInAccumulator(); - tdzCheck(subscriptRequiresTDZCheck); + tdzCheck(subscriptRequiresTDZCheck, false); Instruction::LoadElement load; load.base = elementBase; codegen->bytecodeGenerator->addInstruction(load); @@ -4423,3 +4780,5 @@ QT_WARNING_POP } Q_UNREACHABLE(); } + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 82a4fc3289..3a27cb1487 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #ifndef QV4CODEGEN_P_H #define QV4CODEGEN_P_H @@ -60,6 +24,9 @@ #include <private/qv4bytecodegenerator_p.h> #include <private/qv4calldata_p.h> +#include <QtCore/qsharedpointer.h> +#include <stack> + QT_BEGIN_NAMESPACE namespace QV4 { @@ -78,14 +45,30 @@ struct ControlFlow; struct ControlFlowCatch; struct ControlFlowFinally; -class Q_QMLCOMPILER_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor +class Q_QML_COMPILER_EXPORT CodegenWarningInterface +{ +public: + virtual void reportVarUsedBeforeDeclaration(const QString &name, const QString &fileName, + QQmlJS::SourceLocation declarationLocation, + QQmlJS::SourceLocation accessLocation); + virtual ~CodegenWarningInterface() = default; +}; + +inline CodegenWarningInterface *defaultCodegenWarningInterface() +{ + static CodegenWarningInterface iface; + return &iface; +} + +class Q_QML_COMPILER_EXPORT Codegen: protected QQmlJS::AST::Visitor { protected: using BytecodeGenerator = QV4::Moth::BytecodeGenerator; using Instruction = QV4::Moth::Instruction; public: - Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict); - + Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict, + CodegenWarningInterface *iface = defaultCodegenWarningInterface(), + bool storeSourceLocations = false); void generateFromProgram(const QString &fileName, const QString &finalUrl, @@ -105,15 +88,15 @@ public: class VolatileMemoryLocations { friend VolatileMemoryLocationScanner; bool allVolatile = false; - QVector<QStringView> specificLocations; + QList<QStringView> specificLocations; public: - bool isVolatile(const QStringView &name) { + bool isVolatile(QStringView name) { if (allVolatile) return true; return specificLocations.contains(name); } - void add(const QStringRef &name) { if (!allVolatile) specificLocations.append(name); } + void add(QStringView name) { if (!allVolatile) specificLocations.append(name); } void setAllVolatile() { allVolatile = true; } }; class RValue { @@ -206,7 +189,11 @@ public: stackSlotIsLocalOrArgument(false), isVolatile(false), global(false), - qmlGlobal(false) + qmlGlobal(false), + throwsReferenceError(false), + subscriptLoadedForCall(false), + isOptional(false), + hasSavedCallBaseSlot(false) {} Reference(const Reference &) = default; @@ -253,14 +240,6 @@ public: r.stackSlotIsLocalOrArgument = isLocal; return r; } - static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { - Reference r(cg, StackSlot); - r.theStackSlot = Moth::StackSlot::createRegister( - index + sizeof(CallData) / sizeof(StaticValue) - 1); - r.stackSlotIsLocalOrArgument = true; - r.isVolatile = isVolatile; - return r; - } static Reference fromScopedLocal(Codegen *cg, int index, int scope) { Reference r(cg, ScopedLocal); r.index = index; @@ -277,11 +256,20 @@ public: r.name = name; return r; } - static Reference fromMember(const Reference &baseRef, const QString &name) { + static Reference + fromMember(const Reference &baseRef, const QString &name, + QQmlJS::SourceLocation sourceLocation = QQmlJS::SourceLocation(), + bool isOptional = false, + std::vector<Moth::BytecodeGenerator::Jump> *optionalChainJumpsToPatch = nullptr) + { + Q_ASSERT(baseRef.isValid()); Reference r(baseRef.codegen, Member); r.propertyBase = baseRef.asRValue(); r.propertyNameIndex = r.codegen->registerString(name); r.requiresTDZCheck = baseRef.requiresTDZCheck; + r.sourceLocation = sourceLocation; + r.optionalChainJumpsToPatch = optionalChainJumpsToPatch; + r.isOptional = isOptional; return r; } static Reference fromSuperProperty(const Reference &property) { @@ -345,6 +333,14 @@ public: return theStackSlot; } + void tdzCheck() const + { + if (isAccumulator()) + tdzCheck(requiresTDZCheck, throwsReferenceError); + else if (isStackSlot()) + tdzCheckStackSlot(stackSlot(), requiresTDZCheck, throwsReferenceError); + } + union { Moth::StackSlot theStackSlot; QV4::ReturnedValue constant; @@ -358,7 +354,10 @@ public: }; struct { Moth::StackSlot elementBase; - RValue elementSubscript; + union { + RValue elementSubscript; + Moth::StackSlot element; + }; }; Moth::StackSlot property; // super property }; @@ -374,10 +373,21 @@ public: quint32 isVolatile:1; quint32 global:1; quint32 qmlGlobal:1; + quint32 throwsReferenceError:1; + quint32 subscriptLoadedForCall:1; + quint32 isOptional: 1; + quint32 hasSavedCallBaseSlot: 1; + QQmlJS::SourceLocation sourceLocation = QQmlJS::SourceLocation(); + std::vector<Moth::BytecodeGenerator::Jump> *optionalChainJumpsToPatch = nullptr; + int savedCallBaseSlot = -1; + int savedCallPropertyNameIndex = -1; private: void storeAccumulator() const; Reference doStoreOnStack(int tempIndex) const; + void tdzCheck(bool requiresCheck, bool throwsReferenceError) const; + void tdzCheckStackSlot( + Moth::StackSlot slot, bool requiresCheck, bool throwsReferenceError) const; }; struct RegisterScope { @@ -511,11 +521,26 @@ public: int registerString(const QString &name) { return jsUnitGenerator->registerString(name); } - int registerConstant(QV4::ReturnedValue v) { return jsUnitGenerator->registerConstant(v); } - int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); } - int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); } - int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); } - int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); } + int registerConstant(QV4::ReturnedValue v) + { + return jsUnitGenerator->registerConstant(v); + } + int registerGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) + { + return jsUnitGenerator->registerGetterLookup(nameIndex, mode); + } + int registerSetterLookup(int nameIndex) + { + return jsUnitGenerator->registerSetterLookup(nameIndex); + } + int registerGlobalGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) + { + return jsUnitGenerator->registerGlobalGetterLookup(nameIndex, mode); + } + int registerQmlContextPropertyGetterLookup(int nameIndex, JSUnitGenerator::LookupMode mode) + { + return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex, mode); + } // Returns index in _module->functions virtual int defineFunction(const QString &name, QQmlJS::AST::Node *ast, @@ -575,6 +600,7 @@ protected: bool visit(QQmlJS::AST::UiArrayMemberList *ast) override; bool visit(QQmlJS::AST::UiImport *ast) override; bool visit(QQmlJS::AST::UiHeaderItemList *ast) override; + bool visit(QQmlJS::AST::UiPragmaValueList *ast) override; bool visit(QQmlJS::AST::UiPragma *ast) override; bool visit(QQmlJS::AST::UiObjectInitializer *ast) override; bool visit(QQmlJS::AST::UiObjectMemberList *ast) override; @@ -598,11 +624,14 @@ protected: bool visit(QQmlJS::AST::ArrayMemberExpression *ast) override; bool visit(QQmlJS::AST::BinaryExpression *ast) override; bool visit(QQmlJS::AST::CallExpression *ast) override; + void endVisit(QQmlJS::AST::CallExpression *ast) override; bool visit(QQmlJS::AST::ConditionalExpression *ast) override; bool visit(QQmlJS::AST::DeleteExpression *ast) override; + void endVisit(QQmlJS::AST::DeleteExpression *ast) override; bool visit(QQmlJS::AST::FalseLiteral *ast) override; bool visit(QQmlJS::AST::SuperLiteral *ast) override; bool visit(QQmlJS::AST::FieldMemberExpression *ast) override; + void endVisit(QQmlJS::AST::FieldMemberExpression *ast) override; bool visit(QQmlJS::AST::TaggedTemplate *ast) override; bool visit(QQmlJS::AST::FunctionExpression *ast) override; bool visit(QQmlJS::AST::IdentifierExpression *ast) override; @@ -661,12 +690,12 @@ protected: bool visit(QQmlJS::AST::UiSourceElement *ast) override; bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, - const QQmlJS::AST::SourceLocation &loc); - virtual void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail); - virtual void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail); + const QQmlJS::SourceLocation &loc); + virtual void throwSyntaxError(const QQmlJS::SourceLocation &loc, const QString &detail); + virtual void throwReferenceError(const QQmlJS::SourceLocation &loc, const QString &detail); void throwRecursionDepthError() override { - throwSyntaxError(QQmlJS::AST::SourceLocation(), + throwSyntaxError(QQmlJS::SourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); } @@ -682,11 +711,12 @@ public: QQmlJS::DiagnosticMessage error() const; QUrl url() const; - Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right); + Reference binopHelper(QQmlJS::AST::BinaryExpression *ast, QSOperator::Op oper, Reference &left, + Reference &right); Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right); struct Arguments { int argc; int argv; bool hasSpread; }; Arguments pushArgs(QQmlJS::AST::ArgumentList *args); - void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject); + void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject, bool optional = false); Arguments pushTemplateArgs(QQmlJS::AST::TemplateLiteral *args); bool handleTaggedTemplate(Reference base, QQmlJS::AST::TaggedTemplate *ast); @@ -700,10 +730,11 @@ public: Reference referenceForName( const QString &name, bool lhs, - const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation()); + const QQmlJS::SourceLocation &accessLocation = QQmlJS::SourceLocation()); - QV4::CompiledData::CompilationUnit generateCompilationUnit(bool generateUnitData = true); - static QV4::CompiledData::CompilationUnit compileModule( + QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit( + bool generateUnitData = true); + static QQmlRefPointer<QV4::CompiledData::CompilationUnit> compileModule( bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics); @@ -775,13 +806,24 @@ protected: bool inFormalParameterList = false; bool functionEndsWithReturn = false; bool _tailCallsAreAllowed = true; + bool storeSourceLocations = false; QSet<QString> m_globalNames; + struct OptionalChainState + { + QQmlJS::AST::Node *tailNodeOfChain = nullptr; + std::vector<Moth::BytecodeGenerator::Jump> jumpsToPatch; + bool actuallyHasOptionals = false; + }; + QSet<QQmlJS::AST::Node*> m_seenOptionalChainNodes; + std::stack<OptionalChainState> m_optionalChainsStates; + ControlFlow *controlFlow = nullptr; bool _fileNameIsUrl; ErrorType _errorType = NoError; QQmlJS::DiagnosticMessage _error; + CodegenWarningInterface *_interface; class TailCallBlocker { @@ -808,10 +850,16 @@ protected: }; private: + Q_DISABLE_COPY(Codegen) VolatileMemoryLocations scanVolatileMemoryLocations(QQmlJS::AST::Node *ast); void handleConstruct(const Reference &base, QQmlJS::AST::ArgumentList *args); - void throwError(ErrorType errorType, const QQmlJS::AST::SourceLocation &loc, + void throwError(ErrorType errorType, const QQmlJS::SourceLocation &loc, const QString &detail); + bool traverseOptionalChain(QQmlJS::AST::Node *node); + void optionalChainFinalizer(const Reference &expressionResult, bool tailOfChain, + bool isDeleteExpression = false); + Reference loadSubscriptForCall(const Reference &base); + void generateThrowException(const QString &type, const QString &text = QString()); }; } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index acc4b02e96..7a7c8f621b 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -1,41 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4compiler_p.h> #include <qv4codegen_p.h> @@ -47,14 +12,20 @@ #include <private/qml_compile_hash_p.h> #include <private/qqmlirbuilder_p.h> #include <QCryptographicHash> +#include <QtEndian> // Efficient implementation that takes advantage of powers of two. + +QT_BEGIN_NAMESPACE +namespace QtPrivate { // Disambiguate from WTF::roundUpToMultipleOf static inline size_t roundUpToMultipleOf(size_t divisor, size_t x) { Q_ASSERT(divisor && !(divisor & (divisor - 1))); const size_t remainderMask = divisor - 1; return (x + remainderMask) & ~remainderMask; } +} +QT_END_NAMESPACE QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -100,7 +71,8 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) { char *dataStart = reinterpret_cast<char *>(unit); quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable); - char *stringData = reinterpret_cast<char *>(stringTable) + roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); + char *stringData = reinterpret_cast<char *>(stringTable) + + QtPrivate::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint)); for (int i = backingUnitTableSize ; i < strings.size(); ++i) { const int index = i - backingUnitTableSize; stringTable[index] = stringData - dataStart; @@ -108,19 +80,11 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit) QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData); Q_ASSERT(reinterpret_cast<uintptr_t>(s) % alignof(QV4::CompiledData::String) == 0); - s->refcount = -1; - s->size = qstr.length(); - s->allocAndCapacityReservedFlag = 0; - s->offsetOn32Bit = sizeof(QV4::CompiledData::String); - s->offsetOn64Bit = sizeof(QV4::CompiledData::String); + Q_ASSERT(qstr.size() >= 0); + s->size = qstr.size(); ushort *uc = reinterpret_cast<ushort *>(reinterpret_cast<char *>(s) + sizeof(*s)); -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - memcpy(uc, qstr.constData(), s->size * sizeof(ushort)); -#else - for (int i = 0; i < s->size; ++i) - uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode()); -#endif + qToLittleEndian<ushort>(qstr.constData(), s->size, uc); uc[s->size] = 0; stringData += QV4::CompiledData::String::calculateSize(qstr); @@ -136,7 +100,7 @@ void QV4::Compiler::JSUnitGenerator::generateUnitChecksum(QV4::CompiledData::Uni = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(unit->md5Checksum); const char *dataPtr = reinterpret_cast<const char *>(unit) + checksummableDataOffset; - hash.addData(dataPtr, unit->unitSize - checksummableDataOffset); + hash.addData({dataPtr, qsizetype(unit->unitSize - checksummableDataOffset)}); QByteArray checksum = hash.result(); Q_ASSERT(checksum.size() == sizeof(unit->md5Checksum)); @@ -153,17 +117,22 @@ QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module) registerString(QString()); } -int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name, LookupMode mode) +{ + return registerGetterLookup(registerString(name), mode); +} + +static QV4::CompiledData::Lookup::Mode lookupMode(QV4::Compiler::JSUnitGenerator::LookupMode mode) { - return registerGetterLookup(registerString(name)); + return mode == QV4::Compiler::JSUnitGenerator::LookupForCall + ? QV4::CompiledData::Lookup::Mode_ForCall + : QV4::CompiledData::Lookup::Mode_ForStorage; } -int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex, LookupMode mode) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_Getter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_Getter, lookupMode(mode), nameIndex); return lookups.size() - 1; } @@ -174,49 +143,43 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name) int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_Setter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_Setter, + CompiledData::Lookup::Mode_ForStorage, nameIndex); return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex, LookupMode mode) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_GlobalGetter, lookupMode(mode), nameIndex); return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup( + int nameIndex, LookupMode mode) { - CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter; - l.nameIndex = nameIndex; - lookups << l; + lookups << CompiledData::Lookup( + CompiledData::Lookup::Type_QmlContextPropertyGetter, lookupMode(mode), + nameIndex); return lookups.size() - 1; } int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp) { - CompiledData::RegExp re; - re.stringIndex = registerString(regexp->pattern.toString()); - - re.flags = 0; + quint32 flags = 0; if (regexp->flags & QQmlJS::Lexer::RegExp_Global) - re.flags |= CompiledData::RegExp::RegExp_Global; + flags |= CompiledData::RegExp::RegExp_Global; if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase) - re.flags |= CompiledData::RegExp::RegExp_IgnoreCase; + flags |= CompiledData::RegExp::RegExp_IgnoreCase; if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline) - re.flags |= CompiledData::RegExp::RegExp_Multiline; + flags |= CompiledData::RegExp::RegExp_Multiline; if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode) - re.flags |= CompiledData::RegExp::RegExp_Unicode; + flags |= CompiledData::RegExp::RegExp_Unicode; if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky) - re.flags |= CompiledData::RegExp::RegExp_Sticky; + flags |= CompiledData::RegExp::RegExp_Sticky; - regexps.append(re); + regexps.append(CompiledData::RegExp(flags, registerString(regexp->pattern.toString()))); return regexps.size() - 1; } @@ -229,11 +192,15 @@ int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v) return constants.size() - 1; } -QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) +QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) const { return constants.at(idx); } +// The JSClass object and its members are stored contiguously in the jsClassData. +// In order to get to the members you have to skip over the JSClass, therefore +1. +static constexpr qsizetype jsClassMembersOffset = 1; + int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members) { // ### re-use existing class definitions. @@ -246,17 +213,36 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members) CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); jsClass->nMembers = members.size(); - CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); + CompiledData::JSClassMember *member + = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + jsClassMembersOffset); for (const auto &name : members) { - member->nameOffset = registerString(name); - member->isAccessor = false; + member->set(registerString(name), false); ++member; } return jsClassOffsets.size() - 1; } +int QV4::Compiler::JSUnitGenerator::jsClassSize(int jsClassId) const +{ + const CompiledData::JSClass *jsClass + = reinterpret_cast<const CompiledData::JSClass*>( + jsClassData.data() + jsClassOffsets[jsClassId]); + return jsClass->nMembers; +} + +QString QV4::Compiler::JSUnitGenerator::jsClassMember(int jsClassId, int member) const +{ + const CompiledData::JSClass *jsClass = reinterpret_cast<const CompiledData::JSClass*>( + jsClassData.data() + jsClassOffsets[jsClassId]); + Q_ASSERT(member >= 0); + Q_ASSERT(uint(member) < jsClass->nMembers); + const CompiledData::JSClassMember *members + = reinterpret_cast<const CompiledData::JSClassMember*>(jsClass + jsClassMembersOffset); + return stringForIndex(members[member].nameOffset()); +} + int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData::TranslationData &translation) { translations.append(translation); @@ -265,19 +251,33 @@ int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData: QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) { + const auto registerTypeStrings = [this](QQmlJS::AST::Type *type) { + if (!type) + return; + + if (type->typeArgument) { + registerString(type->typeArgument->toString()); + registerString(type->typeId->toString()); + } + registerString(type->toString()); + }; + registerString(module->fileName); registerString(module->finalUrl); - for (Context *f : qAsConst(module->functions)) { + for (Context *f : std::as_const(module->functions)) { registerString(f->name); - registerString(f->returnType); + registerTypeStrings(f->returnType); for (int i = 0; i < f->arguments.size(); ++i) { registerString(f->arguments.at(i).id); - registerString(f->arguments.at(i).typeName()); + if (const QQmlJS::AST::TypeAnnotation *annotation + = f->arguments.at(i).typeAnnotation.data()) { + registerTypeStrings(annotation->type); + } } for (int i = 0; i < f->locals.size(); ++i) registerString(f->locals.at(i)); } - for (Context *c : qAsConst(module->blocks)) { + for (Context *c : std::as_const(module->blocks)) { for (int i = 0; i < c->locals.size(); ++i) registerString(c->locals.at(i)); } @@ -348,15 +348,17 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO } CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable); - for (const CompiledData::Lookup &l : qAsConst(lookups)) + for (const CompiledData::Lookup &l : std::as_const(lookups)) *lookupsToWrite++ = l; CompiledData::RegExp *regexpTable = reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable); - memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable)); + if (regexps.size()) + memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable)); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable); - memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); + if (constants.size()) + memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue)); #else quint64_le *constantTable = reinterpret_cast<quint64_le *>(dataPtr + unit->offsetToConstantTable); for (int i = 0; i < constants.count(); ++i) @@ -364,16 +366,18 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO #endif { - memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size()); + if (jsClassData.size()) + memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size()); // write js classes and js class lookup table quint32_le *jsClassOffsetTable = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToJSClassTable); - for (int i = 0; i < jsClassOffsets.count(); ++i) + for (int i = 0; i < jsClassOffsets.size(); ++i) jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i); } - - memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.count() * sizeof(CompiledData::TranslationData)); + if (translations.size()) { + memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.size() * sizeof(CompiledData::TranslationData)); + } { const auto populateExportEntryTable = [this, dataPtr](const QVector<Compiler::ExportEntry> &table, quint32_le offset) { @@ -424,7 +428,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte { QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f; - quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*function))); + quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, sizeof(*function))); function->nameIndex = getStringId(irFunction->name); function->flags = 0; @@ -434,15 +438,29 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->flags |= CompiledData::Function::IsArrowFunction; if (irFunction->isGenerator) function->flags |= CompiledData::Function::IsGenerator; - function->nestedFunctionIndex = - irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first())) - : std::numeric_limits<uint32_t>::max(); + if (irFunction->returnsClosure) + function->flags |= CompiledData::Function::IsClosureWrapper; + + if (!irFunction->returnsClosure + || irFunction->innerFunctionAccessesThis + || irFunction->innerFunctionAccessesNewTarget) { + // If the inner function does things with this and new.target we need to do some work in + // the outer function. Then we shouldn't directly access the nested function. + function->nestedFunctionIndex = std::numeric_limits<uint32_t>::max(); + } else { + // Otherwise we can directly use the nested function. + function->nestedFunctionIndex + = quint32(module->functions.indexOf(irFunction->nestedContexts.first())); + } + function->length = irFunction->formals ? irFunction->formals->length() : 0; function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(CompiledData::Parameter); - QmlIR::Parameter::initType(&function->returnType, this, getStringId(irFunction->returnType)); + const auto idGenerator = [this](const QString &str) { return getStringId(str); }; + + QmlIR::Parameter::initType(&function->returnType, idGenerator, irFunction->returnType); function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone; function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone; @@ -452,9 +470,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->localsOffset = currentOffset; currentOffset += function->nLocals * sizeof(quint32); - function->nLineNumbers = irFunction->lineNumberMapping.size(); - Q_ASSERT(function->lineNumberOffset() == currentOffset); - currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); + function->nLineAndStatementNumbers + = irFunction->lineAndStatementNumberMapping.size(); + Q_ASSERT(function->lineAndStatementNumberOffset() == currentOffset); + currentOffset += function->nLineAndStatementNumbers + * sizeof(CompiledData::CodeOffsetToLineAndStatement); function->nRegisters = irFunction->registerCountInFunction; @@ -464,8 +484,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte currentOffset += function->nLabelInfos * sizeof(quint32); } - function->location.line = irFunction->line; - function->location.column = irFunction->column; + function->location.set(irFunction->line, irFunction->column); function->codeOffset = currentOffset; function->codeSize = irFunction->code.size(); @@ -473,8 +492,10 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte // write formals CompiledData::Parameter *formals = (CompiledData::Parameter *)(f + function->formalsOffset); for (int i = 0; i < irFunction->arguments.size(); ++i) { - QmlIR::Parameter::init(&formals[i], this, getStringId(irFunction->arguments.at(i).id), - getStringId(irFunction->arguments.at(i).typeName())); + auto *formal = &formals[i]; + formal->nameIndex = getStringId(irFunction->arguments.at(i).id); + if (QQmlJS::AST::TypeAnnotation *annotation = irFunction->arguments.at(i).typeAnnotation.data()) + QmlIR::Parameter::initType(&formal->type, idGenerator, annotation->type); } // write locals @@ -482,8 +503,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte for (int i = 0; i < irFunction->locals.size(); ++i) locals[i] = getStringId(irFunction->locals.at(i)); - // write line numbers - memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); + // write line and statement numbers + memcpy(f + function->lineAndStatementNumberOffset(), + irFunction->lineAndStatementNumberMapping.constData(), + irFunction->lineAndStatementNumberMapping.size() + * sizeof(CompiledData::CodeOffsetToLineAndStatement)); quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset()); for (unsigned u : irFunction->labelInfo) { @@ -524,25 +548,26 @@ void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Cl static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { - qDebug() << "=== Class " << stringForIndex(cls->nameIndex) << "static methods" << cls->nStaticMethods << "methods" << cls->nMethods; + qDebug() << "=== Class" << stringForIndex(cls->nameIndex) << "static methods" + << cls->nStaticMethods << "methods" << cls->nMethods; qDebug() << " constructor:" << cls->constructorFunction; - const char *staticString = ": static "; for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) { - if (i == cls->nStaticMethods) - staticString = ": "; - const char *type; + QDebug output = qDebug().nospace(); + output << " " << i << ": "; + if (i < cls->nStaticMethods) + output << "static "; switch (cls->methodTable()[i].type) { case CompiledData::Method::Getter: - type = "get "; break; + output << "get "; break; case CompiledData::Method::Setter: - type = "set "; break; + output << "set "; break; default: - type = ""; - + break; } - qDebug() << " " << i << staticString << type << stringForIndex(cls->methodTable()[i].name) << cls->methodTable()[i].function; + output << stringForIndex(cls->methodTable()[i].name) << " " + << cls->methodTable()[i].function; } - qDebug(); + qDebug().space(); } } @@ -578,7 +603,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context { QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); - quint32 currentOffset = static_cast<quint32>(roundUpToMultipleOf(8, sizeof(*block))); + quint32 currentOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, sizeof(*block))); block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone; block->nLocals = irBlock->locals.size(); @@ -630,7 +655,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToBlockTable = nextOffset; nextOffset += unit.blockTableSize * sizeof(uint); - unit.lookupTableSize = lookups.count(); + unit.lookupTableSize = lookups.size(); unit.offsetToLookupTable = nextOffset; nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup); @@ -641,53 +666,58 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.constantTableSize = constants.size(); // Ensure we load constants from well-aligned addresses into for example SSE registers. - nextOffset = static_cast<quint32>(roundUpToMultipleOf(16, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(16, nextOffset)); unit.offsetToConstantTable = nextOffset; nextOffset += unit.constantTableSize * sizeof(ReturnedValue); - unit.jsClassTableSize = jsClassOffsets.count(); + unit.jsClassTableSize = jsClassOffsets.size(); unit.offsetToJSClassTable = nextOffset; nextOffset += unit.jsClassTableSize * sizeof(uint); *jsClassDataOffset = nextOffset; nextOffset += jsClassData.size(); - nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset)); - unit.translationTableSize = translations.count(); + unit.translationTableSize = translations.size(); unit.offsetToTranslationTable = nextOffset; nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData); + if (unit.translationTableSize != 0) { + constexpr auto spaceForTranslationContextId = sizeof(quint32_le); + nextOffset += spaceForTranslationContextId; + } - nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset)); const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) { *tableSizePtr = count; *offsetPtr = nextOffset; nextOffset += count * sizeof(CompiledData::ExportEntry); - nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset)); }; - reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable); - reserveExportTable(module->indirectExportEntries.count(), &unit.indirectExportEntryTableSize, &unit.offsetToIndirectExportEntryTable); - reserveExportTable(module->starExportEntries.count(), &unit.starExportEntryTableSize, &unit.offsetToStarExportEntryTable); + reserveExportTable(module->localExportEntries.size(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable); + reserveExportTable(module->indirectExportEntries.size(), &unit.indirectExportEntryTableSize, &unit.offsetToIndirectExportEntryTable); + reserveExportTable(module->starExportEntries.size(), &unit.starExportEntryTableSize, &unit.offsetToStarExportEntryTable); - unit.importEntryTableSize = module->importEntries.count(); + unit.importEntryTableSize = module->importEntries.size(); unit.offsetToImportEntryTable = nextOffset; nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry); - nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset)); - unit.moduleRequestTableSize = module->moduleRequests.count(); + unit.moduleRequestTableSize = module->moduleRequests.size(); unit.offsetToModuleRequestTable = nextOffset; nextOffset += unit.moduleRequestTableSize * sizeof(uint); - nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset)); quint32 functionSize = 0; for (int i = 0; i < module->functions.size(); ++i) { Context *f = module->functions.at(i); blockAndFunctionOffsets[i] = nextOffset; - quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), - int(f->labelInfo.size()), f->code.size()); + quint32 size = QV4::CompiledData::Function::calculateSize( + f->arguments.size(), f->locals.size(), f->lineAndStatementNumberMapping.size(), + f->nestedContexts.size(), int(f->labelInfo.size()), f->code.size()); functionSize += size - f->code.size(); nextOffset += size; } @@ -719,7 +749,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp if (option == GenerateWithStringTable) { unit.stringTableSize = stringTable.stringCount(); - nextOffset = static_cast<quint32>(roundUpToMultipleOf(8, nextOffset)); + nextOffset = static_cast<quint32>(QtPrivate::roundUpToMultipleOf(8, nextOffset)); unit.offsetToStringTable = nextOffset; nextOffset += stringTable.sizeOfTableAndData(); } else { @@ -738,7 +768,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp if (showStats) { qDebug() << "Generated JS unit that is" << unit.unitSize << "bytes contains:"; qDebug() << " " << functionSize << "bytes for non-code function data for" << unit.functionTableSize << "functions"; - qDebug() << " " << translations.count() * sizeof(CompiledData::TranslationData) << "bytes for" << translations.count() << "translations"; + qDebug() << " " << translations.size() * sizeof(CompiledData::TranslationData) << "bytes for" << translations.size() << "translations"; } return unit; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 4f3c718175..bf2f5c8167 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #ifndef QV4COMPILER_P_H #define QV4COMPILER_P_H @@ -78,7 +42,7 @@ struct Module; struct Class; struct TemplateObject; -struct Q_QMLCOMPILER_PRIVATE_EXPORT StringTableGenerator { +struct Q_QML_COMPILER_EXPORT StringTableGenerator { StringTableGenerator(); int registerString(const QString &str); @@ -105,7 +69,9 @@ private: bool frozen = false; }; -struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { +struct Q_QML_COMPILER_EXPORT JSUnitGenerator { + enum LookupMode { LookupForStorage, LookupForCall }; + static void generateUnitChecksum(CompiledData::Unit *unit); struct MemberInfo { @@ -119,19 +85,23 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { int getStringId(const QString &string) const { return stringTable.getStringId(string); } QString stringForIndex(int index) const { return stringTable.stringForIndex(index); } - int registerGetterLookup(const QString &name); - int registerGetterLookup(int nameIndex); + int registerGetterLookup(const QString &name, LookupMode mode); + int registerGetterLookup(int nameIndex, LookupMode mode); int registerSetterLookup(const QString &name); int registerSetterLookup(int nameIndex); - int registerGlobalGetterLookup(int nameIndex); - int registerQmlContextPropertyGetterLookup(int nameIndex); + int registerGlobalGetterLookup(int nameIndex, LookupMode mode); + int registerQmlContextPropertyGetterLookup(int nameIndex, LookupMode mode); + int lookupNameIndex(int index) const { return lookups[index].nameIndex(); } + QString lookupName(int index) const { return stringForIndex(lookupNameIndex(index)); } int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp); int registerConstant(ReturnedValue v); - ReturnedValue constant(int idx); + ReturnedValue constant(int idx) const; int registerJSClass(const QStringList &members); + int jsClassSize(int jsClassId) const; + QString jsClassMember(int jsClassId, int member) const; int registerTranslation(const CompiledData::TranslationData &translation); @@ -148,6 +118,7 @@ struct Q_QMLCOMPILER_PRIVATE_EXPORT JSUnitGenerator { StringTableGenerator stringTable; QString codeGeneratorName; + private: CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset); diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 88837b0feb..499c804b79 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -1,50 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 "qv4codegen_p.h" #include "qv4compilercontext_p.h" -#include "qv4compilercontrolflow_p.h" #include "qv4bytecodegenerator_p.h" +#include <QtQml/private/qv4calldata_p.h> QT_USE_NAMESPACE using namespace QV4; using namespace QV4::Compiler; using namespace QQmlJS::AST; +using namespace QQmlJS; QT_BEGIN_NAMESPACE @@ -79,14 +45,16 @@ bool Context::Member::requiresTDZCheck(const SourceLocation &accessLocation, boo if (accessAcrossContextBoundaries) return true; - if (!accessLocation.isValid() || !endOfInitializerLocation.isValid()) + if (!accessLocation.isValid() || !declarationLocation.isValid()) return true; - return accessLocation.begin() < endOfInitializerLocation.end(); + return accessLocation.begin() < declarationLocation.end(); } -bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function, - const QQmlJS::AST::SourceLocation &endOfInitializer) +bool Context::addLocalVar( + const QString &name, Context::MemberType type, VariableScope scope, + FunctionExpression *function, const QQmlJS::SourceLocation &declarationLocation, + bool isInjected) { // ### can this happen? if (name.isEmpty()) @@ -111,18 +79,19 @@ bool Context::addLocalVar(const QString &name, Context::MemberType type, Variabl // hoist var declarations to the function level if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition)) - return parent->addLocalVar(name, type, scope, function, endOfInitializer); + return parent->addLocalVar(name, type, scope, function, declarationLocation); Member m; m.type = type; m.function = function; m.scope = scope; - m.endOfInitializerLocation = endOfInitializer; + m.declarationLocation = declarationLocation; + m.isInjected = isInjected; members.insert(name, m); return true; } -Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation) +Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::SourceLocation &accessLocation) { int scope = 0; Context *c = this; @@ -142,12 +111,14 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS result.scope = scope; result.index = m.index; result.isConst = (m.scope == VariableScope::Const); - result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this); + result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this) || c->isCaseBlock(); if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval"))) result.isArgOrEval = true; + result.declarationLocation = m.declarationLocation; + result.isInjected = m.isInjected; return result; } - const int argIdx = c->findArgument(name); + const int argIdx = c->findArgument(name, &result.isInjected); if (argIdx != -1) { if (c->argumentsCanEscape) { result.index = argIdx + c->locals.size(); @@ -173,8 +144,11 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS c = c->parent; } - if (c && c->contextType == ContextType::ESModule) { - for (int i = 0; i < c->importEntries.count(); ++i) { + if (!c) + return result; + + if (c->contextType == ContextType::ESModule) { + for (int i = 0; i < c->importEntries.size(); ++i) { if (c->importEntries.at(i).localName == name) { result.index = i; result.type = ResolvedName::Import; @@ -207,7 +181,7 @@ void Context::emitBlockHeader(Codegen *codegen) if (requiresExecutionContext) { if (blockIndex < 0) { codegen->module()->blocks.append(this); - blockIndex = codegen->module()->blocks.count() - 1; + blockIndex = codegen->module()->blocks.size() - 1; } if (contextType == ContextType::Global) { @@ -299,7 +273,7 @@ void Context::emitBlockHeader(Codegen *codegen) codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator(); } - for (const Context::Member &member : qAsConst(members)) { + for (const Context::Member &member : std::as_const(members)) { if (member.function) { const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body); codegen->loadClosure(function); @@ -387,8 +361,8 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) break; } - sizeOfLocalTemporalDeadZone = localsInTDZ.count(); - for (auto &member: qAsConst(localsInTDZ)) { + sizeOfLocalTemporalDeadZone = localsInTDZ.size(); + for (auto &member: std::as_const(localsInTDZ)) { member->index = locals.size(); locals.append(member.key()); } @@ -402,9 +376,9 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) } } - sizeOfRegisterTemporalDeadZone = registersInTDZ.count(); + sizeOfRegisterTemporalDeadZone = registersInTDZ.size(); firstTemporalDeadZoneRegister = bytecodeGenerator->currentRegister(); - for (auto &member: qAsConst(registersInTDZ)) + for (auto &member: std::as_const(registersInTDZ)) member->index = bytecodeGenerator->newRegister(); nRegisters = bytecodeGenerator->currentRegister() - registerOffset; diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 8c124ac409..ceb5d00a0b 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 #ifndef QV4COMPILERCONTEXT_P_H #define QV4COMPILERCONTEXT_P_H @@ -56,6 +20,11 @@ #include <QtCore/QDateTime> #include <QtCore/QStack> #include <QtCore/QHash> +#include <QtCore/QMap> +#include <QtCore/QSet> +#include <QtCore/QVarLengthArray> + +#include <memory> QT_BEGIN_NAMESPACE @@ -177,16 +146,27 @@ struct Context { FunctionDefinition }; + struct SourceLocationTable + { + struct Entry + { + quint32 offset; + QQmlJS::SourceLocation location; + }; + QVector<Entry> entries; + }; + struct Member { MemberType type = UndefinedMember; int index = -1; QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var; mutable bool canEscape = false; + bool isInjected = false; QQmlJS::AST::FunctionExpression *function = nullptr; - QQmlJS::AST::SourceLocation endOfInitializerLocation; + QQmlJS::SourceLocation declarationLocation; bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; } - bool requiresTDZCheck(const QQmlJS::AST::SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const; + bool requiresTDZCheck(const QQmlJS::SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const; }; typedef QMap<QString, Member> MemberMap; @@ -194,7 +174,7 @@ struct Context { QSet<QString> usedVariables; QQmlJS::AST::FormalParameterList *formals = nullptr; QQmlJS::AST::BoundNames arguments; - QString returnType; + QQmlJS::AST::Type *returnType = nullptr; QStringList locals; QStringList moduleRequests; QVector<ImportEntry> importEntries; @@ -204,7 +184,8 @@ struct Context { ControlFlow *controlFlow = nullptr; QByteArray code; - QVector<CompiledData::CodeOffsetToLine> lineNumberMapping; + QVector<CompiledData::CodeOffsetToLineAndStatement> lineAndStatementNumberMapping; + std::unique_ptr<SourceLocationTable> sourceLocationTable; std::vector<unsigned> labelInfo; int nRegisters = 0; @@ -227,7 +208,7 @@ struct Context { bool isWithBlock = false; bool isCatchBlock = false; QString caughtVariable; - QQmlJS::AST::SourceLocation lastBlockInitializerLocation; + QQmlJS::SourceLocation lastBlockInitializerLocation; enum UsesArgumentsObject { ArgumentsObjectUnknown, @@ -289,12 +270,20 @@ struct Context { isStrict = true; } - int findArgument(const QString &name) const + bool hasArgument(const QString &name) const + { + return arguments.contains(name); + } + + int findArgument(const QString &name, bool *isInjected) const { // search backwards to handle duplicate argument names correctly for (int i = arguments.size() - 1; i >= 0; --i) { - if (arguments.at(i).id == name) + const auto &arg = arguments.at(i); + if (arg.id == name) { + *isInjected = arg.isInjected(); return i; + } } return -1; } @@ -330,8 +319,11 @@ struct Context { usedVariables.insert(name); } - bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr, - const QQmlJS::AST::SourceLocation &endOfInitializer = QQmlJS::AST::SourceLocation()); + bool addLocalVar( + const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, + QQmlJS::AST::FunctionExpression *function = nullptr, + const QQmlJS::SourceLocation &declarationLocation = QQmlJS::SourceLocation(), + bool isInjected = false); struct ResolvedName { enum Type { @@ -346,12 +338,13 @@ struct Context { bool isArgOrEval = false; bool isConst = false; bool requiresTDZCheck = false; + bool isInjected = false; int scope = -1; int index = -1; - QQmlJS::AST::SourceLocation endOfDeclarationLocation; + QQmlJS::SourceLocation declarationLocation; bool isValid() const { return type != Unresolved; } }; - ResolvedName resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation); + ResolvedName resolveName(const QString &name, const QQmlJS::SourceLocation &accessLocation); void emitBlockHeader(Compiler::Codegen *codegen); void emitBlockFooter(Compiler::Codegen *codegen); @@ -367,6 +360,11 @@ struct Context { return parent->canHaveTailCalls(); return false; } + + bool isCaseBlock() const + { + return contextType == ContextType::Block && name == u"%CaseBlock"; + } }; diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h index 5623473726..b190b77410 100644 --- a/src/qml/compiler/qv4compilercontrolflow_p.h +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #ifndef QV4COMPILERCONTROLFLOW_P_H #define QV4COMPILERCONTROLFLOW_P_H @@ -203,8 +167,8 @@ struct ControlFlowUnwindCleanup : public ControlFlowUnwind ~ControlFlowUnwindCleanup() { if (cleanup) { unwindLabel.link(); - generator()->setUnwindHandler(parentUnwindHandler()); cleanup(); + generator()->setUnwindHandler(parentUnwindHandler()); emitUnwindHandler(); } } @@ -374,12 +338,17 @@ struct ControlFlowFinally : public ControlFlowUnwind QQmlJS::AST::Finally *finally; bool insideFinally = false; - ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally) + ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally, bool hasCatchBlock) : ControlFlowUnwind(cg, Finally), finally(finally) { Q_ASSERT(finally != nullptr); setupUnwindHandler(); - generator()->setUnwindHandler(&unwindLabel); + + // No need to set the handler for the finally now if there is a catch block. + // In that case, a handler for the latter will be set immediately after this. + if (!hasCatchBlock) { + generator()->setUnwindHandler(&unwindLabel); + } } virtual bool requiresUnwind() override { diff --git a/src/qml/compiler/qv4compilerglobal_p.h b/src/qml/compiler/qv4compilerglobal_p.h index 3478074827..c997e33093 100644 --- a/src/qml/compiler/qv4compilerglobal_p.h +++ b/src/qml/compiler/qv4compilerglobal_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4COMPILERGLOBAL_H #define QV4COMPILERGLOBAL_H diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index ab0ebf3d4b..f667548878 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "qv4compilerscanfunctions_p.h" @@ -55,12 +19,9 @@ using namespace QV4::Compiler; using namespace QQmlJS; using namespace QQmlJS::AST; -static CompiledData::Location location(const QQmlJS::AST::SourceLocation &astLocation) +static CompiledData::Location location(const QQmlJS::SourceLocation &astLocation) { - CompiledData::Location target; - target.line = astLocation.startLine; - target.column = astLocation.startColumn; - return target; + return CompiledData::Location(astLocation.startLine, astLocation.startColumn); } @@ -107,6 +68,7 @@ void ScanFunctions::leaveEnvironment() void ScanFunctions::checkDirectivePrologue(StatementList *ast) { + Q_ASSERT(_context); for (StatementList *it = ast; it; it = it->next) { if (ExpressionStatement *expr = cast<ExpressionStatement *>(it->statement)) { if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { @@ -115,7 +77,7 @@ void ScanFunctions::checkDirectivePrologue(StatementList *ast) // allowed. if (strLit->literalToken.length < 2) continue; - QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + QStringView str = QStringView{_sourceCode}.mid(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); if (str == QLatin1String("use strict")) { _context->isStrict = true; } else { @@ -129,8 +91,9 @@ void ScanFunctions::checkDirectivePrologue(StatementList *ast) } } -void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) +void ScanFunctions::checkName(QStringView name, const QQmlJS::SourceLocation &loc) { + Q_ASSERT(_context); if (_context->isStrict) { if (name == QLatin1String("implements") || name == QLatin1String("interface") @@ -161,6 +124,7 @@ void ScanFunctions::endVisit(Program *) bool ScanFunctions::visit(ESModule *ast) { enterEnvironment(ast, defaultProgramType, QStringLiteral("%ModuleCode")); + Q_ASSERT(_context); _context->isStrict = true; return true; } @@ -172,6 +136,7 @@ void ScanFunctions::endVisit(ESModule *) bool ScanFunctions::visit(ExportDeclaration *declaration) { + Q_ASSERT(_context); QString module; if (declaration->fromClause) { module = declaration->fromClause->moduleSpecifier.toString(); @@ -181,7 +146,9 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) QString localNameForDefaultExport = QStringLiteral("*default*"); - if (declaration->exportAll) { + if (declaration->exportsAll()) { + Q_ASSERT_X(declaration->fromClause, "ScanFunctions", + "ExportDeclaration with exportAll always have a fromClause"); Compiler::ExportEntry entry; entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString(); entry.importName = QStringLiteral("*"); @@ -262,6 +229,7 @@ bool ScanFunctions::visit(ExportDeclaration *declaration) bool ScanFunctions::visit(ImportDeclaration *declaration) { + Q_ASSERT(_context); QString module; if (declaration->fromClause) { module = declaration->fromClause->moduleSpecifier.toString(); @@ -310,6 +278,7 @@ bool ScanFunctions::visit(ImportDeclaration *declaration) bool ScanFunctions::visit(CallExpression *ast) { + Q_ASSERT(_context); if (!_context->hasDirectEval) { if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("eval")) { @@ -324,20 +293,25 @@ bool ScanFunctions::visit(CallExpression *ast) bool ScanFunctions::visit(PatternElement *ast) { + Q_ASSERT(_context); if (!ast->isVariableDeclaration()) return true; BoundNames names; ast->boundNames(&names); - QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation(); - if (_context->lastBlockInitializerLocation.isValid()) - lastInitializerLocation = _context->lastBlockInitializerLocation; + QQmlJS::SourceLocation declarationLocation = ast->firstSourceLocation(); + if (_context->lastBlockInitializerLocation.isValid()) { + declarationLocation.length = _context->lastBlockInitializerLocation.end() + - declarationLocation.offset; + } else { + declarationLocation.length = ast->lastSourceLocation().end() - declarationLocation.offset; + } - for (const auto &name : qAsConst(names)) { + for (const auto &name : std::as_const(names)) { if (_context->isStrict && (name.id == QLatin1String("eval") || name.id == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); - checkName(QStringRef(&name.id), ast->identifierToken); + checkName(QStringView(name.id), ast->identifierToken); if (name.id == QLatin1String("arguments")) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) { @@ -345,7 +319,7 @@ bool ScanFunctions::visit(PatternElement *ast) return false; } if (!_context->addLocalVar(name.id, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope, - /*function*/nullptr, lastInitializerLocation)) { + /*function*/nullptr, declarationLocation)) { _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name.id)); return false; } @@ -355,6 +329,7 @@ bool ScanFunctions::visit(PatternElement *ast) bool ScanFunctions::visit(IdentifierExpression *ast) { + Q_ASSERT(_context); checkName(ast->name, ast->identifierToken); if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) _context->usesArgumentsObject = Context::ArgumentsObjectUsed; @@ -368,15 +343,18 @@ bool ScanFunctions::visit(ExpressionStatement *ast) if (!_allowFuncDecls) _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); - if (!enterFunction(expr, /*enterName*/ true)) + if (!enterFunction(expr, expr->identifierToken.length > 0 + ? FunctionNameContext::Inner + : FunctionNameContext::None)) { return false; + } Node::accept(expr->formals, this); Node::accept(expr->body, this); leaveEnvironment(); return false; } else { SourceLocation firstToken = ast->firstSourceLocation(); - if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { + if (QStringView{_sourceCode}.mid(firstToken.offset, firstToken.length) == QLatin1String("function")) { _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); } } @@ -385,12 +363,15 @@ bool ScanFunctions::visit(ExpressionStatement *ast) bool ScanFunctions::visit(FunctionExpression *ast) { - return enterFunction(ast, /*enterName*/ false); + return enterFunction(ast, ast->identifierToken.length > 0 + ? FunctionNameContext::Inner + : FunctionNameContext::None); } bool ScanFunctions::visit(ClassExpression *ast) { enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class")); + Q_ASSERT(_context); _context->isStrict = true; _context->hasNestedFunctions = true; if (!ast->name.isEmpty()) @@ -405,6 +386,7 @@ void ScanFunctions::endVisit(ClassExpression *) bool ScanFunctions::visit(ClassDeclaration *ast) { + Q_ASSERT(_context); if (!ast->name.isEmpty()) _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let); @@ -434,9 +416,10 @@ bool ScanFunctions::visit(TemplateLiteral *ast) bool ScanFunctions::visit(SuperLiteral *) { + Q_ASSERT(_context); Context *c = _context; bool needContext = false; - while (c && (c->contextType == ContextType::Block || c->isArrowFunction)) { + while (c->contextType == ContextType::Block || c->isArrowFunction) { needContext |= c->isArrowFunction; c = c->parent; } @@ -455,6 +438,7 @@ bool ScanFunctions::visit(FieldMemberExpression *ast) _cg->throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'.")); return false; } + Q_ASSERT(_context); Context *c = _context; bool needContext = false; while (c->contextType == ContextType::Block || c->isArrowFunction) { @@ -479,11 +463,12 @@ bool ScanFunctions::visit(ArrayPattern *ast) return false; } -bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName) +bool ScanFunctions::enterFunction(FunctionExpression *ast, FunctionNameContext nameContext) { + Q_ASSERT(_context); if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); - return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName); + return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, nameContext); } void ScanFunctions::endVisit(FunctionExpression *) @@ -518,7 +503,7 @@ void ScanFunctions::endVisit(PatternProperty *) bool ScanFunctions::visit(FunctionDeclaration *ast) { - return enterFunction(ast, /*enterName*/ true); + return enterFunction(ast, FunctionNameContext::Outer); } void ScanFunctions::endVisit(FunctionDeclaration *) @@ -553,10 +538,13 @@ void ScanFunctions::endVisit(ForStatement *) leaveEnvironment(); } -bool ScanFunctions::visit(ForEachStatement *ast) { +bool ScanFunctions::visit(ForEachStatement *ast) +{ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach")); - if (ast->expression) + if (ast->expression) { + Q_ASSERT(_context); _context->lastBlockInitializerLocation = ast->expression->lastSourceLocation(); + } Node::accept(ast->lhs, this); Node::accept(ast->expression, this); @@ -573,6 +561,7 @@ void ScanFunctions::endVisit(ForEachStatement *) bool ScanFunctions::visit(ThisExpression *) { + Q_ASSERT(_context); _context->usesThis = true; return false; } @@ -603,6 +592,7 @@ void ScanFunctions::endVisit(CaseBlock *) bool ScanFunctions::visit(Catch *ast) { + Q_ASSERT(_context); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); enterEnvironment(ast, ContextType::Block, QStringLiteral("%CatchBlock")); _context->isCatchBlock = true; @@ -630,6 +620,7 @@ void ScanFunctions::endVisit(Catch *) bool ScanFunctions::visit(WithStatement *ast) { + Q_ASSERT(_context); Node::accept(ast->expression, this); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls); @@ -650,7 +641,9 @@ void ScanFunctions::endVisit(WithStatement *) leaveEnvironment(); } -bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName) +bool ScanFunctions::enterFunction( + Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, + FunctionNameContext nameContext) { Context *outerContext = _context; enterEnvironment(ast, ContextType::Function, name); @@ -661,7 +654,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete if (outerContext) { outerContext->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. - if (enterName) { + if (nameContext == FunctionNameContext::Outer) { if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) { _cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name)); return false; @@ -672,6 +665,7 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed; } + Q_ASSERT(_context); _context->name = name; if (formals && formals->containsName(QStringLiteral("arguments"))) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; @@ -682,12 +676,14 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete _context->isGenerator = true; if (expr->typeAnnotation) - _context->returnType = expr->typeAnnotation->type->toString(); + _context->returnType = expr->typeAnnotation->type; } - if (!enterName && (!name.isEmpty() && (!formals || !formals->containsName(name)))) + if (nameContext == FunctionNameContext::Inner + && (!name.isEmpty() && (!formals || !formals->containsName(name)))) { _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var); + } _context->formals = formals; if (body && !_context->isStrict) @@ -699,22 +695,24 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete const BoundNames boundNames = formals ? formals->boundNames() : BoundNames(); for (int i = 0; i < boundNames.size(); ++i) { - const QString &arg = boundNames.at(i).id; + const auto &arg = boundNames.at(i); if (_context->isStrict || !isSimpleParameterList) { - bool duplicate = (boundNames.indexOf(arg, i + 1) != -1); + bool duplicate = (boundNames.indexOf(arg.id, i + 1) != -1); if (duplicate) { - _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg)); + _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg.id)); return false; } } if (_context->isStrict) { - if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { - _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); + if (arg.id == QLatin1String("eval") || arg.id == QLatin1String("arguments")) { + _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg.id)); return false; } } - if (!_context->arguments.contains(arg)) - _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var); + if (!_context->arguments.contains(arg.id)) { + _context->addLocalVar(arg.id, Context::VariableDefinition, VariableScope::Var, nullptr, + QQmlJS::SourceLocation(), arg.isInjected()); + } } return true; @@ -724,7 +722,7 @@ void ScanFunctions::calcEscapingVariables() { Module *m = _cg->_module; - for (Context *inner : qAsConst(m->contextMap)) { + for (Context *inner : std::as_const(m->contextMap)) { if (inner->usesArgumentsObject != Context::ArgumentsObjectUsed) continue; if (inner->contextType != ContextType::Block && !inner->isArrowFunction) @@ -736,7 +734,7 @@ void ScanFunctions::calcEscapingVariables() c->usesArgumentsObject = Context::ArgumentsObjectUsed; inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed; } - for (Context *inner : qAsConst(m->contextMap)) { + for (Context *inner : std::as_const(m->contextMap)) { if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown) inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed; if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) { @@ -749,7 +747,7 @@ void ScanFunctions::calcEscapingVariables() } } - for (Context *c : qAsConst(m->contextMap)) { + for (Context *c : std::as_const(m->contextMap)) { if (c->contextType != ContextType::ESModule) continue; for (const auto &entry: c->exportEntries) { @@ -760,8 +758,8 @@ void ScanFunctions::calcEscapingVariables() break; } - for (Context *inner : qAsConst(m->contextMap)) { - for (const QString &var : qAsConst(inner->usedVariables)) { + for (Context *inner : std::as_const(m->contextMap)) { + for (const QString &var : std::as_const(inner->usedVariables)) { Context *c = inner; while (c) { Context *current = c; @@ -783,7 +781,7 @@ void ScanFunctions::calcEscapingVariables() } break; } - if (c->findArgument(var) != -1) { + if (c->hasArgument(var)) { c->argumentsCanEscape = true; c->requiresExecutionContext = true; break; @@ -823,7 +821,7 @@ void ScanFunctions::calcEscapingVariables() c->innerFunctionAccessesThis |= innerFunctionAccessesThis; } } - for (Context *c : qAsConst(m->contextMap)) { + for (Context *c : std::as_const(m->contextMap)) { if (c->innerFunctionAccessesThis) { // add an escaping 'this' variable c->addLocalVar(QStringLiteral("this"), Context::VariableDefinition, VariableScope::Let); @@ -849,7 +847,7 @@ void ScanFunctions::calcEscapingVariables() c->requiresExecutionContext = true; c->argumentsCanEscape = true; } else { - for (const auto &m : qAsConst(c->members)) { + for (const auto &m : std::as_const(c->members)) { if (m.isLexicallyScoped()) { c->requiresExecutionContext = true; break; @@ -864,13 +862,13 @@ void ScanFunctions::calcEscapingVariables() mIt->canEscape = true; } const QLatin1String exprForOn("expression for on"); - if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() && + if (c->contextType == ContextType::Binding && c->name.size() > exprForOn.size() && c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper()) // we don't really need this for bindings, but we do for signal handlers, and in this case, // we don't know if the code is a signal handler or not. c->requiresExecutionContext = true; if (c->allVarsEscape) { - for (const auto &m : qAsConst(c->members)) + for (const auto &m : std::as_const(c->members)) m.canEscape = true; } } @@ -878,7 +876,7 @@ void ScanFunctions::calcEscapingVariables() static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS"); if (showEscapingVars) { qDebug() << "==== escaping variables ===="; - for (Context *c : qAsConst(m->contextMap)) { + for (Context *c : std::as_const(m->contextMap)) { qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict; qDebug() << " isArrowFunction" << c->isArrowFunction << "innerFunctionAccessesThis" << c->innerFunctionAccessesThis; qDebug() << " parent:" << c->parent; diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 2de80eac44..db160db09d 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #ifndef QV4COMPILERSCANFUNCTIONS_P_H #define QV4COMPILERSCANFUNCTIONS_P_H @@ -83,21 +47,38 @@ public: ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType); void operator()(QQmlJS::AST::Node *node); + // see comment at its call site in generateJSCodeForFunctionsAndBindings + // for why this function is necessary + void handleTopLevelFunctionFormals(QQmlJS::AST::FunctionExpression *node) { + if (node && node->formals) + node->formals->accept(this); + } + void enterGlobalEnvironment(ContextType compilationMode); void enterEnvironment(QQmlJS::AST::Node *node, ContextType compilationMode, const QString &name); void leaveEnvironment(); void enterQmlFunction(QQmlJS::AST::FunctionExpression *ast) - { enterFunction(ast, false); } + { enterFunction(ast, FunctionNameContext::None); } protected: + // Function declarations add their name to the outer scope, but not the + // inner scope. Function expressions add their name to the inner scope, + // unless the name is actually picked from the outer scope rather than + // given after the function token. QML functions don't add their name + // anywhere because the name is already recorded in the QML element. + // This enum is used to control the behavior of enterFunction(). + enum class FunctionNameContext { + None, Inner, Outer + }; + using Visitor::visit; using Visitor::endVisit; void checkDirectivePrologue(QQmlJS::AST::StatementList *ast); - void checkName(const QStringRef &name, const QQmlJS::AST::SourceLocation &loc); + void checkName(QStringView name, const QQmlJS::SourceLocation &loc); bool visit(QQmlJS::AST::Program *ast) override; void endVisit(QQmlJS::AST::Program *) override; @@ -118,7 +99,8 @@ protected: bool visit(QQmlJS::AST::FieldMemberExpression *) override; bool visit(QQmlJS::AST::ArrayPattern *) override; - bool enterFunction(QQmlJS::AST::FunctionExpression *ast, bool enterName); + bool enterFunction(QQmlJS::AST::FunctionExpression *ast, + FunctionNameContext nameContext); void endVisit(QQmlJS::AST::FunctionExpression *) override; @@ -161,7 +143,7 @@ protected: protected: bool enterFunction(QQmlJS::AST::Node *ast, const QString &name, QQmlJS::AST::FormalParameterList *formals, - QQmlJS::AST::StatementList *body, bool enterName); + QQmlJS::AST::StatementList *body, FunctionNameContext nameContext); void calcEscapingVariables(); // fields: diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 640a908dd3..edba9d40b0 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "qv4instr_moth_p.h" #include <private/qv4compileddata_p.h> @@ -84,20 +48,18 @@ static QByteArray rawBytes(const char *data, int n) } #define ABSOLUTE_OFFSET() \ - (code - start + offset) + (code + beginOffset - start + offset) #define MOTH_BEGIN_INSTR(instr) \ { \ INSTR_##instr(MOTH_DECODE_WITH_BASE) \ - QDebug d = qDebug(); \ - d.noquote(); \ - d.nospace(); \ if (static_cast<int>(Instr::Type::instr) >= 0x100) \ --base_ptr; \ - d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \ + s << alignedLineNumber(line) << alignedNumber(beginOffset + codeOffset).constData() << ": " \ << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " "; #define MOTH_END_INSTR(instr) \ + s << "\n"; \ continue; \ } @@ -142,19 +104,37 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } -void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) +QString dumpBytecode( + const char *code, int len, int nLocals, int nFormals, int /*startLine*/, + const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping) { + return dumpBytecode(code, len, nLocals, nFormals, 0, len - 1, lineAndStatementNumberMapping); +} + +QString dumpBytecode( + const char *code, int len, int nLocals, int nFormals, int beginOffset, int endOffset, + const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping) +{ + Q_ASSERT(beginOffset <= endOffset && 0 <= beginOffset && endOffset <= len); + MOTH_JUMP_TABLE; - auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + auto findLine = [](const CompiledData::CodeOffsetToLineAndStatement &entry, uint offset) { return entry.codeOffset < offset; }; + QString output; + QTextStream s{ &output }; + int lastLine = -1; + code += beginOffset; const char *start = code; - const char *end = code + len; + const char *end = code + (endOffset - beginOffset) + 1; while (code < end) { - const CompiledData::CodeOffsetToLine *codeToLine = std::lower_bound(lineNumberMapping.constBegin(), lineNumberMapping.constEnd(), static_cast<uint>(code - start) + 1, findLine) - 1; + const auto codeToLine = std::lower_bound( + lineAndStatementNumberMapping.constBegin(), + lineAndStatementNumberMapping.constEnd(), + static_cast<uint>(code - start + beginOffset) + 1, findLine) - 1; int line = int(codeToLine->line); if (line != lastLine) lastLine = line; @@ -166,23 +146,23 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_DISPATCH() MOTH_BEGIN_INSTR(LoadReg) - d << dumpRegister(reg, nFormals); + s << dumpRegister(reg, nFormals); MOTH_END_INSTR(LoadReg) MOTH_BEGIN_INSTR(StoreReg) - d << dumpRegister(reg, nFormals); + s << dumpRegister(reg, nFormals); MOTH_END_INSTR(StoreReg) MOTH_BEGIN_INSTR(MoveReg) - d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals); + s << dumpRegister(srcReg, nFormals) << ", " << dumpRegister(destReg, nFormals); MOTH_END_INSTR(MoveReg) MOTH_BEGIN_INSTR(LoadImport) - d << "i" << index; + s << "i" << index; MOTH_END_INSTR(LoadImport) MOTH_BEGIN_INSTR(LoadConst) - d << "C" << index; + s << "C" << index; MOTH_END_INSTR(LoadConst) MOTH_BEGIN_INSTR(LoadNull) @@ -201,103 +181,111 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadUndefined) MOTH_BEGIN_INSTR(LoadInt) - d << value; + s << value; MOTH_END_INSTR(LoadInt) MOTH_BEGIN_INSTR(MoveConst) - d << dumpRegister(destTemp, nFormals) << ", C" << constIndex; + s << "C" << constIndex << ", " << dumpRegister(destTemp, nFormals); MOTH_END_INSTR(MoveConst) MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index; + s << "l" << index; else - d << "a" << (index - nLocals); + s << "a" << (index - nLocals); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) if (index < nLocals) - d << "l" << index; + s << "l" << index; else - d << "a" << (index - nLocals); + s << "a" << (index - nLocals); MOTH_END_INSTR(StoreLocal) MOTH_BEGIN_INSTR(LoadScopedLocal) if (index < nLocals) - d << "l" << index << "@" << scope; + s << "l" << index << "@" << scope; else - d << "a" << (index - nLocals) << "@" << scope; + s << "a" << (index - nLocals) << "@" << scope; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) if (index < nLocals) - d << ", " << "l" << index << "@" << scope; + s << ", " << "l" << index << "@" << scope; else - d << ", " << "a" << (index - nLocals) << "@" << scope; + s << ", " << "a" << (index - nLocals) << "@" << scope; MOTH_END_INSTR(StoreScopedLocal) MOTH_BEGIN_INSTR(LoadRuntimeString) - d << stringId; + s << stringId; MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(MoveRegExp) - d << dumpRegister(destReg, nFormals) << ", " <<regExpId; + s << regExpId << ", " << dumpRegister(destReg, nFormals); MOTH_END_INSTR(MoveRegExp) MOTH_BEGIN_INSTR(LoadClosure) - d << value; + s << value; MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name; + s << name; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index; + s << index; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) - d << index; + s << index; MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) - d << name; + s << name; MOTH_END_INSTR(StoreNameSloppy) MOTH_BEGIN_INSTR(StoreNameStrict) - d << name; + s << name; MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[acc]"; + s << dumpRegister(base, nFormals) << "[acc]"; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + s << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]"; + s << "acc[" << name << "]"; MOTH_END_INSTR(LoadProperty) + MOTH_BEGIN_INSTR(LoadOptionalProperty) + s << "acc[" << name << "], jump(" << ABSOLUTE_OFFSET() << ")"; + MOTH_END_INSTR(LoadOptionalProperty) + MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")"; + s << "acc(" << index << ")"; MOTH_END_INSTR(GetLookup) + MOTH_BEGIN_INSTR(GetOptionalLookup) + s << "acc(" << index << "), jump(" << ABSOLUTE_OFFSET() << ")"; + MOTH_END_INSTR(GetOptionalLookup) + MOTH_BEGIN_INSTR(StoreProperty) - d << dumpRegister(base, nFormals) << "[" << name<< "]"; + s << dumpRegister(base, nFormals) << "[" << name<< "]"; MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) - d << dumpRegister(base, nFormals) << "(" << index << ")"; + s << dumpRegister(base, nFormals) << "(" << index << ")"; MOTH_END_INSTR(SetLookup) MOTH_BEGIN_INSTR(LoadSuperProperty) - d << dumpRegister(property, nFormals); + s << dumpRegister(property, nFormals); MOTH_END_INSTR(LoadSuperProperty) MOTH_BEGIN_INSTR(StoreSuperProperty) - d << dumpRegister(property, nFormals); + s << dumpRegister(property, nFormals); MOTH_END_INSTR(StoreSuperProperty) MOTH_BEGIN_INSTR(Yield) @@ -307,78 +295,73 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(YieldStar) MOTH_BEGIN_INSTR(Resume) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); MOTH_END_INSTR(Resume) MOTH_BEGIN_INSTR(CallValue) - d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); + s << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) - d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) + s << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) + s << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) ; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) - d << dumpRegister(base, nFormals) << "." << lookupIndex + s << dumpRegister(base, nFormals) << "." << lookupIndex << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPropertyLookup) - MOTH_BEGIN_INSTR(CallElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << dumpArguments(argc, argv, nFormals); - MOTH_END_INSTR(CallElement) - MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals); + s << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals); + s << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals); + s << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) - d << index << dumpArguments(argc, argv, nFormals); + s << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) - d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) + s << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) - d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + s << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(Construct) MOTH_BEGIN_INSTR(ConstructWithSpread) - d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + s << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(ConstructWithSpread) MOTH_BEGIN_INSTR(SetUnwindHandler) if (offset) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); else - d << "<null>"; + s << "<null>"; MOTH_END_INSTR(SetUnwindHandler) MOTH_BEGIN_INSTR(UnwindDispatch) MOTH_END_INSTR(UnwindDispatch) MOTH_BEGIN_INSTR(UnwindToLabel) - d << "(" << level << ") " << ABSOLUTE_OFFSET(); + s << "(" << level << ") " << ABSOLUTE_OFFSET(); MOTH_END_INSTR(UnwindToLabel) MOTH_BEGIN_INSTR(DeadTemporalZoneCheck) - d << name; + s << name; MOTH_END_INSTR(DeadTemporalZoneCheck) MOTH_BEGIN_INSTR(ThrowException) @@ -394,21 +377,21 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(CreateCallContext) MOTH_BEGIN_INSTR(PushCatchContext) - d << index << ", " << name; + s << index << ", " << name; MOTH_END_INSTR(PushCatchContext) MOTH_BEGIN_INSTR(PushWithContext) MOTH_END_INSTR(PushWithContext) MOTH_BEGIN_INSTR(PushBlockContext) - d << index; + s << index; MOTH_END_INSTR(PushBlockContext) MOTH_BEGIN_INSTR(CloneBlockContext) MOTH_END_INSTR(CloneBlockContext) MOTH_BEGIN_INSTR(PushScriptContext) - d << index; + s << index; MOTH_END_INSTR(PushScriptContext) MOTH_BEGIN_INSTR(PopScriptContext) @@ -418,55 +401,55 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(PopContext) MOTH_BEGIN_INSTR(GetIterator) - d << iterator; + s << iterator; MOTH_END_INSTR(GetIterator) MOTH_BEGIN_INSTR(IteratorNext) - d << dumpRegister(value, nFormals) << ", " << dumpRegister(done, nFormals); + s << dumpRegister(value, nFormals) << ", " << ABSOLUTE_OFFSET(); MOTH_END_INSTR(IteratorNext) MOTH_BEGIN_INSTR(IteratorNextForYieldStar) - d << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals); + s << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals) + << ABSOLUTE_OFFSET(); MOTH_END_INSTR(IteratorNextForYieldStar) MOTH_BEGIN_INSTR(IteratorClose) - d << dumpRegister(done, nFormals); MOTH_END_INSTR(IteratorClose) MOTH_BEGIN_INSTR(DestructureRestElement) MOTH_END_INSTR(DestructureRestElement) MOTH_BEGIN_INSTR(DeleteProperty) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + s << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; MOTH_END_INSTR(DeleteProperty) MOTH_BEGIN_INSTR(DeleteName) - d << name; + s << name; MOTH_END_INSTR(DeleteName) MOTH_BEGIN_INSTR(TypeofName) - d << name; + s << name; MOTH_END_INSTR(TypeofName) MOTH_BEGIN_INSTR(TypeofValue) MOTH_END_INSTR(TypeofValue) MOTH_BEGIN_INSTR(DeclareVar) - d << isDeletable << ", " << varName; + s << isDeletable << ", " << varName; MOTH_END_INSTR(DeclareVar) MOTH_BEGIN_INSTR(DefineArray) - d << dumpRegister(args, nFormals) << ", " << argc; + s << dumpRegister(args, nFormals) << ", " << argc; MOTH_END_INSTR(DefineArray) MOTH_BEGIN_INSTR(DefineObjectLiteral) - d << internalClassId + s << internalClassId << ", " << argc << ", " << dumpRegister(args, nFormals); MOTH_END_INSTR(DefineObjectLiteral) MOTH_BEGIN_INSTR(CreateClass) - d << classIndex + s << classIndex << ", " << dumpRegister(heritage, nFormals) << ", " << dumpRegister(computedNames, nFormals); MOTH_END_INSTR(CreateClass) @@ -478,7 +461,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(CreateUnmappedArgumentsObject) MOTH_BEGIN_INSTR(CreateRestParameter) - d << argIndex; + s << argIndex; MOTH_END_INSTR(CreateRestParameter) MOTH_BEGIN_INSTR(ConvertThisToObject) @@ -491,23 +474,23 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(ToObject) MOTH_BEGIN_INSTR(Jump) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpNotUndefined) MOTH_BEGIN_INSTR(JumpNoException) - d << ABSOLUTE_OFFSET(); + s << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpNoException) MOTH_BEGIN_INSTR(CheckException) @@ -520,43 +503,43 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(CmpNeNull) MOTH_BEGIN_INSTR(CmpEqInt) - d << lhs; + s << lhs; MOTH_END_INSTR(CmpEq) MOTH_BEGIN_INSTR(CmpNeInt) - d << lhs; + s << lhs; MOTH_END_INSTR(CmpNeInt) MOTH_BEGIN_INSTR(CmpEq) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpEq) MOTH_BEGIN_INSTR(CmpNe) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpNe) MOTH_BEGIN_INSTR(CmpGt) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpGt) MOTH_BEGIN_INSTR(CmpGe) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpGe) MOTH_BEGIN_INSTR(CmpLt) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpLt) MOTH_BEGIN_INSTR(CmpLe) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpLe) MOTH_BEGIN_INSTR(CmpStrictEqual) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpStrictEqual) MOTH_BEGIN_INSTR(CmpStrictNotEqual) - d << dumpRegister(lhs, nFormals); + s << dumpRegister(lhs, nFormals); MOTH_END_INSTR(CmpStrictNotEqual) MOTH_BEGIN_INSTR(UNot) @@ -578,83 +561,87 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(BitAnd) MOTH_BEGIN_INSTR(BitOr) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(BitOr) MOTH_BEGIN_INSTR(BitXor) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(BitXor) MOTH_BEGIN_INSTR(UShr) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(UShr) MOTH_BEGIN_INSTR(Shr) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Shr) MOTH_BEGIN_INSTR(Shl) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Shl) MOTH_BEGIN_INSTR(BitAndConst) - d << "acc, " << rhs; + s << "acc, " << rhs; MOTH_END_INSTR(BitAndConst) MOTH_BEGIN_INSTR(BitOrConst) - d << "acc, " << rhs; + s << "acc, " << rhs; MOTH_END_INSTR(BitOr) MOTH_BEGIN_INSTR(BitXorConst) - d << "acc, " << rhs; + s << "acc, " << rhs; MOTH_END_INSTR(BitXor) MOTH_BEGIN_INSTR(UShrConst) - d << "acc, " << rhs; + s << "acc, " << rhs; MOTH_END_INSTR(UShrConst) MOTH_BEGIN_INSTR(ShrConst) - d << "acc, " << rhs; + s << "acc, " << rhs; MOTH_END_INSTR(ShrConst) MOTH_BEGIN_INSTR(ShlConst) - d << "acc, " << rhs; + s << "acc, " << rhs; MOTH_END_INSTR(ShlConst) MOTH_BEGIN_INSTR(Exp) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(As) + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(CmpIn) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(CmpIn) MOTH_BEGIN_INSTR(CmpInstanceOf) - d << dumpRegister(lhs, nFormals) << ", acc"; + s << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(CmpInstanceOf) MOTH_BEGIN_INSTR(Ret) @@ -664,20 +651,21 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Debug) MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone) - d << dumpRegister(firstReg, nFormals) << ", " << count; + s << dumpRegister(firstReg, nFormals) << ", " << count; MOTH_END_INSTR(InitializeBlockDeadTemporalZone) MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined) MOTH_END_INSTR(ThrowOnNullOrUndefined) MOTH_BEGIN_INSTR(GetTemplateObject) - d << index; + s << index; MOTH_END_INSTR(GetTemplateObject) MOTH_BEGIN_INSTR(TailCall) - d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + s << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(TailCall) } + return output; } } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index c0dd696b8a..4dde924379 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #ifndef QV4INSTR_MOTH_P_H #define QV4INSTR_MOTH_P_H @@ -89,12 +53,14 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) #define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) +#define INSTR_LoadOptionalProperty(op) INSTRUCTION(op, LoadOptionalProperty, 2, name, offset) #define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) +#define INSTR_GetOptionalLookup(op) INSTRUCTION(op, GetOptionalLookup, 2, index, offset) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) #define INSTR_Resume(op) INSTRUCTION(op, Resume, 1, offset) -#define INSTR_IteratorNextForYieldStar(op) INSTRUCTION(op, IteratorNextForYieldStar, 2, iterator, object) +#define INSTR_IteratorNextForYieldStar(op) INSTRUCTION(op, IteratorNextForYieldStar, 3, iterator, object, offset) #define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base) #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) @@ -105,7 +71,6 @@ QT_BEGIN_NAMESPACE #define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv) #define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) #define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) -#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) #define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) #define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) #define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) @@ -129,8 +94,8 @@ QT_BEGIN_NAMESPACE #define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0) #define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 0) #define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator) -#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 2, value, done) -#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 1, done) +#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 2, value, offset) +#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 0) #define INSTR_DestructureRestElement(op) INSTRUCTION(op, DestructureRestElement, 0) #define INSTR_DeleteProperty(op) INSTRUCTION(op, DeleteProperty, 2, base, index) #define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name) @@ -190,6 +155,7 @@ QT_BEGIN_NAMESPACE #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) #define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) #define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) +#define INSTR_As(op) INSTRUCTION(op, As, 1, lhs) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -229,7 +195,9 @@ QT_BEGIN_NAMESPACE F(LoadElement) \ F(StoreElement) \ F(LoadProperty) \ + F(LoadOptionalProperty) \ F(GetLookup) \ + F(GetOptionalLookup) \ F(StoreProperty) \ F(SetLookup) \ F(LoadSuperProperty) \ @@ -280,11 +248,11 @@ QT_BEGIN_NAMESPACE F(Div) \ F(Mod) \ F(Sub) \ + F(As) \ F(CallValue) \ F(CallWithReceiver) \ F(CallProperty) \ F(CallPropertyLookup) \ - F(CallElement) \ F(CallName) \ F(CallPossiblyDirectEval) \ F(CallGlobalLookup) \ @@ -336,11 +304,7 @@ QT_BEGIN_NAMESPACE #define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1) #if defined(Q_CC_GNU) -#if defined(Q_CC_INTEL) -// icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the -// current use results in an internal compiler error. We could enable this if/when it gets fixed -// in a later version. -# elif defined(Q_OS_WASM) && !defined(__asmjs) +#if defined(Q_OS_WASM) && !defined(__asmjs) // Upstream llvm does not support computed goto for the wasm target, unlike the 'fastcomp' llvm fork // shipped with the emscripten SDK. Disable computed goto usage for non-fastcomp llvm on Wasm. #else @@ -348,7 +312,7 @@ QT_BEGIN_NAMESPACE #endif #endif -#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) +#define MOTH_INSTR_ALIGN_MASK (alignof(QV4::Moth::Instr) - 1) #define MOTH_INSTR_ENUM(I) I, I##_Wide, #define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I)) @@ -508,7 +472,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace CompiledData { -struct CodeOffsetToLine; +struct CodeOffsetToLineAndStatement; } namespace Moth { @@ -533,11 +497,22 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h -void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, - const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()); -inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, - const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping = QVector<CompiledData::CodeOffsetToLine>()) { - dumpBytecode(bytecode.constData(), bytecode.length(), nLocals, nFormals, startLine, lineNumberMapping); +Q_QML_EXPORT +QString dumpBytecode( + const char *bytecode, int len, int nLocals, int nFormals, int beginOffset, int endOffset, + const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping = + QVector<CompiledData::CodeOffsetToLineAndStatement>()); +QString dumpBytecode( + const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, + const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping = + QVector<CompiledData::CodeOffsetToLineAndStatement>()); +inline QString dumpBytecode( + const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, + const QVector<CompiledData::CodeOffsetToLineAndStatement> &lineAndStatementNumberMapping = + QVector<CompiledData::CodeOffsetToLineAndStatement>()) +{ + return dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine, + lineAndStatementNumberMapping); } union Instr diff --git a/src/qml/compiler/qv4util_p.h b/src/qml/compiler/qv4util_p.h index bd9758c1fb..4215003e7c 100644 --- a/src/qml/compiler/qv4util_p.h +++ b/src/qml/compiler/qv4util_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4UTIL_H #define QV4UTIL_H @@ -51,6 +15,7 @@ // #include <QtCore/QBitArray> +#include <QtCore/private/qglobal_p.h> #include <algorithm> #include <vector> |