aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri29
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp1201
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h480
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp92
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h81
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp40
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h47
-rw-r--r--src/qml/compiler/qv4codegen.cpp901
-rw-r--r--src/qml/compiler/qv4codegen_p.h188
-rw-r--r--src/qml/compiler/qv4compiler.cpp312
-rw-r--r--src/qml/compiler/qv4compiler_p.h61
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp86
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h96
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h51
-rw-r--r--src/qml/compiler/qv4compilerglobal_p.h40
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp164
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h66
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp292
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h85
-rw-r--r--src/qml/compiler/qv4util_p.h41
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 &parameterName,
- 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(&param->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(
+ &param->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 &parameterName, 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>