aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-03-21 20:42:38 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-03-21 20:42:47 +0100
commit6767114285db9d0e16dc278d08f231e8561546b4 (patch)
tree0945902a2242fd7ec0a1f7fd3e6acbb769e723bd /src/qml/compiler
parentee076afedccbe1d37306a7972051f84eb036d655 (diff)
parentc32b109e9dea44c6775c2dbf8f164870c1dc8971 (diff)
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri31
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp861
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h187
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator.cpp56
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h194
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp228
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h10
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp252
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h42
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp244
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h362
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp82
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h122
-rw-r--r--src/qml/compiler/qv4codegen.cpp5166
-rw-r--r--src/qml/compiler/qv4codegen_p.h904
-rw-r--r--src/qml/compiler/qv4compilationunitmapper.cpp32
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_p.h2
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp18
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp28
-rw-r--r--src/qml/compiler/qv4compileddata.cpp860
-rw-r--r--src/qml/compiler/qv4compileddata_p.h885
-rw-r--r--src/qml/compiler/qv4compiler.cpp533
-rw-r--r--src/qml/compiler/qv4compiler_p.h57
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp432
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h380
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h436
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp895
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h188
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp702
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h1352
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp1528
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h240
-rw-r--r--src/qml/compiler/qv4isel_p.cpp446
-rw-r--r--src/qml/compiler/qv4isel_p.h218
-rw-r--r--src/qml/compiler/qv4isel_util_p.h241
-rw-r--r--src/qml/compiler/qv4jsir.cpp992
-rw-r--r--src/qml/compiler/qv4jsir_p.h1792
-rw-r--r--src/qml/compiler/qv4jssimplifier.cpp384
-rw-r--r--src/qml/compiler/qv4jssimplifier_p.h154
-rw-r--r--src/qml/compiler/qv4ssa.cpp5848
-rw-r--r--src/qml/compiler/qv4ssa_p.h472
41 files changed, 10980 insertions, 16876 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 60f548a2b0..da3c173545 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -2,26 +2,28 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
HEADERS += \
+ $$PWD/qv4bytecodegenerator_p.h \
$$PWD/qv4compileddata_p.h \
$$PWD/qv4compiler_p.h \
+ $$PWD/qv4compilercontext_p.h \
+ $$PWD/qv4compilercontrolflow_p.h \
+ $$PWD/qv4compilerscanfunctions_p.h \
$$PWD/qv4codegen_p.h \
- $$PWD/qv4isel_p.h \
- $$PWD/qv4jsir_p.h \
- $$PWD/qv4isel_util_p.h \
- $$PWD/qv4ssa_p.h \
$$PWD/qqmlirbuilder_p.h \
$$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4jssimplifier_p.h
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4bytecodehandler_p.h
SOURCES += \
+ $$PWD/qv4bytecodegenerator.cpp \
$$PWD/qv4compileddata.cpp \
$$PWD/qv4compiler.cpp \
+ $$PWD/qv4compilercontext.cpp \
+ $$PWD/qv4compilerscanfunctions.cpp \
$$PWD/qv4codegen.cpp \
- $$PWD/qv4isel_p.cpp \
- $$PWD/qv4jsir.cpp \
- $$PWD/qv4ssa.cpp \
$$PWD/qqmlirbuilder.cpp \
- $$PWD/qv4jssimplifier.cpp
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4bytecodehandler.cpp
!qmldevtools_build {
@@ -40,15 +42,8 @@ SOURCES += \
unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
-
-qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl
}
-qmldevtools_build|qtConfig(qml-interpreter) {
- HEADERS += \
- $$PWD/qv4instr_moth_p.h \
- $$PWD/qv4isel_moth_p.h
- SOURCES += \
- $$PWD/qv4instr_moth.cpp \
- $$PWD/qv4isel_moth.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 f766f0e4c8..ea5efcfc66 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -43,8 +43,10 @@
#include <private/qv4compileddata_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljslexer_p.h>
+#include <private/qv4compilerscanfunctions_p.h>
#include <QCoreApplication>
#include <QCryptographicHash>
+#include <cmath>
#ifndef V4_BOOTSTRAP
#include <private/qqmlglobal_p.h>
@@ -60,7 +62,7 @@ QT_USE_NAMESPACE
static const quint32 emptyStringIndex = 0;
-#ifndef V4_BOOTSTRAP
+#if 0 //ndef V4_BOOTSTRAP
DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS);
#endif // V4_BOOTSTRAP
@@ -86,30 +88,32 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons
flags = QV4::CompiledData::Object::NoFlag;
properties = pool->New<PoolList<Property> >();
aliases = pool->New<PoolList<Alias> >();
+ qmlEnums = pool->New<PoolList<Enum>>();
qmlSignals = pool->New<PoolList<Signal> >();
bindings = pool->New<PoolList<Binding> >();
functions = pool->New<PoolList<Function> >();
functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >();
- declarationsOverride = 0;
+ declarationsOverride = nullptr;
}
-QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
+QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
{
QSet<int> functionNames;
- for (Function *f = functions->first; f; f = f->next) {
- QQmlJS::AST::FunctionDeclaration *function = f->functionDeclaration;
- Q_ASSERT(function);
- *errorLocation = function->identifierToken;
+ for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
+ Function *f = functionit.ptr;
+ errorLocation->startLine = f->location.line;
+ errorLocation->startColumn = f->location.column;
if (functionNames.contains(f->nameIndex))
return tr("Duplicate method name");
functionNames.insert(f->nameIndex);
- for (QmlIR::Signal *s = qmlSignals->first; s; s = s->next) {
+ for (auto signalit = obj->signalsBegin(); signalit != obj->signalsEnd(); ++signalit) {
+ QmlIR::Signal *s = signalit.ptr;
if (s->nameIndex == f->nameIndex)
return tr("Duplicate method name");
}
- const QString name = function->name.toString();
+ const QString name = stringAt(f->nameIndex);
if (name.at(0).isUpper())
return tr("Method names cannot begin with an upper case letter");
if (illegalNames.contains(name))
@@ -118,6 +122,21 @@ QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQml
return QString(); // no error
}
+QString Object::appendEnum(Enum *enumeration)
+{
+ Object *target = declarationsOverride;
+ if (!target)
+ target = this;
+
+ for (Enum *e = qmlEnums->first; e; e = e->next) {
+ if (e->nameIndex == enumeration->nameIndex)
+ return tr("Duplicate scoped enum name");
+ }
+
+ target->qmlEnums->append(enumeration);
+ return QString(); // no error
+}
+
QString Object::appendSignal(Signal *signal)
{
Object *target = declarationsOverride;
@@ -215,7 +234,7 @@ Binding *Object::findBinding(quint32 nameIndex) const
for (Binding *b = bindings->first; b; b = b->next)
if (b->propertyNameIndex == nameIndex)
return b;
- return 0;
+ return nullptr;
}
void Object::insertSorted(Binding *b)
@@ -257,7 +276,7 @@ void Document::removeScriptPragmas(QString &script)
const QLatin1String pragma("pragma");
const QLatin1String library("library");
- QQmlJS::Lexer l(0);
+ QQmlJS::Lexer l(nullptr);
l.setCode(script, 0);
int token = l.lex();
@@ -299,22 +318,21 @@ void Document::removeScriptPragmas(QString &script)
Document::Document(bool debugMode)
: jsModule(debugMode)
- , program(0)
- , indexOfRootObject(0)
+ , program(nullptr)
, jsGenerator(&jsModule)
{
}
-ScriptDirectivesCollector::ScriptDirectivesCollector(QQmlJS::Engine *engine, QV4::Compiler::JSUnitGenerator *unitGenerator)
- : engine(engine)
- , jsGenerator(unitGenerator)
- , hasPragmaLibrary(false)
+ScriptDirectivesCollector::ScriptDirectivesCollector(Document *doc)
+ : document(doc)
+ , engine(&doc->jsParserEngine)
+ , jsGenerator(&doc->jsGenerator)
{
}
void ScriptDirectivesCollector::pragmaLibrary()
{
- hasPragmaLibrary = true;
+ document->jsModule.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
}
void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString &module, int lineNumber, int column)
@@ -325,7 +343,7 @@ void ScriptDirectivesCollector::importFile(const QString &jsfile, const QString
import->qualifierIndex = jsGenerator->registerString(module);
import->location.line = lineNumber;
import->location.column = column;
- imports << import;
+ document->imports << import;
}
void ScriptDirectivesCollector::importModule(const QString &uri, const QString &version, const QString &module, int lineNumber, int column)
@@ -341,21 +359,21 @@ void ScriptDirectivesCollector::importModule(const QString &uri, const QString &
import->qualifierIndex = jsGenerator->registerString(module);
import->location.line = lineNumber;
import->location.column = column;
- imports << import;
+ document->imports << import;
}
IRBuilder::IRBuilder(const QSet<QString> &illegalNames)
: illegalNames(illegalNames)
- , _object(0)
- , _propertyDeclaration(0)
- , pool(0)
- , jsGenerator(0)
+ , _object(nullptr)
+ , _propertyDeclaration(nullptr)
+ , pool(nullptr)
+ , jsGenerator(nullptr)
{
}
bool IRBuilder::generateFromQml(const QString &code, const QString &url, Document *output)
{
- QQmlJS::AST::UiProgram *program = 0;
+ QQmlJS::AST::UiProgram *program = nullptr;
{
QQmlJS::Lexer lexer(&output->jsParserEngine);
lexer.setCode(code, /*line = */ 1);
@@ -404,7 +422,10 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen
QQmlJS::AST::UiObjectDefinition *rootObject = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(program->members->member);
Q_ASSERT(rootObject);
- defineQMLObject(&output->indexOfRootObject, rootObject);
+ int rootObjectIndex = -1;
+ if (defineQMLObject(&rootObjectIndex, rootObject)) {
+ Q_ASSERT(rootObjectIndex == 0);
+ }
qSwap(_imports, output->imports);
qSwap(_pragmas, output->pragmas);
@@ -459,7 +480,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectDefinition *node)
appendBinding(nameLocation, nameLocation, emptyStringIndex, idx);
} else {
int idx = 0;
- if (!defineQMLObject(&idx, /*qualfied type name id*/0, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object))
+ if (!defineQMLObject(&idx, /*qualfied type name id*/nullptr, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object))
return false;
appendBinding(node->qualifiedTypeNameId, idx);
}
@@ -477,14 +498,14 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node)
bool IRBuilder::visit(QQmlJS::AST::UiScriptBinding *node)
{
- appendBinding(node->qualifiedId, node->statement);
+ appendBinding(node->qualifiedId, node->statement, node);
return false;
}
bool IRBuilder::visit(QQmlJS::AST::UiArrayBinding *node)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = node->qualifiedId->identifierToken;
- Object *object = 0;
+ Object *object = nullptr;
QQmlJS::AST::UiQualifiedId *name = node->qualifiedId;
if (!resolveQualifiedId(&name, &object))
return false;
@@ -493,7 +514,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiArrayBinding *node)
const int propertyNameIndex = registerString(name->name.toString());
- if (bindingsTarget()->findBinding(propertyNameIndex) != 0) {
+ if (bindingsTarget()->findBinding(propertyNameIndex) != nullptr) {
recordError(name->identifierToken, tr("Property value set multiple times"));
return false;
}
@@ -568,7 +589,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu
_object->declarationsOverride = declarationsOverride;
// A new object is also a boundary for property declarations.
- Property *declaration = 0;
+ Property *declaration = nullptr;
qSwap(_propertyDeclaration, declaration);
accept(initializer);
@@ -581,7 +602,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu
return false;
QQmlJS::AST::SourceLocation loc;
- QString error = obj->sanityCheckFunctionNames(illegalNames, &loc);
+ QString error = sanityCheckFunctionNames(obj, illegalNames, &loc);
if (!error.isEmpty()) {
recordError(loc, error);
return false;
@@ -598,7 +619,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
if (!node->fileName.isNull()) {
uri = node->fileName.toString();
- if (uri.endsWith(QLatin1String(".js"))) {
+ if (uri.endsWith(QLatin1String(".js")) || uri.endsWith(QLatin1String(".mjs"))) {
import->type = QV4::CompiledData::Import::ImportScript;
} else {
import->type = QV4::CompiledData::Import::ImportFile;
@@ -670,9 +691,9 @@ 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->pragmaType->name.isNull())
+ if (!node->name.isNull())
{
- if (QLatin1String("Singleton") == node->pragmaType->name)
+ if (QLatin1String("Singleton") == node->name)
{
pragma->type = Pragma::PragmaSingleton;
} else {
@@ -709,6 +730,52 @@ static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
return QStringList();
}
+bool IRBuilder::visit(QQmlJS::AST::UiEnumDeclaration *node)
+{
+ Enum *enumeration = New<Enum>();
+ QString enumName = node->name.toString();
+ enumeration->nameIndex = registerString(enumName);
+
+ 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->enumValues = New<PoolList<EnumValue>>();
+
+ QQmlJS::AST::UiEnumMemberList *e = node->members;
+ while (e) {
+ EnumValue *enumValue = New<EnumValue>();
+ QString member = e->member.toString();
+ enumValue->nameIndex = registerString(member);
+ if (member.at(0).isLower())
+ COMPILE_EXCEPTION(e->memberToken, tr("Enum names must begin with an upper case letter"));
+
+ double part;
+ if (std::modf(e->value, &part) != 0.0)
+ COMPILE_EXCEPTION(e->valueToken, tr("Enum value must be an integer"));
+ if (e->value > std::numeric_limits<qint32>::max() || e->value < std::numeric_limits<qint32>::min())
+ 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;
+ enumeration->enumValues->append(enumValue);
+
+ e = e->next;
+ }
+
+ QString error = _object->appendEnum(enumeration);
+ if (!error.isEmpty()) {
+ recordError(node->enumToken, error);
+ return false;
+ }
+
+ return false;
+}
+
+
bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
{
static const struct TypeNameToType {
@@ -765,7 +832,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
return false;
}
- const TypeNameToType *type = 0;
+ const TypeNameToType *type = nullptr;
for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
const TypeNameToType *t = propTypeNameToTypes + typeIndex;
if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
@@ -891,7 +958,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
QQmlJS::AST::Node::accept(node->binding, this);
} else if (node->statement) {
if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement))
- appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement, node);
}
qSwap(_propertyDeclaration, property);
}
@@ -905,26 +972,27 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) {
CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
foe->node = funDecl;
+ foe->parentNode = funDecl;
foe->nameIndex = registerString(funDecl->name.toString());
foe->disableAcceleratedLookups = false;
const int index = _object->functionsAndExpressions->append(foe);
Function *f = New<Function>();
- f->functionDeclaration = funDecl;
QQmlJS::AST::SourceLocation loc = funDecl->identifierToken;
f->location.line = loc.startLine;
f->location.column = loc.startColumn;
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
- int formalsCount = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next)
- ++formalsCount;
+ const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList();
+ int formalsCount = formals.size();
f->formals.allocate(pool, formalsCount);
int i = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i)
- f->formals[i] = registerString(it->name.toString());
+ for (const QString &arg : formals) {
+ f->formals[i] = registerString(arg);
+ ++i;
+ }
_object->appendFunction(f);
} else {
@@ -978,7 +1046,7 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::AST::SourceLocation &first, const
return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset);
}
-void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement)
+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;
@@ -1001,15 +1069,23 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
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->setNumberValueInternal(lit->value);
- } 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->setNumberValueInternal(-lit->value);
- }
+ 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)) {
+ tryGeneratingTranslationBinding(base->name, call->arguments, binding);
+ // If it wasn't a translation binding, a normal script binding will be generated
+ // below.
}
+ } else if (QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(expr)) {
+ binding->flags |= 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->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->value.nullMarker = 0;
}
}
@@ -1019,6 +1095,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
expr->node = statement;
+ expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
expr->disableAcceleratedLookups = false;
@@ -1030,10 +1107,125 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
}
}
-void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value)
+void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::ArgumentList *args, QV4::CompiledData::Binding *binding)
+{
+ if (base == QLatin1String("qsTr")) {
+ QV4::CompiledData::TranslationData translationData;
+ translationData.number = -1;
+ translationData.commentIndex = 0; // empty string
+
+ 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
+
+ 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
+
+ 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
+ }
+
+ args = args->next;
+ if (args)
+ return; // too many arguments, stop
+
+ binding->type = QV4::CompiledData::Binding::Type_String;
+ binding->stringIndex = jsGenerator->registerString(str.toString());
+ }
+}
+
+void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken;
- Object *object = 0;
+ Object *object = nullptr;
if (!resolveQualifiedId(&name, &object))
return;
if (_object == object && name->name == QLatin1String("id")) {
@@ -1041,14 +1233,14 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta
return;
}
qSwap(_object, object);
- appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value);
+ appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode);
qSwap(_object, object);
}
void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken;
- Object *object = 0;
+ Object *object = nullptr;
if (!resolveQualifiedId(&name, &object, isOnAssignment))
return;
qSwap(_object, object);
@@ -1056,7 +1248,8 @@ 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, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex,
+ QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
@@ -1064,7 +1257,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
- setBindingValue(binding, value);
+ setBindingValue(binding, value, parentNode);
QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
if (!error.isEmpty()) {
recordError(qualifiedNameLocation, error);
@@ -1199,7 +1392,7 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST
if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node)) {
if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(stmt->expression)) {
str = lit->value;
- node = 0;
+ node = nullptr;
} else
node = stmt->expression;
}
@@ -1270,9 +1463,9 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
if (binding) {
if (isAttachedProperty) {
if (!binding->isAttachedProperty())
- binding = 0;
+ binding = nullptr;
} else if (!binding->isGroupProperty()) {
- binding = 0;
+ binding = nullptr;
}
}
if (!binding) {
@@ -1294,7 +1487,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
int objIndex = 0;
- if (!defineQMLObject(&objIndex, 0, QQmlJS::AST::SourceLocation(), 0, 0))
+ if (!defineQMLObject(&objIndex, nullptr, QQmlJS::AST::SourceLocation(), nullptr, nullptr))
return false;
binding->value.objectIndex = objIndex;
@@ -1361,60 +1554,105 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope
return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
}
-QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
{
+ output.jsGenerator.stringTable.registerString(output.jsModule.fileName);
+ output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl);
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
- QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output);
- const uint unitSize = jsUnit->unitSize;
- const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count();
- const int objectOffsetTableSize = output.objects.count() * sizeof(quint32);
+ const QV4::CompiledData::Unit *jsUnit = nullptr;
+ std::function<QV4::CompiledData::QmlUnit *(QV4::CompiledData::QmlUnit *, uint)> unitFinalizer
+ = [](QV4::CompiledData::QmlUnit *unit, uint) {
+ return unit;
+ };
+
+ // We may already have unit data if we're loading an ahead-of-time generated cache file.
+ if (compilationUnit->data) {
+ jsUnit = compilationUnit->data;
+#ifndef V4_BOOTSTRAP
+ output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings();
+#endif
+ } else {
+ QV4::CompiledData::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;
+ break;
+ }
+ }
+ // This unit's memory was allocated with malloc on the heap, so it's
+ // definitely not suitable for StaticData access.
+ createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
+
+#ifndef V4_BOOTSTRAP
+ if (dependencyHasher) {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (dependencyHasher(&hash)) {
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum));
+ memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum));
+ }
+ }
+#else
+ Q_UNUSED(dependencyHasher);
+#endif
+ createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName);
+ createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl);
+
+ // Combine the qml data into the general unit data.
+ unitFinalizer = [&jsUnit](QV4::CompiledData::QmlUnit *qmlUnit, uint qmlDataSize) {
+ void *ptr = const_cast<QV4::CompiledData::Unit*>(jsUnit);
+ QV4::CompiledData::Unit *newUnit = (QV4::CompiledData::Unit *)realloc(ptr, jsUnit->unitSize + qmlDataSize);
+ jsUnit = newUnit;
+ newUnit->offsetToQmlUnit = newUnit->unitSize;
+ newUnit->unitSize += qmlDataSize;
+ memcpy(const_cast<QV4::CompiledData::QmlUnit *>(newUnit->qmlUnit()), qmlUnit, qmlDataSize);
+ free(const_cast<QV4::CompiledData::QmlUnit*>(qmlUnit));
+ qmlUnit = nullptr;
+ newUnit->generateChecksum();
+ return const_cast<QV4::CompiledData::QmlUnit*>(newUnit->qmlUnit());
+ };
+ }
+
+ // 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);
QHash<const Object*, quint32> objectOffsets;
- int objectsSize = 0;
+ const unsigned int objectOffset = sizeof(QV4::CompiledData::QmlUnit) + importSize;
+ uint nextOffset = objectOffset + objectOffsetTableSize;
for (Object *o : qAsConst(output.objects)) {
- objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize);
- objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
+ objectOffsets.insert(o, nextOffset);
+ nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size());
int signalTableSize = 0;
for (const Signal *s = o->firstSignal(); s; s = s->next)
signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count);
- objectsSize += signalTableSize;
+ nextOffset += signalTableSize;
+
+ int enumTableSize = 0;
+ for (const Enum *e = o->firstEnum(); e; e = e->next)
+ enumTableSize += QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
+
+ nextOffset += enumTableSize;
}
- const int totalSize = unitSize + importSize + objectOffsetTableSize + objectsSize + output.jsGenerator.stringTable.sizeOfTableAndData();
+ const uint totalSize = nextOffset;
char *data = (char*)malloc(totalSize);
- memcpy(data, jsUnit, unitSize);
- memset(data + unitSize, 0, totalSize - unitSize);
- if (jsUnit != compilationUnit->data)
- free(jsUnit);
- jsUnit = 0;
-
- QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(data);
- qmlUnit->unitSize = totalSize;
- qmlUnit->flags |= QV4::CompiledData::Unit::IsQml;
- qmlUnit->offsetToImports = unitSize;
+ memset(data, 0, totalSize);
+ QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data);
+ qmlUnit->offsetToImports = sizeof(*qmlUnit);
qmlUnit->nImports = output.imports.count();
- qmlUnit->offsetToObjects = unitSize + importSize;
+ qmlUnit->offsetToObjects = objectOffset;
qmlUnit->nObjects = output.objects.count();
- qmlUnit->indexOfRootObject = output.indexOfRootObject;
- qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData();
- qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount();
-
-#ifndef V4_BOOTSTRAP
- if (dependencyHasher) {
- QCryptographicHash hash(QCryptographicHash::Md5);
- if (dependencyHasher(&hash)) {
- QByteArray checksum = hash.result();
- Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum));
- memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum));
- }
- }
-#else
- Q_UNUSED(dependencyHasher);
-#endif
// write imports
char *importPtr = data + qmlUnit->offsetToImports;
@@ -1425,10 +1663,10 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
}
// write objects
- quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects);
- char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize;
+ quint32_le *objectTable = reinterpret_cast<quint32_le*>(data + qmlUnit->offsetToObjects);
for (int i = 0; i < output.objects.count(); ++i) {
const Object *o = output.objects.at(i);
+ char * const objectPtr = data + objectOffsets.value(o);
*objectTable++ = objectOffsets.value(o);
QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
@@ -1455,6 +1693,10 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
objectToWrite->offsetToAliases = nextOffset;
nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias);
+ objectToWrite->nEnums = o->enumCount();
+ objectToWrite->offsetToEnums = nextOffset;
+ nextOffset += objectToWrite->nEnums * sizeof(quint32);
+
objectToWrite->nSignals = o->signalCount();
objectToWrite->offsetToSignals = nextOffset;
nextOffset += objectToWrite->nSignals * sizeof(quint32);
@@ -1463,11 +1705,11 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
objectToWrite->offsetToBindings = nextOffset;
nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
- objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count;
+ objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.size();
objectToWrite->offsetToNamedObjectsInComponent = nextOffset;
nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32);
- quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions);
+ 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);
@@ -1493,7 +1735,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingToAlias);
Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount()));
- quint32 *signalOffsetTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToSignals);
+ quint32_le *signalOffsetTable = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToSignals);
quint32 signalTableSize = 0;
char *signalPtr = objectPtr + nextOffset;
for (const Signal *s = o->firstSignal(); s; s = s->next) {
@@ -1512,29 +1754,59 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
signalTableSize += size;
signalPtr += size;
}
-
- quint32 *namedObjectInComponentPtr = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent);
- for (int i = 0; i < o->namedObjectsInComponent.count; ++i) {
- *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
+ 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;
+ QV4::CompiledData::Enum *enumToWrite = reinterpret_cast<QV4::CompiledData::Enum*>(enumPtr);
+
+ enumToWrite->nameIndex = e->nameIndex;
+ enumToWrite->location = e->location;
+ enumToWrite->nEnumValues = e->enumValues->count;
+
+ QV4::CompiledData::EnumValue *enumValueToWrite = reinterpret_cast<QV4::CompiledData::EnumValue*>(enumPtr + sizeof(*enumToWrite));
+ for (EnumValue *enumValue = e->enumValues->first; enumValue; enumValue = enumValue->next, ++enumValueToWrite)
+ *enumValueToWrite = *enumValue;
+
+ int size = QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
+ enumTableSize += size;
+ enumPtr += size;
}
- objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
- objectPtr += signalTableSize;
- }
-
- // enable flag if we encountered pragma Singleton
- for (Pragma *p : qAsConst(output.pragmas)) {
- if (p->type == Pragma::PragmaSingleton) {
- qmlUnit->flags |= QV4::CompiledData::Unit::IsSingleton;
- break;
+ quint32_le *namedObjectInComponentPtr = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent);
+ for (int i = 0; i < o->namedObjectsInComponent.size(); ++i) {
+ *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
}
}
- output.jsGenerator.stringTable.serialize(qmlUnit);
+ qmlUnit = unitFinalizer(qmlUnit, totalSize);
- qmlUnit->generateChecksum();
+ static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
+ if (showStats) {
+ qDebug() << "Generated QML unit that is" << totalSize << "bytes big contains:";
+ qDebug() << " " << jsUnit->functionTableSize << "functions";
+ qDebug() << " " << jsUnit->unitSize << "for JS unit";
+ qDebug() << " " << importSize << "for imports";
+ qDebug() << " " << nextOffset - objectOffset - objectOffsetTableSize << "for" << qmlUnit->nObjects << "objects";
+ quint32 totalBindingCount = 0;
+ for (quint32 i = 0; i < qmlUnit->nObjects; ++i)
+ totalBindingCount += qmlUnit->objectAt(i)->nBindings;
+ qDebug() << " " << totalBindingCount << "bindings";
+ quint32 totalCodeSize = 0;
+ for (quint32 i = 0; i < jsUnit->functionTableSize; ++i)
+ totalCodeSize += jsUnit->functionAt(i)->codeSize;
+ qDebug() << " " << totalCodeSize << "bytes total byte code";
+ qDebug() << " " << jsUnit->stringTableSize << "strings";
+ quint32 totalStringSize = 0;
+ for (quint32 i = 0; i < jsUnit->stringTableSize; ++i)
+ totalStringSize += QV4::CompiledData::String::calculateSize(jsUnit->stringAtInternal(i));
+ qDebug() << " " << totalStringSize << "bytes total strings";
+ }
- return qmlUnit;
+ compilationUnit->setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl);
}
char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
@@ -1551,22 +1823,25 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
return bindingPtr;
}
-JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine,
- QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool)
- : QQmlJS::Codegen(/*strict mode*/false)
+JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
+ QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
+ QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
+ const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames)
+ : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false)
, sourceCode(sourceCode)
, jsEngine(jsEngine)
, qmlRoot(qmlRoot)
, imports(imports)
, stringPool(stringPool)
, _disableAcceleratedLookups(false)
- , _contextObject(0)
- , _scopeObject(0)
- , _qmlContextTemp(-1)
- , _importedScriptsTemp(-1)
+ , _contextObject(nullptr)
+ , _scopeObject(nullptr)
+ , _qmlContextSlot(-1)
+ , _importedScriptsSlot(-1)
{
+ m_globalNames = globalNames;
+
_module = jsModule;
- _module->setFileName(fileName);
_fileNameIsUrl = true;
}
@@ -1574,7 +1849,7 @@ void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, Q
{
_idObjects = objectIds;
_contextObject = contextObject;
- _scopeObject = 0;
+ _scopeObject = nullptr;
}
void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
@@ -1584,28 +1859,37 @@ void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
+ auto qmlName = [&](const CompiledFunctionOrExpression &c) {
+ if (c.nameIndex != 0)
+ return stringPool->stringForIndex(c.nameIndex);
+ else
+ return QStringLiteral("%qml-expression-entry");
+ };
QVector<int> runtimeFunctionIndices(functions.size());
- ScanFunctions scan(this, sourceCode, GlobalCode);
- scan.enterEnvironment(0, QmlBinding);
- scan.enterQmlScope(qmlRoot, QStringLiteral("context scope"));
+ QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global);
+ scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding);
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != qmlRoot);
+ Q_ASSERT(f.parentNode && f.parentNode != qmlRoot);
QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node);
- if (function)
+ if (function) {
scan.enterQmlFunction(function);
- else
- scan.enterEnvironment(f.node, QmlBinding);
+ } else {
+ Q_ASSERT(f.node != f.parentNode);
+ scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
+ }
scan(function ? function->body : f.node);
scan.leaveEnvironment();
}
scan.leaveEnvironment();
- scan.leaveEnvironment();
- _variableEnvironment = 0;
- _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0));
+ if (hasError)
+ return QVector<int>();
+
+ _context = nullptr;
for (int i = 0; i < functions.count(); ++i) {
const CompiledFunctionOrExpression &qmlFunction = functions.at(i);
@@ -1617,15 +1901,13 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QString name;
if (function)
name = function->name.toString();
- else if (qmlFunction.nameIndex != 0)
- name = stringPool->stringForIndex(qmlFunction.nameIndex);
else
- name = QStringLiteral("%qml-expression-entry");
+ name = qmlName(qmlFunction);
- QQmlJS::AST::SourceElements *body;
- if (function)
- body = function->body ? function->body->elements : 0;
- else {
+ QQmlJS::AST::StatementList *body;
+ if (function) {
+ body = function->body;
+ } else {
// Synthesize source elements.
QQmlJS::MemoryPool *pool = jsEngine->pool();
@@ -1635,47 +1917,43 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QQmlJS::AST::ExpressionNode *expr = node->expressionCast();
stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr);
}
- QQmlJS::AST::SourceElement *element = new (pool) QQmlJS::AST::StatementSourceElement(stmt);
- body = new (pool) QQmlJS::AST::SourceElements(element);
+ body = new (pool) QQmlJS::AST::StatementList(stmt);
body = body->finish();
}
_disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
- int idx = defineFunction(name, node,
- function ? function->formals : 0,
+ int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
+ function ? function->formals : nullptr,
body);
runtimeFunctionIndices[i] = idx;
}
- qDeleteAll(_envMap);
- _envMap.clear();
return runtimeFunctionIndices;
}
-#ifndef V4_BOOTSTRAP
-QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup)
+int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
{
- if (propertyExistsButForceNameLookup)
- *propertyExistsButForceNameLookup = false;
- QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0);
+ int qmlContextTemp = -1;
+ int importedScriptsTemp = -1;
+ qSwap(_qmlContextSlot, qmlContextTemp);
+ qSwap(_importedScriptsSlot, importedScriptsTemp);
- // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
- if (pd && pd->isFunction()) {
- if (propertyExistsButForceNameLookup)
- *propertyExistsButForceNameLookup = true;
- pd = 0;
- }
+ int result = Codegen::defineFunction(name, ast, formals, body);
+
+ qSwap(_importedScriptsSlot, importedScriptsTemp);
+ qSwap(_qmlContextSlot, qmlContextTemp);
+
+ return result;
+}
+
+#ifndef V4_BOOTSTRAP
+QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name)
+{
+ QQmlPropertyData *pd = cache->property(name, /*object*/nullptr, /*context*/nullptr);
if (pd && !cache->isAllowedInRevision(pd))
- pd = 0;
+ return nullptr;
- // Return a copy allocated from our memory pool. Property data pointers can change
- // otherwise when the QQmlPropertyCache changes later in the QML type compilation process.
- if (pd) {
- QQmlPropertyData *copy = pd;
- pd = _function->New<QQmlPropertyData>();
- *pd = *copy;
- }
return pd;
}
@@ -1686,8 +1964,10 @@ enum MetaObjectResolverFlags {
ResolveTypeInformationOnly = 0x8
};
+#if 0
static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
-static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index);
+
+static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index);
static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
const QV4::IR::MemberExpressionResolver *resolver,
@@ -1695,16 +1975,16 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
{
QV4::IR::Type result = QV4::IR::VarType;
- QQmlType *type = static_cast<QQmlType*>(resolver->data);
+ QQmlType type = resolver->qmlType;
if (member->name->constData()->isUpper()) {
bool ok = false;
- int value = type->enumValue(qmlEngine, *member->name, &ok);
+ int value = type.enumValue(qmlEngine, *member->name, &ok);
if (ok) {
member->setEnumValue(value);
return QV4::IR::SInt32Type;
} else {
- int index = type->scopedEnumIndex(qmlEngine, *member->name, &ok);
+ int index = type.scopedEnumIndex(qmlEngine, *member->name, &ok);
if (ok) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
@@ -1714,8 +1994,8 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
}
}
- if (type->isCompositeSingleton()) {
- QQmlRefPointer<QQmlTypeData> tdata = qmlEngine->typeLoader.getType(type->singletonInstanceInfo()->url);
+ if (type.isCompositeSingleton()) {
+ QQmlRefPointer<QQmlTypeData> tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url);
Q_ASSERT(tdata);
tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType()
// When a singleton tries to reference itself, it may not be complete yet.
@@ -1726,8 +2006,8 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
newResolver->flags |= AllPropertiesAreFinal;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
- } else if (type->isSingleton()) {
- const QMetaObject *singletonMeta = type->singletonInstanceInfo()->instanceMetaObject;
+ } else if (type.isSingleton()) {
+ const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject;
if (singletonMeta) { // QJSValue-based singletons cannot be accelerated
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
@@ -1752,13 +2032,13 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
return result;
}
-static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType)
+static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType)
{
Q_ASSERT(resolver);
resolver->resolveMember = &resolveQmlType;
- resolver->data = qmlType;
- resolver->extraData = 0;
+ resolver->qmlType = qmlType;
+ resolver->typenameCache = 0;
resolver->flags = 0;
}
@@ -1767,8 +2047,8 @@ static QV4::IR::DiscoveredType resolveImportNamespace(
QV4::IR::Member *member)
{
QV4::IR::Type result = QV4::IR::VarType;
- QQmlTypeNameCache *typeNamespace = static_cast<QQmlTypeNameCache*>(resolver->extraData);
- void *importNamespace = resolver->data;
+ QQmlTypeNameCache *typeNamespace = resolver->typenameCache;
+ const QQmlImportRef *importNamespace = resolver->import;
QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace);
if (r.isValid()) {
@@ -1776,11 +2056,11 @@ static QV4::IR::DiscoveredType resolveImportNamespace(
if (r.scriptIndex != -1) {
// TODO: remember the index and replace with subscript later.
result = QV4::IR::VarType;
- } else if (r.type) {
+ } else if (r.type.isValid()) {
// TODO: Propagate singleton information, so that it is loaded
// through the singleton getter in the run-time. Until then we
// can't accelerate access :(
- if (!r.type->isSingleton()) {
+ if (!r.type.isSingleton()) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initQmlTypeResolver(newResolver, r.type);
@@ -1795,11 +2075,11 @@ static QV4::IR::DiscoveredType resolveImportNamespace(
}
static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver,
- QQmlTypeNameCache *imports, const void *importNamespace)
+ QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace)
{
resolver->resolveMember = &resolveImportNamespace;
- resolver->data = const_cast<void*>(importNamespace);
- resolver->extraData = imports;
+ resolver->import = importNamespace;
+ resolver->typenameCache = imports;
resolver->flags = 0;
}
@@ -1808,7 +2088,7 @@ static QV4::IR::DiscoveredType resolveMetaObjectProperty(
QV4::IR::Member *member)
{
QV4::IR::Type result = QV4::IR::VarType;
- QQmlPropertyCache *metaObject = static_cast<QQmlPropertyCache*>(resolver->data);
+ QQmlPropertyCache *metaObject = resolver->propertyCache;
if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) {
const QMetaObject *mo = metaObject->createMetaObject();
@@ -1890,7 +2170,7 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver,
Q_ASSERT(resolver);
resolver->resolveMember = &resolveMetaObjectProperty;
- resolver->data = metaObject;
+ resolver->propertyCache = metaObject;
resolver->flags = 0;
}
@@ -1901,54 +2181,59 @@ static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine,
if (!member->name->constData()->isUpper())
return QV4::IR::VarType;
- QQmlType *type = static_cast<QQmlType*>(resolver->data);
+ QQmlType type = resolver->qmlType;
int index = resolver->flags;
bool ok = false;
- int value = type->scopedEnumValue(qmlEngine, index, *member->name, &ok);
+ int value = type.scopedEnumValue(qmlEngine, index, *member->name, &ok);
if (!ok)
return QV4::IR::VarType;
member->setEnumValue(value);
return QV4::IR::SInt32Type;
}
-static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index)
+static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index)
{
Q_ASSERT(resolver);
resolver->resolveMember = &resolveScopedEnum;
- resolver->data = qmlType;
- resolver->extraData = 0;
+ resolver->qmlType = qmlType;
resolver->flags = index;
}
+#endif
#endif // V4_BOOTSTRAP
void JSCodeGen::beginFunctionBodyHook()
{
- _qmlContextTemp = _block->newTemp();
- _importedScriptsTemp = _block->newTemp();
+ _qmlContextSlot = bytecodeGenerator->newRegister();
+ _importedScriptsSlot = bytecodeGenerator->newRegister();
#ifndef V4_BOOTSTRAP
- QV4::IR::Temp *temp = _block->TEMP(_qmlContextTemp);
+ Instruction::LoadQmlContext load;
+ load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
+ bytecodeGenerator->addInstruction(load);
+
+#if 0
temp->type = QV4::IR::QObjectType;
temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
initMetaObjectResolver(temp->memberResolver, _scopeObject);
auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0);
name->type = temp->type;
- move(temp, name);
+#endif
- move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0));
+ Instruction::LoadQmlImportedScripts loadScripts;
+ loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot();
+ bytecodeGenerator->addInstruction(loadScripts);
#endif
}
-QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col)
+QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
{
- Q_UNUSED(line)
- Q_UNUSED(col)
#ifndef V4_BOOTSTRAP
if (_disableAcceleratedLookups)
- return 0;
+ return Reference();
+
// Implement QML lookup semantics in the current file context.
//
// Note: We do not check if properties of the qml scope object or context object
@@ -1963,20 +2248,16 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_function->isQmlBinding)
- _function->idObjectDependencies.insert(mapping.idIndex);
-
- QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex);
- QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
- _block->MOVE(result, s);
- result = _block->TEMP(result->index);
- if (mapping.type) {
- result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- result->memberResolver->owner = _function;
- initMetaObjectResolver(result->memberResolver, mapping.type);
- result->memberResolver->flags |= AllPropertiesAreFinal;
- }
- result->isReadOnly = true; // don't allow use as lvalue
+ if (_context->contextType == QV4::Compiler::ContextType::Binding)
+ _context->idObjectDependencies.insert(mapping.idIndex);
+
+ Instruction::LoadIdObject load;
+ load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
+ load.index = mapping.idIndex;
+
+ Reference result = Reference::fromAccumulator(this);
+ bytecodeGenerator->addInstruction(load);
+ result.isReadonly = true;
return result;
}
}
@@ -1985,68 +2266,54 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
QQmlTypeNameCache::Result r = imports->query(name);
if (r.isValid()) {
if (r.scriptIndex != -1) {
- return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(QV4::IR::SInt32Type, r.scriptIndex));
- } else if (r.type) {
- QV4::IR::Name *typeName = _block->NAME(name, line, col);
- // Make sure the run-time loads this through the more efficient singleton getter.
- typeName->qmlSingleton = r.type->isCompositeSingleton();
- typeName->freeOfSideEffects = true;
- QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
- _block->MOVE(result, typeName);
-
- result = _block->TEMP(result->index);
- result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- result->memberResolver->owner = _function;
- initQmlTypeResolver(result->memberResolver, r.type);
- return result;
+ Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot);
+ return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex)));
+ } else if (r.type.isValid()) {
+ return Reference::fromName(this, name);
} else {
Q_ASSERT(r.importNamespace);
- QV4::IR::Name *namespaceName = _block->NAME(name, line, col);
- namespaceName->freeOfSideEffects = true;
- QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
- result->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- result->memberResolver->owner = _function;
- initImportNamespaceResolver(result->memberResolver, imports, r.importNamespace);
-
- _block->MOVE(result, namespaceName);
- return _block->TEMP(result->index);
+ return Reference::fromName(this, name);
}
}
}
if (_scopeObject) {
- bool propertyExistsButForceNameLookup = false;
- QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup);
- if (propertyExistsButForceNameLookup)
- return 0;
- if (pd) {
- QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp);
- base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- base->memberResolver->owner = _function;
- initMetaObjectResolver(base->memberResolver, _scopeObject);
- return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject);
+ QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name);
+ if (data) {
+ // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
+ if (data->isFunction())
+ return Reference::fromName(this, name);
+
+ Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
+ Reference::PropertyCapturePolicy capturePolicy;
+ if (!data->isConstant() && !data->isQmlBinding())
+ capturePolicy = Reference::CaptureAtRuntime;
+ else
+ capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
+ return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
}
}
if (_contextObject) {
- bool propertyExistsButForceNameLookup = false;
- QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup);
- if (propertyExistsButForceNameLookup)
- return 0;
- if (pd) {
- QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp);
- base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
- base->memberResolver->owner = _function;
- initMetaObjectResolver(base->memberResolver, _contextObject);
- return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject);
+ QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name);
+ if (data) {
+ // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
+ if (data->isFunction())
+ return Reference::fromName(this, name);
+
+ Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
+ Reference::PropertyCapturePolicy capturePolicy;
+ if (!data->isConstant() && !data->isQmlBinding())
+ capturePolicy = Reference::CaptureAtRuntime;
+ else
+ capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
+ return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
}
}
-
#else
Q_UNUSED(name)
#endif // V4_BOOTSTRAP
- // fall back to name lookup at run-time.
- return 0;
+ return Reference();
}
#ifndef V4_BOOTSTRAP
@@ -2055,7 +2322,7 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev
{
if (notInRevision) *notInRevision = false;
- QQmlPropertyData *d = cache->property(name, 0, 0);
+ QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
// Find the first property
while (d && d->isFunction())
@@ -2063,7 +2330,7 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev
if (check != IgnoreRevision && d && !cache->isAllowedInRevision(d)) {
if (notInRevision) *notInRevision = true;
- return 0;
+ return nullptr;
} else {
return d;
}
@@ -2074,7 +2341,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis
{
if (notInRevision) *notInRevision = false;
- QQmlPropertyData *d = cache->property(name, 0, 0);
+ QQmlPropertyData *d = cache->property(name, nullptr, nullptr);
if (notInRevision) *notInRevision = false;
while (d && !(d->isFunction()))
@@ -2082,7 +2349,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis
if (d && !cache->isAllowedInRevision(d)) {
if (notInRevision) *notInRevision = true;
- return 0;
+ return nullptr;
} else if (d && d->isSignal()) {
return d;
}
@@ -2095,7 +2362,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis
return cache->signal(d->notifyIndex());
}
- return 0;
+ return nullptr;
}
IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output)
@@ -2107,12 +2374,12 @@ IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *outp
void IRLoader::load()
{
- output->jsGenerator.stringTable.clear();
- for (uint i = 0; i < unit->stringTableSize; ++i)
- output->jsGenerator.stringTable.registerString(unit->stringAt(i));
+ output->jsGenerator.stringTable.initializeFromBackingUnit(unit);
- for (quint32 i = 0; i < unit->nImports; ++i)
- output->imports << unit->importAt(i);
+ const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit();
+
+ for (quint32 i = 0; i < qmlUnit->nImports; ++i)
+ output->imports << qmlUnit->importAt(i);
if (unit->flags & QV4::CompiledData::Unit::IsSingleton) {
QmlIR::Pragma *p = New<QmlIR::Pragma>();
@@ -2121,10 +2388,8 @@ void IRLoader::load()
output->pragmas << p;
}
- output->indexOfRootObject = unit->indexOfRootObject;
-
- for (uint i = 0; i < unit->nObjects; ++i) {
- const QV4::CompiledData::Object *serializedObject = unit->objectAt(i);
+ for (uint i = 0; i < qmlUnit->nObjects; ++i) {
+ const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
QmlIR::Object *object = loadObject(serializedObject);
output->objects.append(object);
}
@@ -2206,6 +2471,22 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
object->qmlSignals->append(s);
}
+ for (uint i = 0; i < serializedObject->nEnums; ++i) {
+ const QV4::CompiledData::Enum *serializedEnum = serializedObject->enumAt(i);
+ QmlIR::Enum *e = pool->New<QmlIR::Enum>();
+ e->nameIndex = serializedEnum->nameIndex;
+ e->location = serializedEnum->location;
+ e->enumValues = pool->New<QmlIR::PoolList<QmlIR::EnumValue> >();
+
+ for (uint i = 0; i < serializedEnum->nEnumValues; ++i) {
+ QmlIR::EnumValue *v = pool->New<QmlIR::EnumValue>();
+ *static_cast<QV4::CompiledData::EnumValue*>(v) = *serializedEnum->enumValueAt(i);
+ e->enumValues->append(v);
+ }
+
+ object->qmlEnums->append(e);
+ }
+
const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable();
for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) {
QmlIR::Property *p = pool->New<QmlIR::Property>();
@@ -2222,9 +2503,7 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
}
}
- QQmlJS::Engine *jsParserEngine = &output->jsParserEngine;
-
- const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable();
+ const quint32_le *functionIdx = serializedObject->functionOffsetTable();
for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
QmlIR::Function *f = pool->New<QmlIR::Function>();
const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx);
@@ -2234,26 +2513,8 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
f->location = compiledFunction->location;
f->nameIndex = compiledFunction->nameIndex;
- QQmlJS::AST::FormalParameterList *paramList = 0;
- const QV4::CompiledData::LEUInt32 *formalNameIdx = compiledFunction->formalsTable();
- for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) {
- const QString formal = unit->stringAt(*formalNameIdx);
- QStringRef paramNameRef = jsParserEngine->newStringRef(formal);
-
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
- }
-
- if (paramList)
- paramList = paramList->finish();
-
- const QString name = unit->stringAt(compiledFunction->nameIndex);
- f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0);
-
f->formals.allocate(pool, int(compiledFunction->nFormals));
- formalNameIdx = compiledFunction->formalsTable();
+ const quint32_le *formalNameIdx = compiledFunction->formalsTable();
for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
f->formals[i] = *formalNameIdx;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 64bf111d9a..8512b22fbd 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -57,14 +57,9 @@
#include <private/qqmljsmemorypool_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qqmljslexer_p.h>
#include <QTextStream>
#include <QCoreApplication>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
QT_BEGIN_NAMESPACE
class QQmlPropertyCache;
@@ -80,17 +75,16 @@ template <typename T>
struct PoolList
{
PoolList()
- : first(0)
- , last(0)
- , count(0)
+ : first(nullptr)
+ , last(nullptr)
{}
T *first;
T *last;
- int count;
+ int count = 0;
int append(T *item) {
- item->next = 0;
+ item->next = nullptr;
if (last)
last->next = item;
else
@@ -110,7 +104,7 @@ struct PoolList
template <typename Sortable, typename Base, Sortable Base::*sortMember>
T *findSortedInsertionPoint(T *item) const
{
- T *insertPos = 0;
+ T *insertPos = nullptr;
for (T *it = first; it; it = it->next) {
if (!(it->*sortMember <= item->*sortMember))
@@ -200,70 +194,26 @@ struct PoolList
Iterator end() { return Iterator(nullptr); }
};
-template <typename T>
-class FixedPoolArray
-{
- T *data;
-public:
- int count;
-
- FixedPoolArray()
- : data(0)
- , count(0)
- {}
-
- void allocate(QQmlJS::MemoryPool *pool, int size)
- {
- count = size;
- data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
- }
-
- void allocate(QQmlJS::MemoryPool *pool, const QVector<T> &vector)
- {
- count = vector.count();
- data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
-
- if (QTypeInfo<T>::isComplex) {
- for (int i = 0; i < count; ++i)
- new (data + i) T(vector.at(i));
- } else {
- memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T));
- }
- }
-
- template <typename Container>
- void allocate(QQmlJS::MemoryPool *pool, const Container &container)
- {
- count = container.count();
- data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
- typename Container::ConstIterator it = container.constBegin();
- for (int i = 0; i < count; ++i)
- new (data + i) T(*it++);
- }
-
- const T &at(int index) const {
- Q_ASSERT(index >= 0 && index < count);
- return data[index];
- }
+struct Object;
- T &operator[](int index) {
- Q_ASSERT(index >= 0 && index < count);
- return data[index];
- }
+struct EnumValue : public QV4::CompiledData::EnumValue
+{
+ EnumValue *next;
+};
+struct Enum
+{
+ int nameIndex;
+ QV4::CompiledData::Location location;
+ PoolList<EnumValue> *enumValues;
- int indexOf(const T &value) const {
- for (int i = 0; i < count; ++i)
- if (data[i] == value)
- return i;
- return -1;
- }
+ int enumValueCount() const { return enumValues->count; }
+ PoolList<EnumValue>::Iterator enumValuesBegin() const { return enumValues->begin(); }
+ PoolList<EnumValue>::Iterator enumValuesEnd() const { return enumValues->end(); }
- const T *begin() const { return data; }
- const T *end() const { return data + count; }
+ Enum *next;
};
-struct Object;
struct SignalParameter : public QV4::CompiledData::Parameter
{
@@ -307,7 +257,6 @@ struct Alias : public QV4::CompiledData::Alias
struct Function
{
- QQmlJS::AST::FunctionDeclaration *functionDeclaration;
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
@@ -324,21 +273,13 @@ struct Function
struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
{
CompiledFunctionOrExpression()
- : node(0)
- , nameIndex(0)
- , disableAcceleratedLookups(false)
- , next(0)
- {}
- CompiledFunctionOrExpression(QQmlJS::AST::Node *n)
- : node(n)
- , nameIndex(0)
- , disableAcceleratedLookups(false)
- , next(0)
{}
- QQmlJS::AST::Node *node; // FunctionDeclaration, Statement or Expression
- quint32 nameIndex;
- bool disableAcceleratedLookups;
- CompiledFunctionOrExpression *next;
+
+ QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
+ QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
+ quint32 nameIndex = 0;
+ bool disableAcceleratedLookups = false;
+ CompiledFunctionOrExpression *next = nullptr;
};
struct Q_QML_PRIVATE_EXPORT Object
@@ -359,6 +300,8 @@ public:
int propertyCount() const { return properties->count; }
Alias *firstAlias() const { return aliases->first; }
int aliasCount() const { return aliases->count; }
+ const Enum *firstEnum() const { return qmlEnums->first; }
+ int enumCount() const { return qmlEnums->count; }
const Signal *firstSignal() const { return qmlSignals->first; }
int signalCount() const { return qmlSignals->count; }
Binding *firstBinding() const { return bindings->first; }
@@ -372,6 +315,8 @@ public:
PoolList<Property>::Iterator propertiesEnd() const { return properties->end(); }
PoolList<Alias>::Iterator aliasesBegin() const { return aliases->begin(); }
PoolList<Alias>::Iterator aliasesEnd() const { return aliases->end(); }
+ PoolList<Enum>::Iterator enumsBegin() const { return qmlEnums->begin(); }
+ PoolList<Enum>::Iterator enumsEnd() const { return qmlEnums->end(); }
PoolList<Signal>::Iterator signalsBegin() const { return qmlSignals->begin(); }
PoolList<Signal>::Iterator signalsEnd() const { return qmlSignals->end(); }
PoolList<Function>::Iterator functionsBegin() const { return functions->begin(); }
@@ -383,8 +328,7 @@ public:
void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
- QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
-
+ 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);
@@ -400,7 +344,7 @@ public:
FixedPoolArray<int> runtimeFunctionIndices;
FixedPoolArray<quint32> namedObjectsInComponent;
- int namedObjectsInComponentCount() const { return namedObjectsInComponent.count; }
+ int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); }
const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); }
private:
@@ -408,6 +352,7 @@ private:
PoolList<Property> *properties;
PoolList<Alias> *aliases;
+ PoolList<Enum> *qmlEnums;
PoolList<Signal> *qmlSignals;
PoolList<Binding> *bindings;
PoolList<Function> *functions;
@@ -428,11 +373,10 @@ struct Q_QML_PRIVATE_EXPORT Document
Document(bool debugMode);
QString code;
QQmlJS::Engine jsParserEngine;
- QV4::IR::Module jsModule;
+ QV4::Compiler::Module jsModule;
QList<const QV4::CompiledData::Import *> imports;
QList<Pragma*> pragmas;
QQmlJS::AST::UiProgram *program;
- int indexOfRootObject;
QVector<Object*> objects;
QV4::Compiler::JSUnitGenerator jsGenerator;
@@ -444,14 +388,14 @@ struct Q_QML_PRIVATE_EXPORT Document
static void removeScriptPragmas(QString &script);
};
-struct Q_QML_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
+class Q_QML_PRIVATE_EXPORT ScriptDirectivesCollector : public QQmlJS::Directives
{
- ScriptDirectivesCollector(QQmlJS::Engine *engine, QV4::Compiler::JSUnitGenerator *unitGenerator);
-
+ QmlIR::Document *document;
QQmlJS::Engine *engine;
QV4::Compiler::JSUnitGenerator *jsGenerator;
- QList<const QV4::CompiledData::Import *> imports;
- bool hasPragmaLibrary;
+
+public:
+ ScriptDirectivesCollector(QmlIR::Document *doc);
void pragmaLibrary() override;
void importFile(const QString &jsfile, const QString &module, int lineNumber, int column) override;
@@ -482,6 +426,7 @@ 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::UiEnumDeclaration *ast) override;
bool visit(QQmlJS::AST::UiPublicMember *ast) override;
bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
bool visit(QQmlJS::AST::UiSourceElement *ast) override;
@@ -489,8 +434,8 @@ public:
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 = 0);
- bool defineQMLObject(int *objectIndex, QQmlJS::AST::UiObjectDefinition *node, Object *declarationsOverride = 0)
+ 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); }
static QString asString(QQmlJS::AST::UiQualifiedId *node);
@@ -501,11 +446,12 @@ public:
QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first,
const QQmlJS::AST::SourceLocation &last) const;
- void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement);
+ void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode);
+ void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding);
- void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value);
+ void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, 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, QQmlJS::AST::Statement *value);
+ void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
bool appendAlias(QQmlJS::AST::UiPublicMember *node);
@@ -528,6 +474,8 @@ 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);
+
QList<QQmlJS::DiagnosticMessage> errors;
QSet<QString> illegalNames;
@@ -548,7 +496,7 @@ public:
struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
{
- QV4::CompiledData::Unit *generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
+ void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
private:
typedef bool (Binding::*BindingFilter)() const;
@@ -558,7 +506,7 @@ private:
#ifndef V4_BOOTSTRAP
struct Q_QML_EXPORT PropertyResolver
{
- PropertyResolver(const QQmlPropertyCache *cache)
+ PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
: cache(cache)
{}
@@ -572,20 +520,20 @@ struct Q_QML_EXPORT PropertyResolver
IgnoreRevision
};
- QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision) const;
+ QQmlPropertyData *property(const QString &name, bool *notInRevision = nullptr, RevisionCheck check = CheckRevision) const;
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
- const QQmlPropertyCache *cache;
+ QQmlRefPointer<QQmlPropertyCache> cache;
};
#endif
-struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen
+struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
- JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule,
- QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
- const QV4::Compiler::StringTableGenerator *stringPool);
+ JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule,
+ QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot,
+ QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
struct IdMapping
{
@@ -601,12 +549,18 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
+ int defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::StatementList *body) override;
+
protected:
void beginFunctionBodyHook() override;
- QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col) override;
+ bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; }
+ Reference fallbackNameLookup(const QString &name) override;
private:
- QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup = 0);
+ // returns nullptr if lookup needs to happen by name
+ QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name);
QString sourceCode;
QQmlJS::Engine *jsEngine; // needed for memory pool
@@ -618,8 +572,8 @@ private:
ObjectIdMapping _idObjects;
QQmlPropertyCache *_contextObject;
QQmlPropertyCache *_scopeObject;
- int _qmlContextTemp;
- int _importedScriptsTemp;
+ int _qmlContextSlot;
+ int _importedScriptsSlot;
};
struct Q_QML_PRIVATE_EXPORT IRLoader {
@@ -639,6 +593,17 @@ private:
} // namespace QmlIR
+struct QQmlCompileError
+{
+ QQmlCompileError() {}
+ QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description)
+ : location(location), description(description) {}
+ QV4::CompiledData::Location location;
+ QString description;
+
+ bool isSet() const { return !description.isEmpty(); }
+};
+
QT_END_NAMESPACE
#endif // QQMLIRBUILDER_P_H
diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp
index f8d63ec634..fb54da5b73 100644
--- a/src/qml/compiler/qqmlpropertycachecreator.cpp
+++ b/src/qml/compiler/qqmlpropertycachecreator.cpp
@@ -45,26 +45,54 @@ QT_BEGIN_NAMESPACE
QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
-QQmlBindingInstantiationContext::QQmlBindingInstantiationContext()
- : referencingObjectIndex(-1)
- , instantiatingBinding(nullptr)
- , instantiatingProperty(nullptr)
+QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding,
+ const QString &instantiatingPropertyName, QQmlPropertyCache *referencingObjectPropertyCache)
+ : referencingObjectIndex(referencingObjectIndex)
+ , instantiatingBinding(instantiatingBinding)
+ , instantiatingPropertyName(instantiatingPropertyName)
+ , referencingObjectPropertyCache(referencingObjectPropertyCache)
{
+}
+bool QQmlBindingInstantiationContext::resolveInstantiatingProperty()
+{
+ if (!instantiatingBinding || instantiatingBinding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ return true;
+
+ Q_ASSERT(referencingObjectIndex >= 0);
+ Q_ASSERT(referencingObjectPropertyCache);
+ Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
+
+ bool notInRevision = false;
+ instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, &notInRevision, QmlIR::PropertyResolver::IgnoreRevision);
+ return instantiatingProperty != nullptr;
}
-QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache)
- : referencingObjectIndex(referencingObjectIndex)
- , instantiatingBinding(instantiatingBinding)
- , instantiatingProperty(nullptr)
+QQmlRefPointer<QQmlPropertyCache> QQmlBindingInstantiationContext::instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const
{
- if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
- Q_ASSERT(referencingObjectIndex >= 0);
- Q_ASSERT(referencingObjectPropertyCache);
- Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
+ if (instantiatingProperty) {
+ if (instantiatingProperty->isQObject()) {
+ return enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType(), instantiatingProperty->typeMinorVersion());
+ } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType())) {
+ return enginePrivate->cache(vtmo, instantiatingProperty->typeMinorVersion());
+ }
+ }
+ return QQmlRefPointer<QQmlPropertyCache>();
+}
+
+void QQmlPendingGroupPropertyBindings::resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const
+{
+ for (QQmlBindingInstantiationContext pendingBinding: *this) {
+ const int groupPropertyObjectIndex = pendingBinding.instantiatingBinding->value.objectIndex;
+
+ if (propertyCaches->at(groupPropertyObjectIndex))
+ continue;
+
+ if (!pendingBinding.resolveInstantiatingProperty())
+ continue;
- bool notInRevision = false;
- instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, &notInRevision);
+ auto cache = pendingBinding.instantiatingPropertyCache(enginePrivate);
+ propertyCaches->set(groupPropertyObjectIndex, cache);
}
}
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
index 3c14abc019..7d416561bb 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -50,18 +50,32 @@
// We mean it.
//
-#include "qqmltypecompiler_p.h"
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qqmlmetaobject_p.h>
QT_BEGIN_NAMESPACE
struct QQmlBindingInstantiationContext {
- QQmlBindingInstantiationContext();
- QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache);
- int referencingObjectIndex;
- const QV4::CompiledData::Binding *instantiatingBinding;
- QQmlPropertyData *instantiatingProperty;
+ QQmlBindingInstantiationContext() {}
+ QQmlBindingInstantiationContext(int referencingObjectIndex,
+ const QV4::CompiledData::Binding *instantiatingBinding,
+ const QString &instantiatingPropertyName,
+ QQmlPropertyCache *referencingObjectPropertyCache);
+
+ bool resolveInstantiatingProperty();
+ QQmlRefPointer<QQmlPropertyCache> instantiatingPropertyCache(QQmlEnginePrivate *enginePrivate) const;
+
+ int referencingObjectIndex = -1;
+ const QV4::CompiledData::Binding *instantiatingBinding = nullptr;
+ QString instantiatingPropertyName;
+ QQmlRefPointer<QQmlPropertyCache> referencingObjectPropertyCache;
+ QQmlPropertyData *instantiatingProperty = nullptr;
+};
+
+struct QQmlPendingGroupPropertyBindings : public QVector<QQmlBindingInstantiationContext>
+{
+ void resolveMissingPropertyCaches(QQmlEnginePrivate *enginePrivate, QQmlPropertyCacheVector *propertyCaches) const;
};
struct QQmlPropertyCacheCreatorBase
@@ -77,14 +91,17 @@ class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
public:
typedef typename ObjectContainer::CompiledObject CompiledObject;
- QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports);
+ QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
+ QQmlEnginePrivate *enginePrivate,
+ const ObjectContainer *objectContainer, const QQmlImports *imports);
QQmlCompileError buildMetaObjects();
protected:
QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
- QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
- QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
+ QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
QString stringAt(int index) const { return objectContainer->stringAt(index); }
@@ -92,14 +109,19 @@ protected:
const ObjectContainer * const objectContainer;
const QQmlImports * const imports;
QQmlPropertyCacheVector *propertyCaches;
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings;
};
template <typename ObjectContainer>
-inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports)
+inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches,
+ QQmlPendingGroupPropertyBindings *pendingGroupPropertyBindings,
+ QQmlEnginePrivate *enginePrivate,
+ const ObjectContainer *objectContainer, const QQmlImports *imports)
: enginePrivate(enginePrivate)
, objectContainer(objectContainer)
, imports(imports)
, propertyCaches(propertyCaches)
+ , pendingGroupPropertyBindings(pendingGroupPropertyBindings)
{
propertyCaches->resize(objectContainer->objectCount());
}
@@ -108,7 +130,7 @@ template <typename ObjectContainer>
inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
{
QQmlBindingInstantiationContext context;
- return buildMetaObjectRecursively(objectContainer->rootObjectIndex(), context);
+ return buildMetaObjectRecursively(/*root object*/0, context);
}
template <typename ObjectContainer>
@@ -116,7 +138,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
{
const CompiledObject *obj = objectContainer->objectAt(objectIndex);
- bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0;
+ bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0 || obj->enumCount() != 0;
if (!needVMEMetaObject) {
auto binding = obj->bindingsBegin();
auto end = obj->bindingsEnd();
@@ -129,9 +151,9 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) {
if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) {
const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
- auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
- QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
if (error.isSet())
return error;
@@ -145,7 +167,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
}
- QQmlPropertyCache *baseTypeCache;
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
{
QQmlCompileError error;
baseTypeCache = propertyCacheForObject(obj, context, &error);
@@ -169,6 +191,14 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
for ( ; binding != end; ++binding)
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+
+ // Binding to group property where we failed to look up the type of the
+ // property? Possibly a group property that is an alias that's not resolved yet.
+ // Let's attempt to resolve it after we're done with the aliases and fill in the
+ // propertyCaches entry then.
+ if (!context.resolveInstantiatingProperty())
+ pendingGroupPropertyBindings->append(context);
+
QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context);
if (error.isSet())
return error;
@@ -180,16 +210,12 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
template <typename ObjectContainer>
-inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
{
if (context.instantiatingProperty) {
- if (context.instantiatingProperty->isQObject()) {
- return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType());
- } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType())) {
- return enginePrivate->cache(vtmo);
- }
+ return context.instantiatingPropertyCache(enginePrivate);
} else if (obj->inheritedTypeNameIndex != 0) {
- auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ auto *typeRef = objectContainer->resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
if (typeRef->isFullyDynamicType) {
@@ -209,26 +235,25 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
} else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) {
- auto *typeRef = objectContainer->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex);
+ auto *typeRef = objectContainer->resolvedType(
+ context.instantiatingBinding->propertyNameIndex);
Q_ASSERT(typeRef);
- QQmlType *qmltype = typeRef->type;
- if (!qmltype) {
+ QQmlType qmltype = typeRef->type;
+ if (!qmltype.isValid()) {
QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
- if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) {
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
+ if (qmltype.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
- const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0;
+ const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate);
if (!attachedMo) {
*error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
return nullptr;
@@ -239,12 +264,12 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache)
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
{
QQmlRefPointer<QQmlPropertyCache> cache;
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
- obj->signalCount() + obj->propertyCount() + obj->aliasCount()));
+ obj->signalCount() + obj->propertyCount() + obj->aliasCount(), obj->enumCount()));
propertyCaches->set(objectIndex, cache);
propertyCaches->setNeedsVMEMetaObject(objectIndex);
@@ -278,7 +303,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
QByteArray newClassName;
- if (objectIndex == objectContainer->rootObjectIndex()) {
+ if (objectIndex == /*root object*/0) {
const QString path = objectContainer->url().path();
int lastSlash = path.lastIndexOf(QLatin1Char('/'));
if (lastSlash > -1) {
@@ -289,7 +314,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
}
}
if (newClassName.isEmpty()) {
- newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName = QQmlMetaObject(baseTypeCache.data()).className();
newClassName.append("_QML_");
newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
}
@@ -329,7 +354,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
// and throw an error if there is a signal/method defined as an override.
QSet<QString> seenSignals;
seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
- QQmlPropertyCache *parentCache = cache;
+ QQmlPropertyCache *parentCache = cache.data();
while ((parentCache = parentCache->parent())) {
if (int pSigCount = parentCache->signalCount()) {
int pSigOffset = parentCache->signalOffset();
@@ -370,6 +395,21 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
}
+ auto e = obj->enumsBegin();
+ auto eend = obj->enumsEnd();
+ for ( ; e != eend; ++e) {
+ const int enumValueCount = e->enumValueCount();
+ QVector<QQmlEnumValue> values;
+ values.reserve(enumValueCount);
+
+ auto enumValue = e->enumValuesBegin();
+ auto end = e->enumValuesEnd();
+ for ( ; enumValue != end; ++enumValue)
+ values.append(QQmlEnumValue(stringAt(enumValue->nameIndex), enumValue->value));
+
+ cache->appendEnum(stringAt(e->nameIndex), values);
+ }
+
// Dynamic signals
auto s = obj->signalsBegin();
auto send = obj->signalsEnd();
@@ -395,22 +435,20 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
// lazily resolved type
Q_ASSERT(param->type == QV4::CompiledData::Property::Custom);
const QString customTypeName = stringAt(param->customTypeNameIndex);
- QQmlType *qmltype = 0;
- if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0))
+ QQmlType qmltype;
+ if (!imports->resolveType(customTypeName, &qmltype, nullptr, nullptr, nullptr))
return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ if (qmltype.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
paramTypes[i + 1] = compilationUnit->metaTypeId;
-
- tdata->release();
} else {
- paramTypes[i + 1] = qmltype->typeId();
+ paramTypes[i + 1] = qmltype.typeId();
}
}
}
@@ -426,7 +464,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
seenSignals.insert(signalName);
cache->appendSignal(signalName, flags, effectiveMethodIndex++,
- paramCount?paramTypes.constData():0, names);
+ paramCount?paramTypes.constData():nullptr, names);
}
@@ -461,6 +499,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
pend = obj->propertiesEnd();
for ( ; p != pend; ++p, ++propertyIdx) {
int propertyType = 0;
+ int propertTypeMinorVersion = 0;
QQmlPropertyData::Flags propertyFlags;
if (p->type == QV4::CompiledData::Property::Var) {
@@ -475,14 +514,14 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList ||
p->type == QV4::CompiledData::Property::Custom);
- QQmlType *qmltype = 0;
- if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) {
+ QQmlType qmltype;
+ if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, nullptr, nullptr, nullptr)) {
return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
}
- Q_ASSERT(qmltype);
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ Q_ASSERT(qmltype.isValid());
+ if (qmltype.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
@@ -493,13 +532,12 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
} else {
propertyType = compilationUnit->listMetaTypeId;
}
-
- tdata->release();
} else {
if (p->type == QV4::CompiledData::Property::Custom) {
- propertyType = qmltype->typeId();
+ propertyType = qmltype.typeId();
+ propertTypeMinorVersion = qmltype.minorVersion();
} else {
- propertyType = qmltype->qListTypeId();
+ propertyType = qmltype.qListTypeId();
}
}
@@ -517,7 +555,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
cache->_defaultPropertyName = propertyName;
cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- propertyType, effectiveSignalIndex);
+ propertyType, propertTypeMinorVersion, effectiveSignalIndex);
effectiveSignalIndex++;
}
@@ -536,11 +574,11 @@ public:
void appendAliasPropertiesToMetaObjects();
- void appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
+ QQmlCompileError appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
private:
void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
- void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, QQmlPropertyRawData::Flags *propertyFlags);
+ QQmlCompileError propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags);
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
@@ -561,7 +599,9 @@ inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCre
template <typename ObjectContainer>
inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
{
- for (int i = 0; i < objectContainer->objectCount(); ++i) {
+ // skip the root object (index 0) as that one does not have a first object index originating
+ // from a binding.
+ for (int i = 1; i < objectContainer->objectCount(); ++i) {
const CompiledObject &component = *objectContainer->objectAt(i);
if (!(component.flags & QV4::CompiledData::Object::IsComponent))
continue;
@@ -570,7 +610,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
}
- const int rootObjectIndex = objectContainer->rootObjectIndex();
+ const int rootObjectIndex = 0;
appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
}
@@ -633,7 +673,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
objectsWithAliases->append(objectIndex);
// Stop at Component boundary
- if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != objectContainer->rootObjectIndex())
+ if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
return;
auto binding = object.bindingsBegin();
@@ -649,8 +689,8 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
}
template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
- const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type,
+inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
QQmlPropertyData::Flags *propertyFlags)
{
const int targetObjectIndex = objectForId(component, alias.targetObjectId);
@@ -668,18 +708,24 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias
auto targetAlias = targetObject.aliasesBegin();
for (uint i = 0; i < alias.localAliasIndex; ++i)
++targetAlias;
- propertyDataForAlias(component, *targetAlias, type, propertyFlags);
- return;
+ return propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags);
} else if (alias.encodedMetaPropertyIndex == -1) {
Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
- auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
+ auto *typeRef = objectContainer->resolvedType(targetObject.inheritedTypeNameIndex);
+ if (!typeRef) {
+ // Can be caused by the alias target not being a valid id or property. E.g.:
+ // property alias dataValue: dataVal
+ // invalidAliasComponent { id: dataVal }
+ return QQmlCompileError(targetObject.location, QQmlPropertyCacheCreatorBase::tr("Invalid alias target"));
+ }
- if (typeRef->type)
- *type = typeRef->type->typeId();
+ if (typeRef->type.isValid())
+ *type = typeRef->type.typeId();
else
*type = typeRef->compilationUnit->metaTypeId;
+ *minorVersion = typeRef->minorVersion;
+
propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
} else {
int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
@@ -716,15 +762,16 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias
propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable;
propertyFlags->isResettable = resettable;
+ return QQmlCompileError();
}
template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
const CompiledObject &component, int objectIndex)
{
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
if (!object.aliasCount())
- return;
+ return QQmlCompileError();
QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
Q_ASSERT(propertyCache);
@@ -739,8 +786,11 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPrope
Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
int type = 0;
+ int minorVersion = 0;
QQmlPropertyData::Flags propertyFlags;
- propertyDataForAlias(component, *alias, &type, &propertyFlags);
+ QQmlCompileError error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags);
+ if (error.isSet())
+ return error;
const QString propertyName = objectContainer->stringAt(alias->nameIndex);
@@ -748,8 +798,10 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPrope
propertyCache->_defaultPropertyName = propertyName;
propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- type, effectiveSignalIndex++);
+ type, minorVersion, effectiveSignalIndex++);
}
+
+ return QQmlCompileError();
}
template <typename ObjectContainer>
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
index 383c20239f..4714f505a7 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -45,20 +45,20 @@
QT_BEGIN_NAMESPACE
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
+ , compilationUnit(compilationUnit)
, imports(imports)
- , qmlUnit(compilationUnit->data)
- , resolvedTypes(compilationUnit->resolvedTypes)
+ , qmlUnit(compilationUnit->unitData())
, propertyCaches(compilationUnit->propertyCaches)
, bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject)
{
- bindingPropertyDataPerObject->resize(qmlUnit->nObjects);
+ bindingPropertyDataPerObject->resize(compilationUnit->objectCount());
}
QVector<QQmlCompileError> QQmlPropertyValidator::validate()
{
- return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0);
+ return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr);
}
typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
@@ -81,7 +81,7 @@ struct BindingFinder
QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
{
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
Q_ASSERT(obj->nBindings == 1);
@@ -94,20 +94,10 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
if (!propertyCache)
return QVector<QQmlCompileError>();
- QStringList deferredPropertyNames;
- {
- const QMetaObject *mo = propertyCache->firstCppMetaObject();
- const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
- if (namesIndex != -1) {
- QMetaClassInfo classInfo = mo->classInfo(namesIndex);
- deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
- }
- }
-
- QQmlCustomParser *customParser = 0;
- if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
- if (typeRef->type)
- customParser = typeRef->type->customParser();
+ QQmlCustomParser *customParser = nullptr;
+ if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) {
+ if (typeRef->type.isValid())
+ customParser = typeRef->type.customParser();
}
QList<const QV4::CompiledData::Binding*> customBindings;
@@ -134,7 +124,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
QmlIR::PropertyResolver propertyResolver(propertyCache);
QString defaultPropertyName;
- QQmlPropertyData *defaultProperty = 0;
+ QQmlPropertyData *defaultProperty = nullptr;
if (obj->indexOfDefaultPropertyOrAlias != -1) {
QQmlPropertyCache *cache = propertyCache->parent();
defaultPropertyName = cache->defaultPropertyName();
@@ -167,19 +157,21 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
bool notInRevision = false;
- QQmlPropertyData *pd = 0;
+ QQmlPropertyData *pd = nullptr;
if (!name.isEmpty()) {
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) {
pd = propertyResolver.signal(name, &notInRevision);
- else
- pd = propertyResolver.property(name, &notInRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision);
+ } else {
+ pd = propertyResolver.property(name, &notInRevision,
+ QmlIR::PropertyResolver::CheckRevision);
+ }
if (notInRevision) {
QString typeName = stringAt(obj->inheritedTypeNameIndex);
- auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex);
- if (objectType && objectType->type) {
- return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion));
+ auto *objectType = resolvedType(obj->inheritedTypeNameIndex);
+ if (objectType && objectType->type.isValid()) {
+ return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion));
} else {
return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
}
@@ -197,16 +189,22 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
collectedBindingPropertyData[i] = pd;
if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
- QQmlType *type = 0;
- QQmlImportNamespace *typeNamespace = 0;
- imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace);
+ QQmlType type;
+ QQmlImportNamespace *typeNamespace = nullptr;
+ imports.resolveType(stringAt(binding->propertyNameIndex), &type, nullptr, nullptr, &typeNamespace);
if (typeNamespace)
return recordError(binding->location, tr("Invalid use of namespace"));
return recordError(binding->location, tr("Invalid attached object assignment"));
}
if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
- const QVector<QQmlCompileError> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()));
+ const bool populatingValueTypeGroupProperty
+ = pd
+ && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())
+ && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment);
+ const QVector<QQmlCompileError> subObjectValidatorErrors
+ = validateObject(binding->value.objectIndex, binding,
+ populatingValueTypeGroupProperty);
if (!subObjectValidatorErrors.isEmpty())
return subObjectValidatorErrors;
}
@@ -279,7 +277,11 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
}
} else {
if (!enginePrivate->propertyCacheForType(pd->propType())) {
- return recordError(binding->location, tr("Invalid grouped property access"));
+ return recordError(binding->location,
+ tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type")
+ .arg(name)
+ .arg(QString::fromLatin1(QMetaType::typeName(pd->propType())))
+ );
}
}
}
@@ -297,6 +299,9 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
}
if (obj->idNameIndex) {
+ if (populatingValueTypeGroupProperty)
+ return recordError(obj->locationOfIdProperty, tr("Invalid use of id property with a value type"));
+
bool notInRevision = false;
collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
}
@@ -306,10 +311,10 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
customParser->validator = this;
customParser->engine = enginePrivate;
customParser->imports = &imports;
- customParser->verifyBindings(qmlUnit, customBindings);
- customParser->validator = 0;
- customParser->engine = 0;
- customParser->imports = (QQmlImports*)0;
+ customParser->verifyBindings(compilationUnit, customBindings);
+ customParser->validator = nullptr;
+ customParser->engine = nullptr;
+ customParser->imports = (QQmlImports*)nullptr;
QVector<QQmlCompileError> parserErrors = customParser->errors();
if (!parserErrors.isEmpty())
return parserErrors;
@@ -333,7 +338,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
return noError;
- QString value = binding->valueAsString(qmlUnit);
+ QString value = binding->valueAsString(compilationUnit.data());
QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
bool ok;
if (p.isFlagType()) {
@@ -347,148 +352,163 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
return noError;
}
+ auto warnOrError = [&](const QString &error) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Null) {
+ QQmlError warning;
+ warning.setUrl(compilationUnit->url());
+ warning.setLine(binding->valueLocation.line);
+ warning.setColumn(binding->valueLocation.column);
+ warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML "
+ "is deprecated. This will become a compile error in "
+ "future versions of Qt."));
+ enginePrivate->warning(warning);
+ return noError;
+ }
+ return QQmlCompileError(binding->valueLocation, error);
+ };
+
switch (property->propType()) {
case QMetaType::QVariant:
break;
case QVariant::String: {
if (!binding->evaluatesToString()) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected"));
+ return warnOrError(tr("Invalid property assignment: string expected"));
}
}
break;
case QVariant::StringList: {
if (!binding->evaluatesToString()) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected"));
+ return warnOrError(tr("Invalid property assignment: string or string list expected"));
}
}
break;
case QVariant::ByteArray: {
if (binding->type != QV4::CompiledData::Binding::Type_String) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected"));
+ return warnOrError(tr("Invalid property assignment: byte array expected"));
}
}
break;
case QVariant::Url: {
if (binding->type != QV4::CompiledData::Binding::Type_String) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected"));
+ return warnOrError(tr("Invalid property assignment: url expected"));
}
}
break;
case QVariant::UInt: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
+ double d = binding->valueAsNumber(compilationUnit->constants);
if (double(uint(d)) == d)
return noError;
}
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected"));
+ return warnOrError(tr("Invalid property assignment: unsigned int expected"));
}
break;
case QVariant::Int: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
+ double d = binding->valueAsNumber(compilationUnit->constants);
if (double(int(d)) == d)
return noError;
}
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected"));
+ return warnOrError(tr("Invalid property assignment: int expected"));
}
break;
case QMetaType::Float: {
if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected"));
+ return warnOrError(tr("Invalid property assignment: number expected"));
}
}
break;
case QVariant::Double: {
if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected"));
+ return warnOrError(tr("Invalid property assignment: number expected"));
}
}
break;
case QVariant::Color: {
bool ok = false;
- QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected"));
+ return warnOrError(tr("Invalid property assignment: color expected"));
}
}
break;
#if QT_CONFIG(datestring)
case QVariant::Date: {
bool ok = false;
- QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected"));
+ return warnOrError(tr("Invalid property assignment: date expected"));
}
}
break;
case QVariant::Time: {
bool ok = false;
- QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected"));
+ return warnOrError(tr("Invalid property assignment: time expected"));
}
}
break;
case QVariant::DateTime: {
bool ok = false;
- QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected"));
+ return warnOrError(tr("Invalid property assignment: datetime expected"));
}
}
break;
#endif // datestring
case QVariant::Point: {
bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ return warnOrError(tr("Invalid property assignment: point expected"));
}
}
break;
case QVariant::PointF: {
bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ return warnOrError(tr("Invalid property assignment: point expected"));
}
}
break;
case QVariant::Size: {
bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
+ return warnOrError(tr("Invalid property assignment: size expected"));
}
}
break;
case QVariant::SizeF: {
bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
+ return warnOrError(tr("Invalid property assignment: size expected"));
}
}
break;
case QVariant::Rect: {
bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected"));
+ return warnOrError(tr("Invalid property assignment: rect expected"));
}
}
break;
case QVariant::RectF: {
bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ return warnOrError(tr("Invalid property assignment: point expected"));
}
}
break;
case QVariant::Bool: {
if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected"));
+ return warnOrError(tr("Invalid property assignment: boolean expected"));
}
}
break;
@@ -497,8 +517,8 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float xp;
float yp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected"));
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: 2D vector expected"));
}
}
break;
@@ -508,8 +528,8 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float yp;
float zy;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected"));
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: 3D vector expected"));
}
}
break;
@@ -520,8 +540,8 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float zy;
float wp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected"));
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: 4D vector expected"));
}
}
break;
@@ -532,55 +552,59 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float yp;
float zp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: quaternion expected"));
+ if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
+ return warnOrError(tr("Invalid property assignment: quaternion expected"));
}
}
break;
case QVariant::RegExp:
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
+ case QVariant::RegularExpression:
+ return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
default: {
// generate single literal value assignment to a list property if required
if (property->propType() == qMetaTypeId<QList<qreal> >()) {
if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected"));
+ return warnOrError(tr("Invalid property assignment: number or array of numbers expected"));
}
break;
} else if (property->propType() == qMetaTypeId<QList<int> >()) {
bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
if (ok) {
- double n = binding->valueAsNumber();
+ double n = binding->valueAsNumber(compilationUnit->constants);
if (double(int(n)) != n)
ok = false;
}
if (!ok)
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected"));
+ return warnOrError(tr("Invalid property assignment: int or array of ints expected"));
break;
} else if (property->propType() == qMetaTypeId<QList<bool> >()) {
if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected"));
+ return warnOrError(tr("Invalid property assignment: bool or array of bools expected"));
}
break;
} else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
if (binding->type != QV4::CompiledData::Binding::Type_String) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected"));
+ return warnOrError(tr("Invalid property assignment: url or array of urls expected"));
}
break;
} else if (property->propType() == qMetaTypeId<QList<QString> >()) {
if (!binding->evaluatesToString()) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected"));
+ return warnOrError(tr("Invalid property assignment: string or array of strings expected"));
}
break;
} else if (property->propType() == qMetaTypeId<QJSValue>()) {
break;
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
break;
+ } else if (property->isQObject()
+ && binding->type == QV4::CompiledData::Binding::Type_Null) {
+ break;
}
// otherwise, try a custom type assignment
QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
if (!converter) {
- return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType()))));
+ return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType()))));
}
}
break;
@@ -628,21 +652,19 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
bool isValueSource = false;
bool isPropertyInterceptor = false;
- QQmlType *qmlType = 0;
- const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
- if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) {
- QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex);
+ if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) {
+ QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
const QMetaObject *mo = cache->firstCppMetaObject();
- while (mo && !qmlType) {
+ QQmlType qmlType;
+ while (mo && !qmlType.isValid()) {
qmlType = QQmlMetaType::qmlType(mo);
mo = mo->superClass();
}
- Q_ASSERT(qmlType);
- }
+ Q_ASSERT(qmlType.isValid());
- if (qmlType) {
- isValueSource = qmlType->propertyValueSourceCast() != -1;
- isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1;
+ isValueSource = qmlType.propertyValueSourceCast() != -1;
+ isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1;
}
if (!isValueSource && !isPropertyInterceptor) {
@@ -656,7 +678,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
// Can only check at instantiation time if the created sub-object successfully casts to the
// target interface.
return noError;
- } else if (property->propType() == QMetaType::QVariant) {
+ } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId<QJSValue>()) {
// We can convert everything to QVariant :)
return noError;
} else if (property->isQList()) {
@@ -668,21 +690,20 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
}
return noError;
- } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) {
- return noError;
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
} else if (QQmlValueTypeFactory::isValueType(property->propType())) {
- return QQmlCompileError(binding->location, tr("Unexpected object assignment"));
+ return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName));
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
} else {
- // We want to raw metaObject here as the raw metaobject is the
+ // We want to use the raw metaObject here as the raw metaobject is the
// actual property type before we applied any extensions that might
// effect the properties on the type, but don't effect assignability
- QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType());
+ // Using -1 for the minor version ensures that we get the raw metaObject.
+ QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1);
- // Will be true if the assgned type inherits propertyMetaObject
+ // Will be true if the assigned type inherits propertyMetaObject
bool isAssignable = false;
// Determine isAssignable value
if (propertyMetaObject) {
@@ -694,7 +715,8 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
if (!isAssignable) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property"));
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
+ .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType()))));
}
}
return noError;
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
index e37b8141f4..e9ae844ccb 100644
--- a/src/qml/compiler/qqmlpropertyvalidator_p.h
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -58,7 +58,7 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit);
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
QVector<QQmlCompileError> validate();
@@ -71,12 +71,16 @@ private:
Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const;
Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const;
- QString stringAt(int index) const { return qmlUnit->stringAt(index); }
+ QString stringAt(int index) const { return compilationUnit->stringAt(index); }
+ QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ {
+ return compilationUnit->resolvedType(id);
+ }
QQmlEnginePrivate *enginePrivate;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
const QQmlImports &imports;
const QV4::CompiledData::Unit *qmlUnit;
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QQmlPropertyCacheVector &propertyCaches;
QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index c09fde86f1..70b048d737 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,10 +44,7 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qv4ssa_p.h>
-
-#include "qqmlpropertycachecreator_p.h"
-#include "qv4jssimplifier_p.h"
+#include <private/qqmldelegatecomponent_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -59,7 +56,7 @@ QT_BEGIN_NAMESPACE
QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
: resolvedTypes(resolvedTypeCache)
, engine(engine)
, typeData(typeData)
@@ -69,19 +66,23 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
{
}
-QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
- for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd();
+ for (auto it = resolvedTypes->constBegin(), end = resolvedTypes->constEnd();
it != end; ++it) {
- QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0;
+ QQmlCustomParser *customParser = (*it)->type.customParser();
if (customParser)
customParsers.insert(it.key(), customParser);
}
+ QQmlPendingGroupPropertyBindings pendingGroupPropertyBindings;
+
+
{
- QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, engine, this, imports());
+ QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, &pendingGroupPropertyBindings,
+ engine, this, imports());
QQmlCompileError error = propertyCacheBuilder.buildMetaObjects();
if (error.isSet()) {
recordError(error);
@@ -123,6 +124,8 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
QQmlComponentAndAliasResolver resolver(this);
if (!resolver.resolve())
return nullptr;
+
+ pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_propertyCaches);
}
{
@@ -131,8 +134,8 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
return nullptr;
}
- // Compile JS binding expressions and signal handlers
if (!document->javaScriptCompilationUnit) {
+ // Compile JS binding expressions and signal handlers if necessary
{
// We can compile script strings ahead of time, but they must be compiled
// without type optimizations as their scope is always entirely dynamic.
@@ -140,37 +143,28 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
sss.scan();
}
- QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable);
+ document->jsModule.fileName = typeData->urlString();
+ document->jsModule.finalUrl = typeData->finalUrlString();
+ QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine,
+ document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
+ v4CodeGenerator.setUseFastLookups(false);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
return nullptr;
- QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator);
- pass.reduceTranslationBindings();
-
- QV4::ExecutionEngine *v4 = engine->v4engine();
- QScopedPointer<QV4::EvalInstructionSelection> isel(v4->iselFactory->create(engine, v4->executableAllocator, &document->jsModule, &document->jsGenerator));
- isel->setUseFastLookups(false);
- isel->setUseTypeInference(true);
- document->javaScriptCompilationUnit = isel->compile(/*generated unit data*/false);
+ document->javaScriptCompilationUnit = v4CodeGenerator.generateCompilationUnit(/*generated unit data*/false);
}
// Generate QML compiled type data structures
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, dependencyHasher);
-
- Q_ASSERT(document->javaScriptCompilationUnit);
- // The js unit owns the data and will free the qml unit.
- document->javaScriptCompilationUnit->data = qmlUnit;
+ qmlGenerator.generate(*document, dependencyHasher);
- QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit;
- compilationUnit = document->javaScriptCompilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit;
compilationUnit->typeNameCache = typeNameCache;
- compilationUnit->resolvedTypes = resolvedTypes;
+ compilationUnit->resolvedTypes = *resolvedTypes;
compilationUnit->propertyCaches = std::move(m_propertyCaches);
- Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects));
-
+ Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount()));
if (errors.isEmpty())
return compilationUnit;
@@ -209,14 +203,14 @@ int QQmlTypeCompiler::registerString(const QString &str)
return document->jsGenerator.registerString(str);
}
-QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const
+int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
{
- return &document->jsModule;
+ return document->jsGenerator.registerConstant(v);
}
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return document->javaScriptCompilationUnit->data;
+ return document->javaScriptCompilationUnit->unitData();
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -229,15 +223,10 @@ QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
return &document->objects;
}
-int QQmlTypeCompiler::rootObjectIndex() const
-{
- return document->indexOfRootObject;
-}
-
void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
{
m_propertyCaches = std::move(caches);
- Q_ASSERT(m_propertyCaches.count() >= document->indexOfRootObject);
+ Q_ASSERT(m_propertyCaches.count() > 0);
}
const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
@@ -310,8 +299,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, imports(typeCompiler->imports())
, customParsers(typeCompiler->customParserCache())
- , resolvedTypes(typeCompiler->resolvedTypes)
- , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames())
+ , illegalNames(typeCompiler->enginePrivate()->v8engine()->illegalNames())
, propertyCaches(typeCompiler->propertyCaches())
{
}
@@ -344,24 +332,22 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
// Attached property?
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
- auto *typeRef = resolvedTypes.value(binding->propertyNameIndex);
- QQmlType *type = typeRef ? typeRef->type : 0;
- if (!type) {
- if (imports->resolveType(propertyName, &type, 0, 0, 0)) {
- if (type->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type->sourceUrl());
+ auto *typeRef = resolvedType(binding->propertyNameIndex);
+ QQmlType type = typeRef ? typeRef->type : QQmlType();
+ if (!type.isValid()) {
+ if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
+ if (type.isComposite()) {
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
- const QMetaObject *attachedType = type ? type->attachedPropertiesType(enginePrivate) : 0;
+ const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate);
if (!attachedType)
COMPILE_EXCEPTION(binding, tr("Non-existent attached object"));
QQmlPropertyCache *cache = compiler->enginePrivate()->cache(attachedType);
@@ -412,15 +398,15 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
} else {
if (notInRevision) {
// Try assinging it as a property later
- if (resolver.property(propertyName, /*notInRevision ptr*/0))
+ if (resolver.property(propertyName, /*notInRevision ptr*/nullptr))
continue;
const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
- auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
- const QQmlType *type = typeRef ? typeRef->type : 0;
- if (type) {
- COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion()));
+ auto *typeRef = resolvedType(obj->inheritedTypeNameIndex);
+ const QQmlType type = typeRef ? typeRef->type : QQmlType();
+ if (type.isValid()) {
+ COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type.module()).arg(type.majorVersion()).arg(type.minorVersion()));
} else {
COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName));
}
@@ -472,21 +458,19 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
QQmlJS::MemoryPool *pool = compiler->memoryPool();
- QQmlJS::AST::FormalParameterList *paramList = 0;
+ QQmlJS::AST::FormalParameterList *paramList = nullptr;
for (const QString &param : qAsConst(parameters)) {
QStringRef paramNameRef = compiler->newStringRef(param);
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
+ QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr);
+ paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b);
}
if (paramList)
- paramList = paramList->finish();
+ paramList = paramList->finish(pool);
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
- QQmlJS::AST::FunctionDeclaration *functionDeclaration = 0;
+ QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr;
if (QQmlJS::AST::ExpressionStatement *es = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(foe->node)) {
if (QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression*>(es->expression)) {
functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(fe->name, fe->formals, fe->body);
@@ -500,14 +484,13 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
if (!functionDeclaration) {
QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
- QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement);
- QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement);
- elements = elements->finish();
-
- QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
+ QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement);
+ body = body->finish();
functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
- functionDeclaration->functionToken = foe->node->firstSourceLocation();
+ functionDeclaration->lbraceToken = functionDeclaration->functionToken
+ = foe->node->firstSourceLocation();
+ functionDeclaration->rbraceToken = foe->node->lastSourceLocation();
}
foe->node = functionDeclaration;
binding->propertyNameIndex = compiler->registerString(propertyName);
@@ -521,7 +504,6 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
, imports(typeCompiler->imports())
- , resolvedTypes(&typeCompiler->resolvedTypes)
{
}
@@ -572,7 +554,8 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS
COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString()));
}
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->setNumberValueInternal((double)enumValue);
+ binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
+// binding->setNumberValueInternal((double)enumValue);
binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
return true;
}
@@ -623,17 +606,17 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
}
return true;
}
- QQmlType *type = 0;
- imports->resolveType(typeName, &type, 0, 0, 0);
+ QQmlType type;
+ imports->resolveType(typeName, &type, nullptr, nullptr, nullptr);
- if (!type && !isQtObject)
+ if (!type.isValid() && !isQtObject)
return true;
int value = 0;
bool ok = false;
- auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
- if (type && tr && tr->type == type) {
+ auto *tr = resolvedType(obj->inheritedTypeNameIndex);
+ if (type.isValid() && tr && tr->type == type) {
// When these two match, we can short cut the search
QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
QMetaEnum menum = mprop.enumerator();
@@ -648,11 +631,11 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
}
} else {
// Otherwise we have to search the whole type
- if (type) {
+ if (type.isValid()) {
if (!scopedEnumName.isEmpty())
- value = type->scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok);
+ value = type.scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok);
else
- value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok);
+ value = type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok);
} else {
QByteArray enumName = enumValue.toUtf8();
const QMetaObject *metaObject = StaticQtMetaObject::get();
@@ -675,13 +658,13 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &e
*ok = false;
if (scope != QLatin1String("Qt")) {
- QQmlType *type = 0;
- imports->resolveType(scope, &type, 0, 0, 0);
- if (!type)
+ QQmlType type;
+ imports->resolveType(scope, &type, nullptr, nullptr, nullptr);
+ if (!type.isValid())
return -1;
if (!enumName.isEmpty())
- return type->scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok);
- return type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok);
+ return type.scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok);
+ return type.enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok);
}
const QMetaObject *mo = StaticQtMetaObject::get();
@@ -704,7 +687,7 @@ QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *t
void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings()
{
- scanObjectRecursively(compiler->rootObjectIndex());
+ scanObjectRecursively(/*root object*/0);
}
void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings)
@@ -799,8 +782,6 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t
, enginePrivate(typeCompiler->enginePrivate())
, pool(typeCompiler->memoryPool())
, qmlObjects(typeCompiler->qmlObjects())
- , indexOfRootObject(typeCompiler->rootObjectIndex())
- , resolvedTypes(&typeCompiler->resolvedTypes)
, propertyCaches(std::move(typeCompiler->takePropertyCaches()))
{
}
@@ -818,17 +799,25 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
continue;
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
- auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
+ auto *tr = resolvedType(targetObject->inheritedTypeNameIndex);
Q_ASSERT(tr);
- if (QQmlType *targetType = tr->type) {
- if (targetType->metaObject() == &QQmlComponent::staticMetaObject)
- continue;
- } else if (tr->compilationUnit) {
- if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
- continue;
- }
- QQmlPropertyData *pd = 0;
+ const QMetaObject *firstMetaObject = nullptr;
+ if (tr->type.isValid())
+ firstMetaObject = tr->type.metaObject();
+ else if (tr->compilationUnit)
+ firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ // 1: test for QQmlComponent
+ if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject)
+ continue;
+ // 2: test for QQmlAbstractDelegateComponent
+ while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject)
+ firstMetaObject = firstMetaObject->superClass();
+ if (firstMetaObject)
+ continue;
+ // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping
+
+ QQmlPropertyData *pd = nullptr;
if (binding->propertyNameIndex != quint32(0)) {
bool notInRevision = false;
pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
@@ -838,8 +827,8 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
if (!pd || !pd->isQObject())
continue;
- QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType());
- const QMetaObject *mo = pc ? pc->firstCppMetaObject() : 0;
+ QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), pd->typeMinorVersion());
+ const QMetaObject *mo = pc ? pc->firstCppMetaObject() : nullptr;
while (mo) {
if (mo == &QQmlComponent::staticMetaObject)
break;
@@ -850,23 +839,23 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
continue;
// emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}"
- QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject);
- Q_ASSERT(componentType);
+ QQmlType componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject);
+ Q_ASSERT(componentType.isValid());
const QString qualifier = QStringLiteral("QmlInternals");
- compiler->addImport(componentType->module(), qualifier, componentType->majorVersion(), componentType->minorVersion());
+ compiler->addImport(componentType.module(), qualifier, componentType.majorVersion(), componentType.minorVersion());
QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
- syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType->elementName()), compiler->registerString(QString()));
+ syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType.elementName()), compiler->registerString(QString()));
syntheticComponent->location = binding->valueLocation;
syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
- if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
+ if (!containsResolvedType(syntheticComponent->inheritedTypeNameIndex)) {
auto typeRef = new QV4::CompiledData::ResolvedTypeReference;
typeRef->type = componentType;
- typeRef->majorVersion = componentType->majorVersion();
- typeRef->minorVersion = componentType->minorVersion();
- resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef);
+ typeRef->majorVersion = componentType.majorVersion();
+ typeRef->minorVersion = componentType.minorVersion();
+ insertResolvedType(syntheticComponent->inheritedTypeNameIndex, typeRef);
}
qmlObjects->append(syntheticComponent);
@@ -904,9 +893,9 @@ bool QQmlComponentAndAliasResolver::resolve()
bool isExplicitComponent = false;
if (obj->inheritedTypeNameIndex) {
- auto *tref = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ auto *tref = resolvedType(obj->inheritedTypeNameIndex);
Q_ASSERT(tref);
- if (tref->type && tref->type->metaObject() == &QQmlComponent::staticMetaObject)
+ if (tref->type.metaObject() == &QQmlComponent::staticMetaObject)
isExplicitComponent = true;
}
if (!isExplicitComponent) {
@@ -937,9 +926,9 @@ bool QQmlComponentAndAliasResolver::resolve()
if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
- // We are going to collect ids/aliases and resolve them for the root object as a separate
+ // For the root object, we are going to collect ids/aliases and resolve them for as a separate
// last pass.
- if (i != indexOfRootObject)
+ if (i != 0)
componentRoots.append(i);
}
@@ -965,12 +954,12 @@ bool QQmlComponentAndAliasResolver::resolve()
_idToObjectIndex.clear();
_objectsWithAliases.clear();
- collectIdsAndAliases(indexOfRootObject);
+ collectIdsAndAliases(/*root object*/0);
- QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject);
+ QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0);
rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
- if (!resolveAliases(indexOfRootObject))
+ if (!resolveAliases(/*root object*/0))
return false;
// Implicit component insertion may have added objects and thus we also need
@@ -998,7 +987,7 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
_objectsWithAliases.append(objectIndex);
// Stop at Component boundary
- if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != compiler->rootObjectIndex())
+ if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0)
return true;
for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
@@ -1037,7 +1026,11 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
}
if (result == AllAliasesResolved) {
- aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
+ QQmlCompileError error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
+ if (error.isSet()) {
+ recordError(error);
+ return false;
+ }
atLeastOneAliasResolved = true;
} else if (result == SomeAliasesResolved) {
atLeastOneAliasResolved = true;
@@ -1107,7 +1100,11 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv
alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
} else {
QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
- Q_ASSERT(targetCache);
+ if (!targetCache) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ break;
+ }
+
QmlIR::PropertyResolver resolver(targetCache);
QQmlPropertyData *targetProperty = resolver.property(property.toString());
@@ -1189,7 +1186,7 @@ QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingSca
bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
{
- return scanObject(compiler->rootObjectIndex());
+ return scanObject(/*root object*/0);
}
bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
@@ -1210,7 +1207,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
return true;
QString defaultPropertyName;
- QQmlPropertyData *defaultProperty = 0;
+ QQmlPropertyData *defaultProperty = nullptr;
if (obj->indexOfDefaultPropertyOrAlias != -1) {
QQmlPropertyCache *cache = propertyCache->parent();
defaultPropertyName = cache->defaultPropertyName();
@@ -1235,7 +1232,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
}
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- QQmlPropertyData *pd = 0;
+ QQmlPropertyData *pd = nullptr;
QString name = stringAt(binding->propertyNameIndex);
if (customParser) {
@@ -1275,7 +1272,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
_seenObjectWithId |= seenSubObjectWithId;
}
- if (!seenSubObjectWithId
+ if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
@@ -1299,7 +1296,6 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen)
: QQmlCompilePass(typeCompiler)
- , resolvedTypes(typeCompiler->resolvedTypes)
, customParsers(typeCompiler->customParserCache())
, qmlObjects(*typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
@@ -1315,7 +1311,7 @@ bool QQmlJSCodeGenerator::generateCodeForComponents()
return false;
}
- return compileComponent(compiler->rootObjectIndex());
+ return compileComponent(/*root object*/0);
}
bool QQmlJSCodeGenerator::compileComponent(int contextObject)
@@ -1329,8 +1325,8 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject)
}
QmlIR::JSCodeGen::ObjectIdMapping idMapping;
- idMapping.reserve(obj->namedObjectsInComponent.count);
- for (int i = 0; i < obj->namedObjectsInComponent.count; ++i) {
+ idMapping.reserve(obj->namedObjectsInComponent.size());
+ for (int i = 0; i < obj->namedObjectsInComponent.size(); ++i) {
const int objectIndex = obj->namedObjectsInComponent.at(i);
QmlIR::JSCodeGen::IdMapping m;
const QmlIR::Object *obj = qmlObjects.at(objectIndex);
@@ -1338,9 +1334,9 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject)
m.idIndex = obj->id;
m.type = propertyCaches->at(objectIndex);
- auto *tref = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ auto *tref = resolvedType(obj->inheritedTypeNameIndex);
if (tref && tref->isFullyDynamicType)
- m.type = 0;
+ m.type = nullptr;
idMapping << m;
}
@@ -1418,10 +1414,10 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
QmlIR::Object *object = qmlObjects.at(objectIndex);
QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
- QmlIR::Binding *bindingsToReinsert = 0;
- QmlIR::Binding *tail = 0;
+ QmlIR::Binding *bindingsToReinsert = nullptr;
+ QmlIR::Binding *tail = nullptr;
- QmlIR::Binding *previousBinding = 0;
+ QmlIR::Binding *previousBinding = nullptr;
QmlIR::Binding *binding = object->firstBinding();
while (binding) {
if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) {
@@ -1440,7 +1436,7 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
tail->next = toReinsert;
tail = tail->next;
}
- tail->next = 0;
+ tail->next = nullptr;
}
binding = bindingsToReinsert;
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 76aa422fc5..a49b97453f 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -55,6 +55,7 @@
#include <qhash.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmlirbuilder_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
QT_BEGIN_NAMESPACE
@@ -74,22 +75,13 @@ struct Location;
}
}
-struct QQmlCompileError
-{
- QQmlCompileError() {}
- QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description)
- : location(location), description(description) {}
- QV4::CompiledData::Location location;
- QString description;
-
- bool isSet() const { return !description.isEmpty(); }
-};
-
struct QQmlTypeCompiler
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
public:
- QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache,
+ QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document,
+ const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache,
const QV4::CompiledData::DependentTypesHasher &dependencyHasher);
// --- interface used by QQmlPropertyCacheCreator
@@ -99,10 +91,10 @@ public:
QString stringAt(int idx) const;
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); }
- QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes;
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes = nullptr;
// ---
- QV4::CompiledData::CompilationUnit *compile();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(QQmlError error);
@@ -110,8 +102,7 @@ public:
void recordError(const QQmlCompileError &error);
int registerString(const QString &str);
-
- QV4::IR::Module *jsIRModule() const;
+ int registerConstant(QV4::ReturnedValue v);
const QV4::CompiledData::Unit *qmlUnit() const;
@@ -119,7 +110,6 @@ public:
QQmlEnginePrivate *enginePrivate() const { return engine; }
const QQmlImports *imports() const;
QVector<QmlIR::Object *> *qmlObjects() const;
- int rootObjectIndex() const;
void setPropertyCaches(QQmlPropertyCacheVector &&caches);
const QQmlPropertyCacheVector *propertyCaches() const;
QQmlPropertyCacheVector &&takePropertyCaches();
@@ -136,6 +126,11 @@ public:
void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion);
+ QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ {
+ return resolvedTypes->value(id);
+ }
+
private:
QList<QQmlError> errors;
QQmlEnginePrivate *engine;
@@ -162,6 +157,14 @@ protected:
void recordError(const QQmlCompileError &error)
{ compiler->recordError(error); }
+ QV4::CompiledData::ResolvedTypeReference *resolvedType(int id) const
+ { return compiler->resolvedType(id); }
+ bool containsResolvedType(int id) const
+ { return compiler->resolvedTypes->contains(id); }
+ QV4::CompiledData::ResolvedTypeReferenceMap::iterator insertResolvedType(
+ int id, QV4::CompiledData::ResolvedTypeReference *value)
+ { return compiler->resolvedTypes->insert(id, value); }
+
QQmlTypeCompiler *compiler;
};
@@ -184,7 +187,6 @@ private:
const QVector<QmlIR::Object*> &qmlObjects;
const QQmlImports *imports;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QSet<QString> &illegalNames;
const QQmlPropertyCacheVector * const propertyCaches;
};
@@ -215,7 +217,6 @@ private:
const QVector<QmlIR::Object*> &qmlObjects;
const QQmlPropertyCacheVector * const propertyCaches;
const QQmlImports *imports;
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes;
};
class QQmlCustomParserScriptIndexer: public QQmlCompilePass
@@ -282,7 +283,6 @@ protected:
QQmlJS::MemoryPool *pool;
QVector<QmlIR::Object*> *qmlObjects;
- const int indexOfRootObject;
// indices of the objects that are actually Component {}
QVector<quint32> componentRoots;
@@ -291,7 +291,6 @@ protected:
QMap<int, int> _idToObjectIndex;
QVector<int> _objectsWithAliases;
- QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes;
QQmlPropertyCacheVector propertyCaches;
};
@@ -324,7 +323,6 @@ private:
bool compileComponent(int componentRoot);
bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
- const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QHash<int, QQmlCustomParser*> &customParsers;
const QVector<QmlIR::Object*> &qmlObjects;
const QQmlPropertyCacheVector * const propertyCaches;
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
new file mode 100644
index 0000000000..ea252a6013
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qqmljsastfwd_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace Moth;
+
+void BytecodeGenerator::setLocation(const QQmlJS::AST::SourceLocation &loc)
+{
+ currentLine = static_cast<int>(loc.startLine);
+}
+
+int BytecodeGenerator::newRegister()
+{
+ int t = currentReg++;
+ if (regCount < currentReg)
+ regCount = currentReg;
+ return t;
+}
+
+int BytecodeGenerator::newRegisterArray(int n)
+{
+ int t = currentReg;
+ currentReg += n;
+ if (regCount < currentReg)
+ regCount = currentReg;
+ return t;
+}
+
+void BytecodeGenerator::packInstruction(I &i)
+{
+ Instr::Type type = Instr::unpack(i.packed);
+ Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
+ type = Instr::narrowInstructionType(type);
+ int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
+ int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
+ uchar *code = i.packed + Instr::encodedLength(type);
+ for (int j = 0; j < nMembers; ++j) {
+ instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int));
+ }
+ enum {
+ Normal,
+ Wide
+ } width = Normal;
+ for (int n = 0; n < nMembers; ++n) {
+ if (width == Normal && (static_cast<qint8>(instructionsAsInts[n]) != instructionsAsInts[n])) {
+ width = Wide;
+ break;
+ }
+ }
+ code = i.packed;
+ switch (width) {
+ case Normal:
+ code = Instr::pack(code, type);
+ for (int n = 0; n < nMembers; ++n) {
+ qint8 v = static_cast<qint8>(instructionsAsInts[n]);
+ memcpy(code, &v, 1);
+ code += 1;
+ }
+ i.size = code - i.packed;
+ if (i.offsetForJump != -1)
+ i.offsetForJump = i.size - 1;
+ break;
+ case Wide:
+ // nothing to do
+ break;
+ }
+}
+
+void BytecodeGenerator::adjustJumpOffsets()
+{
+ for (int index = 0; index < instructions.size(); ++index) {
+ auto &i = instructions[index];
+ if (i.offsetForJump == -1) // no jump
+ continue;
+ Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1);
+ const auto &linkedInstruction = instructions.at(labels.at(i.linkedLabel));
+ qint8 *c = reinterpret_cast<qint8*>(i.packed + i.offsetForJump);
+ int jumpOffset = linkedInstruction.position - (i.position + i.size);
+// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
+// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
+ Instr::Type type = Instr::unpack(i.packed);
+ if (Instr::isWide(type)) {
+ Q_ASSERT(i.offsetForJump == i.size - 4);
+ qToLittleEndian<qint32>(jumpOffset, c);
+ } else {
+ Q_ASSERT(i.offsetForJump == i.size - 1);
+ qint8 o = jumpOffset;
+ Q_ASSERT(o == jumpOffset);
+ *c = o;
+ }
+ }
+}
+
+void BytecodeGenerator::compressInstructions()
+{
+ // first round: compress all non jump instructions
+ int position = 0;
+ for (auto &i : instructions) {
+ i.position = position;
+ if (i.offsetForJump == -1)
+ packInstruction(i);
+ position += i.size;
+ }
+
+ adjustJumpOffsets();
+
+ // compress all jumps
+ position = 0;
+ for (auto &i : instructions) {
+ i.position = position;
+ if (i.offsetForJump != -1)
+ packInstruction(i);
+ position += i.size;
+ }
+
+ // adjust once again, as the packing above could have changed offsets
+ adjustJumpOffsets();
+}
+
+void BytecodeGenerator::finalize(Compiler::Context *context)
+{
+ compressInstructions();
+
+ // collect content and line numbers
+ QByteArray code;
+ QVector<CompiledData::CodeOffsetToLine> lineNumbers;
+ currentLine = -1;
+ Q_UNUSED(startLine);
+ for (const auto &i : qAsConst(instructions)) {
+ if (i.line != currentLine) {
+ currentLine = i.line;
+ CompiledData::CodeOffsetToLine entry;
+ entry.codeOffset = code.size();
+ entry.line = currentLine;
+ lineNumbers.append(entry);
+ }
+ code.append(reinterpret_cast<const char *>(i.packed), i.size);
+ }
+
+ context->code = code;
+ context->lineNumberMapping = lineNumbers;
+
+ 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) {
+ if (lastInstrType == int(Instr::Type::StoreReg)) {
+ if (type == Instr::Type::LoadReg) {
+ if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
+ // value is already in the accumulator
+ return -1;
+ }
+ }
+ if (type == Instr::Type::MoveReg) {
+ if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
+ Instruction::StoreReg store;
+ store.reg = i.MoveReg.destReg;
+ addInstruction(store);
+ return -1;
+ }
+ }
+ }
+ lastInstrType = int(type);
+ lastInstr = i;
+
+#if QT_CONFIG(qml_debug)
+ if (debugMode && type != Instr::Type::Debug) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
+ if (instructions.isEmpty() || currentLine != instructions.constLast().line) {
+ addInstruction(Instruction::Debug());
+ } else if (type == Instr::Type::Ret) {
+ currentLine = -currentLine;
+ addInstruction(Instruction::Debug());
+ currentLine = -currentLine;
+ }
+QT_WARNING_POP
+ }
+#else
+ Q_UNUSED(debugMode);
+#endif
+
+ const int pos = instructions.size();
+
+ const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
+ 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" };
+ uchar *code = instr.packed;
+ code = Instr::pack(code, Instr::wideInstructionType(type));
+
+ for (int j = 0; j < argCount; ++j) {
+ qToLittleEndian<qint32>(i.argumentsAsInts[j], code);
+ code += sizeof(int);
+ }
+
+ instructions.append(instr);
+
+ return pos;
+}
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
new file mode 100644
index 0000000000..1d0a57c536
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -0,0 +1,362 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QV4BYTECODEGENERATOR_P_H
+#define QV4BYTECODEGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <private/qv4instr_moth_p.h>
+#include <private/qv4compileddata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace AST {
+class SourceLocation;
+}
+}
+
+namespace QV4 {
+namespace Moth {
+
+class BytecodeGenerator {
+public:
+ typedef CompiledData::Function::TraceInfoCount TraceInfoCount;
+
+ BytecodeGenerator(int line, bool debug)
+ : startLine(line), debugMode(debug) {}
+
+ struct Label {
+ enum LinkMode {
+ LinkNow,
+ LinkLater
+ };
+ Label() = default;
+ Label(BytecodeGenerator *generator, LinkMode mode = LinkNow)
+ : generator(generator),
+ index(generator->labels.size()) {
+ generator->labels.append(-1);
+ if (mode == LinkNow)
+ link();
+ }
+
+ void link() {
+ Q_ASSERT(index >= 0);
+ Q_ASSERT(generator->labels[index] == -1);
+ generator->labels[index] = generator->instructions.size();
+ generator->clearLastInstruction();
+ }
+ bool isValid() const { return generator != nullptr; }
+
+ BytecodeGenerator *generator = nullptr;
+ int index = -1;
+ };
+
+ struct Jump {
+ Jump(BytecodeGenerator *generator, int instruction)
+ : generator(generator),
+ index(instruction)
+ { Q_ASSERT(generator && index != -1); }
+
+ ~Jump() {
+ Q_ASSERT(index == -1 || generator->instructions[index].linkedLabel != -1); // make sure link() got called
+ }
+
+ Jump(Jump &&j) {
+ std::swap(generator, j.generator);
+ std::swap(index, j.index);
+ }
+
+ BytecodeGenerator *generator = nullptr;
+ int index = -1;
+
+ void link() {
+ link(generator->label());
+ }
+ void link(Label l) {
+ Q_ASSERT(l.index >= 0);
+ Q_ASSERT(generator->instructions[index].linkedLabel == -1);
+ generator->instructions[index].linkedLabel = l.index;
+ }
+
+ private:
+ // make this type move-only:
+ Q_DISABLE_COPY(Jump)
+ // we never move-assign this type anywhere, so disable it:
+ Jump &operator=(Jump &&) = delete;
+ };
+
+ struct ExceptionHandler : public Label {
+ ExceptionHandler() = default;
+ ExceptionHandler(BytecodeGenerator *generator)
+ : Label(generator, LinkLater)
+ {
+ }
+ ~ExceptionHandler()
+ {
+ Q_ASSERT(!generator || generator->currentExceptionHandler != this);
+ }
+ bool isValid() const { return generator != nullptr; }
+ };
+
+ Label label() {
+ return Label(this, Label::LinkNow);
+ }
+
+ Label newLabel() {
+ return Label(this, Label::LinkLater);
+ }
+
+ ExceptionHandler newExceptionHandler() {
+ return ExceptionHandler(this);
+ }
+
+ template<int InstrT>
+ void addInstruction(const InstrData<InstrT> &data)
+ {
+ Instr genericInstr;
+ InstrMeta<InstrT>::setData(genericInstr, data);
+ addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr);
+ }
+
+ // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot
+ // be reused afterwards.
+ template<int InstrT>
+ void addTracingInstruction(InstrData<InstrT> data)
+ {
+ data.traceSlot = nextTraceInfo();
+ addInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jump()
+ {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
+ Instruction::Jump data;
+ return addJumpInstruction(data);
+QT_WARNING_POP
+ }
+
+ Q_REQUIRED_RESULT Jump jumpTrue()
+ {
+ return addTracingJumpInstruction(Instruction::JumpTrue());
+ }
+
+ Q_REQUIRED_RESULT Jump jumpFalse()
+ {
+ return addTracingJumpInstruction(Instruction::JumpFalse());
+ }
+
+ Q_REQUIRED_RESULT Jump jumpNotUndefined()
+ {
+ Instruction::JumpNotUndefined data;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpNoException()
+ {
+ Instruction::JumpNoException data;
+ return addJumpInstruction(data);
+ }
+
+ void jumpStrictEqual(const StackSlot &lhs, const Label &target)
+ {
+ Instruction::CmpStrictEqual cmp;
+ cmp.lhs = lhs;
+ addInstruction(std::move(cmp));
+ addTracingJumpInstruction(Instruction::JumpTrue()).link(target);
+ }
+
+ void jumpStrictNotEqual(const StackSlot &lhs, const Label &target)
+ {
+ Instruction::CmpStrictNotEqual cmp;
+ cmp.lhs = lhs;
+ addInstruction(std::move(cmp));
+ addTracingJumpInstruction(Instruction::JumpTrue()).link(target);
+ }
+
+ void setUnwindHandler(ExceptionHandler *handler)
+ {
+ currentExceptionHandler = handler;
+ Instruction::SetUnwindHandler data;
+ data.offset = 0;
+ if (!handler)
+ addInstruction(data);
+ else
+ addJumpInstruction(data).link(*handler);
+ }
+
+ void unwindToLabel(int level, const Label &target)
+ {
+ if (level) {
+ Instruction::UnwindToLabel unwind;
+ unwind.level = level;
+ addJumpInstruction(unwind).link(target);
+ } else {
+ jump().link(target);
+ }
+ }
+
+
+
+ void setLocation(const QQmlJS::AST::SourceLocation &loc);
+
+ ExceptionHandler *exceptionHandler() const {
+ return currentExceptionHandler;
+ }
+
+ int newRegister();
+ int newRegisterArray(int n);
+ int registerCount() const { return regCount; }
+ int currentRegister() const { return currentReg; }
+
+ void finalize(Compiler::Context *context);
+
+ template<int InstrT>
+ Jump addTracingJumpInstruction(InstrData<InstrT> &&data)
+ {
+ data.traceSlot = nextTraceInfo();
+ return addJumpInstruction(data);
+ }
+
+ template<int InstrT>
+ Jump addJumpInstruction(const InstrData<InstrT> &data)
+ {
+ Instr genericInstr;
+ InstrMeta<InstrT>::setData(genericInstr, data);
+ return Jump(this, addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr, offsetof(InstrData<InstrT>, offset)));
+ }
+
+ void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel)
+ {
+ if (jumpOnFalse)
+ addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel);
+ else
+ addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
+ }
+
+ void clearLastInstruction()
+ {
+ lastInstrType = -1;
+ }
+
+ TraceInfoCount nextTraceInfo()
+ {
+ // If tracing is disabled, use slot 0 to unconditionally store all trace info
+ if (nTraceInfos == CompiledData::Function::NoTracing())
+ return TraceInfoCount(0);
+ return nTraceInfos++;
+ }
+
+ void setTracing(bool onoff, int argumentCount)
+ {
+ if (onoff)
+ nTraceInfos = argumentCount;
+ else
+ nTraceInfos = CompiledData::Function::NoTracing();
+ }
+
+ TraceInfoCount traceInfoCount() const
+ {
+ return nTraceInfos;
+ }
+
+ void addLoopStart(const Label &start)
+ {
+ _labelInfos.push_back({ start.index });
+ }
+
+private:
+ friend struct Jump;
+ friend struct Label;
+ friend struct ExceptionHandler;
+
+ int addInstructionHelper(Moth::Instr::Type type, const Instr &i, int offsetOfOffset = -1);
+
+ struct I {
+ Moth::Instr::Type type;
+ short size;
+ uint position;
+ int line;
+ int offsetForJump;
+ int linkedLabel;
+ unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type
+ };
+
+ void compressInstructions();
+ void packInstruction(I &i);
+ void adjustJumpOffsets();
+
+ QVector<I> instructions;
+ QVector<int> labels;
+ ExceptionHandler *currentExceptionHandler = nullptr;
+ int regCount = 0;
+public:
+ int currentReg = 0;
+private:
+ int startLine = 0;
+ int currentLine = 0;
+ bool debugMode = false;
+
+ int lastInstrType = -1;
+ Moth::Instr lastInstr;
+
+ TraceInfoCount nTraceInfos = TraceInfoCount(0);
+
+ struct LabelInfo {
+ int labelIndex;
+ };
+ std::vector<LabelInfo> _labelInfos;
+};
+
+}
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
new file mode 100644
index 0000000000..f9f755b8c0
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include <private/qv4bytecodehandler_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace Moth;
+
+ByteCodeHandler::~ByteCodeHandler()
+{
+}
+
+#define DISPATCH_INSTRUCTION(name, nargs, ...) \
+ generate_##name( \
+ __VA_ARGS__ \
+ );
+
+#define DECODE_AND_DISPATCH(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ Q_UNUSED(base_ptr); \
+ _currentOffset = _nextOffset; \
+ _nextOffset = code - start; \
+ if (startInstruction(Instr::Type::instr) == ProcessInstruction) { \
+ INSTR_##instr(DISPATCH) \
+ endInstruction(Instr::Type::instr); \
+ } \
+ continue; \
+ }
+
+void ByteCodeHandler::decode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+
+ FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
+ }
+}
+
+#undef DECODE_AND_DISPATCH
+#undef DISPATCH_INSTRUCTION
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
new file mode 100644
index 0000000000..f1e7c99447
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QV4BYTECODEHANDLER_P_H
+#define QV4BYTECODEHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <private/qv4instr_moth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace Moth {
+
+#define BYTECODE_HANDLER_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(BYTECODE_HANDLER_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define BYTECODE_HANDLER_DEFINE_ARGS0()
+#define BYTECODE_HANDLER_DEFINE_ARGS1(arg) \
+ int arg
+#define BYTECODE_HANDLER_DEFINE_ARGS2(arg1, arg2) \
+ int arg1, \
+ int arg2
+#define BYTECODE_HANDLER_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1, \
+ int arg2, \
+ int arg3
+#define BYTECODE_HANDLER_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1, \
+ int arg2, \
+ int arg3, \
+ int arg4
+#define BYTECODE_HANDLER_DEFINE_ARGS5(arg1, arg2, arg3, arg4, arg5) \
+ int arg1, \
+ int arg2, \
+ int arg3, \
+ int arg4, \
+ int arg5
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
+ virtual void generate_##name( \
+ BYTECODE_HANDLER_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ ) = 0;
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
+ INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+class ByteCodeHandler
+{
+public:
+ virtual ~ByteCodeHandler();
+
+ void decode(const char *code, uint len);
+
+ int currentInstructionOffset() const { return _currentOffset; }
+ int nextInstructionOffset() const { return _nextOffset; }
+ int absoluteOffset(int relativeOffset) const
+ { return nextInstructionOffset() + relativeOffset; }
+
+protected:
+ FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+ enum Verdict { ProcessInstruction, SkipInstruction };
+ virtual Verdict startInstruction(Moth::Instr::Type instr) = 0;
+ virtual void endInstruction(Moth::Instr::Type instr) = 0;
+
+private:
+ int _currentOffset = 0;
+ int _nextOffset = 0;
+};
+
+} // Moth namespace
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // QV4BYTECODEHANDLER_P_H
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 693a4230ba..448fbff27b 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -39,61 +39,39 @@
#include "qv4codegen_p.h"
#include "qv4util_p.h"
-#include "qv4engine_p.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QStringList>
-#include <QtCore/QSet>
-#include <QtCore/QBuffer>
-#include <QtCore/QBitArray>
-#include <QtCore/QLinkedList>
#include <QtCore/QStack>
+#include <QScopeGuard>
#include <private/qqmljsast_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4compilercontrolflow_p.h>
+#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4compilerscanfunctions_p.h>
#ifndef V4_BOOTSTRAP
-#include <qv4context_p.h>
+# include <qqmlerror.h>
#endif
#include <cmath>
#include <iostream>
+static const bool disable_lookups = false;
+
#ifdef CONST
#undef CONST
#endif
+QT_USE_NAMESPACE
using namespace QV4;
-using namespace QQmlJS;
-using namespace AST;
-
-static inline void setLocation(IR::Stmt *s, const SourceLocation &loc)
-{
- if (s && loc.isValid())
- s->location = loc;
-}
-
-static bool cjumpCanHandle(IR::AluOp op)
-{
- switch (op) {
- case IR::OpIn:
- case IR::OpInstanceof:
- case IR::OpEqual:
- case IR::OpNotEqual:
- case IR::OpGe:
- case IR::OpGt:
- case IR::OpLe:
- case IR::OpLt:
- case IR::OpStrictEqual:
- case IR::OpStrictNotEqual:
- return true;
- default:
- return false;
- }
-}
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
-static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body,
- const SourceLocation &fallback)
+static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator,
+ const Statement *body, const SourceLocation &fallback)
{
switch (body->kind) {
// Statements where we might never execute the last line.
@@ -102,677 +80,296 @@ static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body,
case Statement::Kind_ForEachStatement:
case Statement::Kind_ForStatement:
case Statement::Kind_IfStatement:
- case Statement::Kind_LocalForEachStatement:
- case Statement::Kind_LocalForStatement:
case Statement::Kind_WhileStatement:
- setLocation(s, fallback);
+ bytecodeGenerator->setLocation(fallback);
break;
default:
- setLocation(s, body->lastSourceLocation());
+ bytecodeGenerator->setLocation(body->lastSourceLocation());
break;
}
}
-Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
- : _cg(cg)
- , _sourceCode(sourceCode)
- , _variableEnvironment(0)
- , _allowFuncDecls(true)
- , defaultProgramMode(defaultProgramMode)
-{
-}
-
-void Codegen::ScanFunctions::operator()(Node *node)
-{
- if (node)
- node->accept(this);
-}
-
-void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode)
-{
- Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode);
- if (!e->isStrict)
- e->isStrict = _cg->_strictMode;
- _envStack.append(e);
- _variableEnvironment = e;
-}
-
-void Codegen::ScanFunctions::leaveEnvironment()
-{
- _envStack.pop();
- _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top();
-}
-
-void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast)
-{
- for (SourceElements *it = ast; it; it = it->next) {
- if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) {
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) {
- if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
- // Use the source code, because the StringLiteral's
- // value might have escape sequences in it, which is not
- // allowed.
- if (strLit->literalToken.length < 2)
- continue;
- QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
- if (str == QLatin1String("use strict")) {
- _variableEnvironment->isStrict = true;
- } else {
- // TODO: give a warning.
- }
- continue;
- }
- }
- }
-
- break;
- }
-}
-
-void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
-{
- if (_variableEnvironment->isStrict) {
- if (name == QLatin1String("implements")
- || name == QLatin1String("interface")
- || name == QLatin1String("let")
- || name == QLatin1String("package")
- || name == QLatin1String("private")
- || name == QLatin1String("protected")
- || name == QLatin1String("public")
- || name == QLatin1String("static")
- || name == QLatin1String("yield")) {
- _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word"));
- }
- }
-}
-void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters)
-{
- while (parameters) {
- if (parameters->name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
- parameters = parameters->next;
- }
-}
-
-bool Codegen::ScanFunctions::visit(Program *ast)
-{
- enterEnvironment(ast, defaultProgramMode);
- checkDirectivePrologue(ast->elements);
- return true;
-}
-
-void Codegen::ScanFunctions::endVisit(Program *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(CallExpression *ast)
-{
- if (! _variableEnvironment->hasDirectEval) {
- if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
- if (id->name == QLatin1String("eval")) {
- if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown)
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed;
- _variableEnvironment->hasDirectEval = true;
- }
- }
- }
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(NewMemberExpression *ast)
-{
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(ArrayLiteral *ast)
-{
- int index = 0;
- for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next)
- ++index;
- ++index;
- }
- if (ast->elision) {
- for (Elision *elision = ast->elision->next; elision; elision = elision->next)
- ++index;
- }
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(VariableDeclaration *ast)
-{
- if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- checkName(ast->name, ast->identifierToken);
- if (ast->name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
- if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
- return false;
- }
- QString name = ast->name.toString();
- const Environment::Member *m = 0;
- if (_variableEnvironment->memberInfo(name, &m)) {
- if (m->isLexicallyScoped() || ast->isLexicallyScoped()) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
- return false;
- }
- }
- _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope);
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(IdentifierExpression *ast)
-{
- checkName(ast->name, ast->identifierToken);
- if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed;
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(ExpressionStatement *ast)
-{
- if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) {
- if (!_allowFuncDecls)
- _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
-
- enterFunction(expr, /*enterName*/ true);
- 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")) {
- _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token"));
- }
- }
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(FunctionExpression *ast)
-{
- enterFunction(ast, /*enterName*/ false);
- return true;
-}
-
-void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression)
-{
- if (_variableEnvironment->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"));
- enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression);
-}
-
-void Codegen::ScanFunctions::endVisit(FunctionExpression *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(ObjectLiteral *ast)
-{
- int argc = 0;
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString key = it->assignment->name->asString();
- if (QV4::String::toArrayIndex(key) != UINT_MAX)
- ++argc;
- ++argc;
- if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
- ++argc;
- }
- _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- Node::accept(ast->properties, this);
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(PropertyGetterSetter *ast)
-{
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false);
- return true;
-}
-
-void Codegen::ScanFunctions::endVisit(PropertyGetterSetter *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(FunctionDeclaration *ast)
-{
- enterFunction(ast, /*enterName*/ true, /*isExpression */false);
- return true;
-}
-
-void Codegen::ScanFunctions::endVisit(FunctionDeclaration *)
-{
- leaveEnvironment();
-}
-
-bool Codegen::ScanFunctions::visit(WithStatement *ast)
-{
- if (_variableEnvironment->isStrict) {
- _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
- return false;
- }
-
- return true;
-}
-
-bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) {
- {
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
- }
- Node::accept(ast->expression, this);
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(ForStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->condition, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(LocalForStatement *ast) {
- Node::accept(ast->declarations, this);
- Node::accept(ast->condition, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(ForEachStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) {
- Node::accept(ast->declaration, this);
- Node::accept(ast->expression, this);
-
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict);
- Node::accept(ast->statement, this);
-
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(ThisExpression *)
-{
- _variableEnvironment->usesThis = true;
- return false;
-}
-
-bool Codegen::ScanFunctions::visit(Block *ast) {
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls);
- Node::accept(ast->statements, this);
- return false;
-}
-
-void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression)
-{
- bool wasStrict = false;
- if (_variableEnvironment) {
- _variableEnvironment->hasNestedFunctions = true;
- // The identifier of a function expression cannot be referenced from the enclosing environment.
- if (expr)
- _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr);
- if (name == QLatin1String("arguments"))
- _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed;
- wasStrict = _variableEnvironment->isStrict;
- }
-
- enterEnvironment(ast, FunctionCode);
- checkForArguments(formals);
-
- _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty();
- _variableEnvironment->formals = formals;
-
- if (body)
- checkDirectivePrologue(body->elements);
-
- if (wasStrict || _variableEnvironment->isStrict) {
- QStringList args;
- for (FormalParameterList *it = formals; it; it = it->next) {
- QString arg = it->name.toString();
- if (args.contains(arg)) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
- return;
- }
- if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
- return;
- }
- args += arg;
- }
- }
-}
-
-
-Codegen::Codegen(bool strict)
- : _module(0)
- , _function(0)
- , _block(0)
- , _exitBlock(0)
- , _returnAddress(0)
- , _variableEnvironment(0)
- , _loop(0)
- , _labelledStatement(0)
- , _scopeAndFinally(0)
+Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
+ : _module(nullptr)
+ , _returnAddress(-1)
+ , _context(nullptr)
+ , _labelledStatement(nullptr)
+ , jsUnitGenerator(jsUnitGenerator)
, _strictMode(strict)
, _fileNameIsUrl(false)
, hasError(false)
{
-}
+ jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
+}
+
+const char *globalNames[] = {
+ "isNaN",
+ "parseFloat",
+ "String",
+ "EvalError",
+ "URIError",
+ "Math",
+ "encodeURIComponent",
+ "RangeError",
+ "eval",
+ "isFinite",
+ "ReferenceError",
+ "Infinity",
+ "Function",
+ "RegExp",
+ "Number",
+ "parseInt",
+ "Object",
+ "decodeURI",
+ "TypeError",
+ "Boolean",
+ "encodeURI",
+ "NaN",
+ "Error",
+ "decodeURIComponent",
+ "Date",
+ "Array",
+ "Symbol",
+ "escape",
+ "unescape",
+ "SyntaxError",
+ "undefined",
+ "JSON",
+ "ArrayBuffer",
+ "SharedArrayBuffer",
+ "DataView",
+ "Int8Array",
+ "Uint8Array",
+ "Uint8ClampedArray",
+ "Int16Array",
+ "Uint16Array",
+ "Int32Array",
+ "Uint32Array",
+ "Float32Array",
+ "Float64Array",
+ "WeakSet",
+ "Set",
+ "WeakMap",
+ "Map",
+ "Reflect",
+ "Proxy",
+ "Atomics",
+ "Promise",
+ nullptr
+};
void Codegen::generateFromProgram(const QString &fileName,
+ const QString &finalUrl,
const QString &sourceCode,
Program *node,
- QV4::IR::Module *module,
- CompilationMode mode,
- const QStringList &inheritedLocals)
+ Module *module,
+ ContextType contextType)
{
Q_ASSERT(node);
_module = module;
- _variableEnvironment = 0;
-
- _module->setFileName(fileName);
+ _context = nullptr;
+
+ // ### should be set on the module outside of this method
+ _module->fileName = fileName;
+ _module->finalUrl = finalUrl;
+
+ if (contextType == ContextType::ScriptImportedByQML) {
+ // the global object is frozen, so we know that members of it are
+ // pointing to the global object. This is important so that references
+ // to Math etc. do not go through the expensive path in the context wrapper
+ // that tries to see whether we have a matching type
+ //
+ // Since this can be called from the loader thread we can't get the list
+ // directly from the engine, so let's hardcode the most important ones here
+ for (const char **g = globalNames; *g != nullptr; ++g)
+ m_globalNames << QString::fromLatin1(*g);
+ }
- ScanFunctions scan(this, sourceCode, mode);
+ ScanFunctions scan(this, sourceCode, contextType);
scan(node);
- defineFunction(QStringLiteral("%entry"), node, 0, node->elements, inheritedLocals);
- qDeleteAll(_envMap);
- _envMap.clear();
-}
-
-void Codegen::generateFromFunctionExpression(const QString &fileName,
- const QString &sourceCode,
- AST::FunctionExpression *ast,
- QV4::IR::Module *module)
-{
- _module = module;
- _module->setFileName(fileName);
- _variableEnvironment = 0;
-
- ScanFunctions scan(this, sourceCode, GlobalCode);
- // fake a global environment
- scan.enterEnvironment(0, FunctionCode);
- scan(ast);
- scan.leaveEnvironment();
-
- defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
+ if (hasError)
+ return;
- qDeleteAll(_envMap);
- _envMap.clear();
+ defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements);
}
-
-void Codegen::enterEnvironment(Node *node)
+void Codegen::generateFromModule(const QString &fileName,
+ const QString &finalUrl,
+ const QString &sourceCode,
+ ESModule *node,
+ Module *module)
{
- _variableEnvironment = _envMap.value(node);
- Q_ASSERT(_variableEnvironment);
-}
+ Q_ASSERT(node);
-void Codegen::leaveEnvironment()
-{
- Q_ASSERT(_variableEnvironment);
- _variableEnvironment = _variableEnvironment->parent;
-}
+ _module = module;
+ _context = nullptr;
-void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock)
-{
- _loop = new Loop(node, breakBlock, continueBlock, _loop);
- _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement
- _loop->scopeAndFinally = _scopeAndFinally;
- _labelledStatement = 0;
-}
+ // ### should be set on the module outside of this method
+ _module->fileName = fileName;
+ _module->finalUrl = finalUrl;
-void Codegen::leaveLoop()
-{
- Loop *current = _loop;
- _loop = _loop->parent;
- delete current;
-}
+ ScanFunctions scan(this, sourceCode, ContextType::ESModule);
+ scan(node);
-IR::Expr *Codegen::member(IR::Expr *base, const QString *name)
-{
if (hasError)
- return 0;
-
- if (base->asTemp() || base->asArgLocal())
- return _block->MEMBER(base, name);
- else {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), base);
- return _block->MEMBER(_block->TEMP(t), name);
- }
-}
+ return;
-IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index)
-{
- if (hasError)
- return 0;
+ {
+ Compiler::Context *moduleContext = _module->contextMap.value(node);
+ for (const auto &entry: moduleContext->exportEntries) {
+ if (entry.moduleRequest.isEmpty()) {
+ // ### check against imported bound names
+ _module->localExportEntries << entry;
+ } else if (entry.importName == QLatin1Char('*')) {
+ _module->starExportEntries << entry;
+ } else {
+ _module->indirectExportEntries << entry;
+ }
+ }
+ _module->importEntries = moduleContext->importEntries;
- if (! base->asTemp() && !base->asArgLocal()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), base);
- base = _block->TEMP(t);
+ _module->moduleRequests = std::move(moduleContext->moduleRequests);
+ _module->moduleRequests.removeDuplicates();
}
- if (! index->asTemp() && !index->asArgLocal() && !index->asConst()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), index);
- index = _block->TEMP(t);
- }
+ std::sort(_module->localExportEntries.begin(), _module->localExportEntries.end(), ExportEntry::lessThan);
+ std::sort(_module->starExportEntries.begin(), _module->starExportEntries.end(), ExportEntry::lessThan);
+ std::sort(_module->indirectExportEntries.begin(), _module->indirectExportEntries.end(), ExportEntry::lessThan);
- Q_ASSERT(base->asTemp() || base->asArgLocal());
- Q_ASSERT(index->asTemp() || index->asArgLocal() || index->asConst());
- return _block->SUBSCRIPT(base, index);
+ defineFunction(QStringLiteral("%entry"), node, nullptr, node->body);
}
-IR::Expr *Codegen::argument(IR::Expr *expr)
+void Codegen::enterContext(Node *node)
{
- if (expr && !expr->asTemp()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
- }
- return expr;
+ _context = _module->contextMap.value(node);
+ Q_ASSERT(_context);
}
-// keeps references alive, converts other expressions to temps
-IR::Expr *Codegen::reference(IR::Expr *expr)
+int Codegen::leaveContext()
{
- if (hasError)
- return 0;
+ Q_ASSERT(_context);
+ int functionIndex = _context->functionIndex;
+ _context = _context->parent;
+ return functionIndex;
+}
- if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
- }
- return expr;
+Context *Codegen::enterBlock(Node *node)
+{
+ enterContext(node);
+ return _context;
}
-IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc)
+Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
- return 0;
-
- Q_ASSERT(op != IR::OpIncrement);
- Q_ASSERT(op != IR::OpDecrement);
+ return _expr.result();
- if (IR::Const *c = expr->asConst()) {
- if (c->type == IR::NumberType) {
+ if (expr.isConstant()) {
+ auto v = Value::fromReturnedValue(expr.constant);
+ if (v.isNumber()) {
switch (op) {
- case IR::OpNot:
- return _block->CONST(IR::BoolType, !c->value);
- case IR::OpUMinus:
- return _block->CONST(IR::NumberType, -c->value);
- case IR::OpUPlus:
+ case Not:
+ return Reference::fromConst(this, Encode(!v.toBoolean()));
+ case UMinus:
+ return Reference::fromConst(this, Runtime::UMinus::call(v));
+ case UPlus:
return expr;
- case IR::OpCompl:
- return _block->CONST(IR::NumberType, ~QV4::Primitive::toInt32(c->value));
- case IR::OpIncrement:
- return _block->CONST(IR::NumberType, c->value + 1);
- case IR::OpDecrement:
- return _block->CONST(IR::NumberType, c->value - 1);
+ case Compl:
+ return Reference::fromConst(this, Encode((int)~v.toInt32()));
default:
break;
}
}
}
- if (!expr->asTemp() && !expr->asArgLocal()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), expr), loc);
- expr = _block->TEMP(t);
- }
- Q_ASSERT(expr->asTemp() || expr->asArgLocal());
- return _block->UNOP(op, expr);
-}
-
-IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AST::SourceLocation &loc)
-{
- if (hasError)
- return 0;
-
- if (IR::Const *c1 = left->asConst()) {
- if (IR::Const *c2 = right->asConst()) {
- if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) {
- switch (op) {
- case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value);
- case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0);
- case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value));
- case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value));
- case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value));
- case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value);
- case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value);
- case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value);
- case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value);
- case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value);
- case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value);
- case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value);
- case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value);
- case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value);
- case IR::OpLShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) << (QV4::Primitive::toUInt32(c2->value) & 0x1f));
- case IR::OpMod: return _block->CONST(IR::NumberType, std::fmod(c1->value, c2->value));
- case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value);
- case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value);
- case IR::OpRShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f));
- case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value);
- case IR::OpURShift: return _block->CONST(IR::NumberType,QV4::Primitive::toUInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f));
-
- case IR::OpInstanceof:
- case IR::OpIn:
- break;
- case IR::OpIfTrue: // unary ops
- case IR::OpNot:
- case IR::OpUMinus:
- case IR::OpUPlus:
- case IR::OpCompl:
- case IR::OpIncrement:
- case IR::OpDecrement:
- case IR::OpInvalid:
- break;
- }
- }
- }
- } else if (op == IR::OpAdd) {
- if (IR::String *s1 = left->asString()) {
- if (IR::String *s2 = right->asString()) {
- return _block->STRING(_function->newString(*s1->value + *s2->value));
- }
- }
+ switch (op) {
+ case UMinus: {
+ expr.loadInAccumulator();
+ Instruction::UMinus uminus = {};
+ bytecodeGenerator->addTracingInstruction(uminus);
+ return Reference::fromAccumulator(this);
}
-
- if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), left), loc);
- left = _block->TEMP(t);
+ case UPlus: {
+ expr.loadInAccumulator();
+ Instruction::UPlus uplus;
+ bytecodeGenerator->addInstruction(uplus);
+ return Reference::fromAccumulator(this);
}
-
- if (!right->asTemp() && !right->asArgLocal() && !right->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), right), loc);
- right = _block->TEMP(t);
+ case Not: {
+ expr.loadInAccumulator();
+ Instruction::UNot unot;
+ bytecodeGenerator->addInstruction(unot);
+ return Reference::fromAccumulator(this);
}
-
- Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst());
- Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst());
-
- return _block->BINOP(op, left, right);
-}
-
-IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args)
-{
- if (hasError)
- return 0;
- base = reference(base);
- return _block->CALL(base, args);
-}
-
-IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op)
-{
- if (hasError)
- return 0;
-
- Q_ASSERT(target->isLValue());
-
- if (op != IR::OpInvalid) {
- return move(target, binop(op, target, source));
+ case Compl: {
+ expr.loadInAccumulator();
+ Instruction::UCompl ucompl;
+ bytecodeGenerator->addInstruction(ucompl);
+ return Reference::fromAccumulator(this);
}
-
- if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) {
- unsigned t = _block->newTemp();
- _block->MOVE(_block->TEMP(t), source);
- source = _block->TEMP(t);
+ case PostIncrement:
+ if (!_expr.accept(nx) || requiresReturnValue) {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::UPlus uplus;
+ bytecodeGenerator->addInstruction(uplus);
+ Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
+ Instruction::Increment inc = {};
+ bytecodeGenerator->addTracingInstruction(inc);
+ e.storeConsumeAccumulator();
+ return originalValue;
+ } else {
+ // intentionally fall-through: the result is never used, so it's equivalent to
+ // "expr += 1", which is what a pre-increment does as well.
+ Q_FALLTHROUGH();
+ }
+ case PreIncrement: {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::Increment inc = {};
+ bytecodeGenerator->addTracingInstruction(inc);
+ if (_expr.accept(nx))
+ return e.storeConsumeAccumulator();
+ else
+ return e.storeRetainAccumulator();
+ }
+ case PostDecrement:
+ if (!_expr.accept(nx) || requiresReturnValue) {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::UPlus uplus;
+ bytecodeGenerator->addInstruction(uplus);
+ Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
+ Instruction::Decrement dec = {};
+ bytecodeGenerator->addTracingInstruction(dec);
+ e.storeConsumeAccumulator();
+ return originalValue;
+ } else {
+ // intentionally fall-through: the result is never used, so it's equivalent to
+ // "expr -= 1", which is what a pre-decrement does as well.
+ Q_FALLTHROUGH();
+ }
+ case PreDecrement: {
+ Reference e = expr.asLValue();
+ e.loadInAccumulator();
+ Instruction::Decrement dec = {};
+ bytecodeGenerator->addTracingInstruction(dec);
+ if (_expr.accept(nx))
+ return e.storeConsumeAccumulator();
+ else
+ return e.storeRetainAccumulator();
}
- if (source->asConst() && !target->asTemp() && !target->asArgLocal()) {
- unsigned t = _block->newTemp();
- _block->MOVE(_block->TEMP(t), source);
- source = _block->TEMP(t);
}
- return _block->MOVE(target, source);
+ Q_UNREACHABLE();
}
-IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
+void Codegen::addCJump()
{
- if (hasError)
- return 0;
-
- if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), cond);
- cond = _block->TEMP(t);
- }
- return _block->CJUMP(cond, iftrue, iffalse);
+ bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(),
+ _expr.iftrue(), _expr.iffalse());
}
void Codegen::accept(Node *node)
@@ -786,8 +383,15 @@ void Codegen::accept(Node *node)
void Codegen::statement(Statement *ast)
{
- _block->nextLocation = ast->firstSourceLocation();
+ RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
+ RegisterScope scope(this);
+
+ bytecodeGenerator->setLocation(ast->firstSourceLocation());
+
+ VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
+ qSwap(_volatileMemoryLocations, vLocs);
}
void Codegen::statement(ExpressionNode *ast)
@@ -795,296 +399,685 @@ void Codegen::statement(ExpressionNode *ast)
if (! ast) {
return;
} else {
+ RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
+ RegisterScope scope(this);
+
Result r(nx);
qSwap(_expr, r);
+ VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
+ qSwap(_volatileMemoryLocations, vLocs);
+
accept(ast);
+
+ qSwap(_volatileMemoryLocations, vLocs);
+ qSwap(_expr, r);
+
if (hasError)
return;
- qSwap(_expr, r);
- if (r.format == ex) {
- if (r->asCall()) {
- _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..))
- } else if (r->asTemp() || r->asArgLocal()) {
- // there is nothing to do
- } else {
- unsigned t = _block->newTemp();
- move(_block->TEMP(t), *r);
- }
- }
+ if (r.result().loadTriggersSideEffect())
+ r.result().loadInAccumulator(); // triggers side effects
}
}
-void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
+void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
+ const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition)
{
- if (ast) {
- Result r(iftrue, iffalse);
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
- if (r.format == ex) {
- setLocation(cjump(*r, r.iftrue, r.iffalse), ast->firstSourceLocation());
- }
+ if (hasError)
+ return;
+
+ if (!ast)
+ return;
+
+ RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
+ Result r(iftrue, iffalse, trueBlockFollowsCondition);
+ qSwap(_expr, r);
+ accept(ast);
+ qSwap(_expr, r);
+
+ if (hasError)
+ return;
+
+ if (r.format() == ex) {
+ Q_ASSERT(iftrue == r.iftrue());
+ Q_ASSERT(iffalse == r.iffalse());
+ Q_ASSERT(r.result().isValid());
+ bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ r.result().loadInAccumulator();
+ if (r.trueBlockFollowsCondition())
+ bytecodeGenerator->jumpFalse().link(*r.iffalse());
+ else
+ bytecodeGenerator->jumpTrue().link(*r.iftrue());
}
}
-Codegen::Result Codegen::expression(ExpressionNode *ast)
+Codegen::Reference Codegen::expression(ExpressionNode *ast)
{
+ RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
Result r;
if (ast) {
qSwap(_expr, r);
accept(ast);
qSwap(_expr, r);
}
- return r;
+ return r.result();
}
-Codegen::Result Codegen::sourceElement(SourceElement *ast)
+void Codegen::program(Program *ast)
{
- Result r(nx);
if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
+ statementList(ast->statements);
}
- return r;
}
-Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast)
+enum class CompletionState {
+ Empty,
+ EmptyAbrupt,
+ NonEmpty
+};
+
+static CompletionState completionState(StatementList *list)
+{
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return CompletionState::EmptyAbrupt;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableDeclaration ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState subState = completionState(static_cast<Block *>(it->statement)->statements);
+ if (subState != CompletionState::Empty)
+ return subState;
+ continue;
+ }
+ return CompletionState::NonEmpty;
+ }
+ return CompletionState::Empty;
+}
+
+static Node *completionStatement(StatementList *list)
+{
+ Node *completionStatement = nullptr;
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return completionStatement;
+ if (it->statement->kind == Statement::Kind_ThrowStatement ||
+ it->statement->kind == Statement::Kind_ReturnStatement)
+ return it->statement;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableStatement ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState state = completionState(static_cast<Block *>(it->statement)->statements);
+ switch (state) {
+ case CompletionState::Empty:
+ continue;
+ case CompletionState::EmptyAbrupt:
+ return it->statement;
+ case CompletionState::NonEmpty:
+ break;
+ }
+ }
+ completionStatement = it->statement;
+ }
+ return completionStatement;
+}
+
+void Codegen::statementList(StatementList *ast)
{
- UiMember m;
- if (ast) {
- qSwap(_uiMember, m);
- accept(ast);
- qSwap(_uiMember, m);
+ if (!ast)
+ return;
+
+ bool _requiresReturnValue = requiresReturnValue;
+ // ### the next line is pessimizing a bit too much, as there are many cases, where the complietion from the break
+ // statement will not be used, but it's at least spec compliant
+ if (!controlFlow || !controlFlow->hasLoop())
+ requiresReturnValue = false;
+
+ Node *needsCompletion = nullptr;
+
+ if (_requiresReturnValue && !requiresReturnValue)
+ needsCompletion = completionStatement(ast);
+
+ if (requiresReturnValue && !needsCompletion && !insideSwitch) {
+ // break or continue is the first real statement, set the return value to undefined
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+ }
+
+ bool _insideSwitch = insideSwitch;
+ insideSwitch = false;
+
+ for (StatementList *it = ast; it; it = it->next) {
+ if (it->statement == needsCompletion)
+ requiresReturnValue = true;
+ if (Statement *s = it->statement->statementCast())
+ statement(s);
+ else
+ statement(static_cast<ExpressionNode *>(it->statement));
+ if (it->statement == needsCompletion)
+ requiresReturnValue = false;
+ if (it->statement->kind == Statement::Kind_ThrowStatement ||
+ it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement ||
+ it->statement->kind == Statement::Kind_ReturnStatement)
+ // any code after those statements is unreachable
+ break;
}
- return m;
+ requiresReturnValue = _requiresReturnValue;
+ insideSwitch = _insideSwitch;
}
-void Codegen::functionBody(FunctionBody *ast)
+void Codegen::variableDeclaration(PatternElement *ast)
{
- if (ast)
- sourceElements(ast->elements);
+ TailCallBlocker blockTailCalls(this);
+ RegisterScope scope(this);
+
+ if (!ast->initializer) {
+ if (ast->isLexicallyScoped()) {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ Reference varToStore = targetForPatternElement(ast);
+ varToStore.storeConsumeAccumulator();
+ }
+ return;
+ }
+ initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
}
-void Codegen::program(Program *ast)
+void Codegen::variableDeclarationList(VariableDeclarationList *ast)
{
- if (ast) {
- sourceElements(ast->elements);
+ for (VariableDeclarationList *it = ast; it; it = it->next) {
+ variableDeclaration(it->declaration);
}
}
-void Codegen::sourceElements(SourceElements *ast)
+Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
{
- for (SourceElements *it = ast; it; it = it->next) {
- sourceElement(it->element);
- if (hasError)
- return;
+ if (!p->bindingIdentifier.isNull())
+ return referenceForName(p->bindingIdentifier.toString(), true, p->firstSourceLocation());
+ if (!p->bindingTarget || p->destructuringPattern())
+ return Codegen::Reference::fromStackSlot(this);
+ Reference lhs = expression(p->bindingTarget);
+ if (hasError)
+ return lhs;
+ if (!lhs.isLValue()) {
+ throwReferenceError(p->bindingTarget->firstSourceLocation(), QStringLiteral("Binding target is not a reference."));
+ return lhs;
}
+ lhs = lhs.asLValue();
+ return lhs;
}
-void Codegen::variableDeclaration(VariableDeclaration *ast)
+void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition)
{
- IR::Expr *initializer = 0;
- if (!ast->expression)
- return;
- Result expr = expression(ast->expression);
+ Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement);
+ RegisterScope scope(this);
+ Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base;
+ Reference varToStore = targetForPatternElement(e);
+ if (isDefinition)
+ varToStore.isReferenceToConst = false;
if (hasError)
return;
- Q_ASSERT(expr.code);
- initializer = *expr;
+ if (e->initializer) {
+ if (!baseRef.isValid()) {
+ // assignment
+ Reference expr = expression(e->initializer);
+ if (hasError)
+ return;
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ } else if (baseRef == varToStore) {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ jump.link();
+ } else {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ jump.link();
+ varToStore.storeConsumeAccumulator();
+ }
+ } else if (baseRef != varToStore && baseRef.isValid()) {
+ baseRef.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ }
+ Pattern *p = e->destructuringPattern();
+ if (!p)
+ return;
- IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine,
- ast->identifierToken.startColumn);
+ if (!varToStore.isStackSlot())
+ varToStore = varToStore.storeOnStack();
+ if (PatternElementList *l = e->elementList()) {
+ destructureElementList(varToStore, l, isDefinition);
+ } else if (PatternPropertyList *p = e->propertyList()) {
+ destructurePropertyList(varToStore, p, isDefinition);
+ } else if (e->bindingTarget) {
+ // empty binding pattern. For spec compatibility, try to coerce the argument to an object
+ varToStore.loadInAccumulator();
+ Instruction::ToObject toObject;
+ bytecodeGenerator->addInstruction(toObject);
+ return;
+ }
+}
- if (lhs->asArgLocal()) {
- move(lhs, initializer);
+Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name)
+{
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name);
+ Reference property;
+ if (cname) {
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return Reference();
+ computedName = computedName.storeOnStack();
+ property = Reference::fromSubscript(object, computedName).asLValue();
} else {
- int initialized = _block->newTemp();
- move(_block->TEMP(initialized), initializer);
- move(lhs, _block->TEMP(initialized));
+ QString propertyName = name->asString();
+ property = Reference::fromMember(object, propertyName);
}
+ return property;
}
-void Codegen::variableDeclarationList(VariableDeclarationList *ast)
+void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition)
{
- for (VariableDeclarationList *it = ast; it; it = it->next) {
- variableDeclaration(it->declaration);
+ RegisterScope scope(this);
+
+ object.loadInAccumulator();
+ Instruction::ThrowOnNullOrUndefined t;
+ bytecodeGenerator->addInstruction(t);
+
+ for (PatternPropertyList *it = bindingList; it; it = it->next) {
+ PatternProperty *p = it->property;
+ RegisterScope scope(this);
+ Reference property = referenceForPropertyName(object, p->name);
+ if (hasError)
+ return;
+ initializeAndDestructureBindingElement(p, property, isDefinition);
+ if (hasError)
+ return;
}
}
+void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition)
+{
+ 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());
+
+ array.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = static_cast<int>(AST::ForEachType::Of);
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+
+ {
+ auto cleanup = [this, iterator, iteratorDone]() {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ };
+
+ ControlFlowUnwindCleanup flow(this, cleanup);
+
+ 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());
+ bytecodeGenerator->addInstruction(Instruction::DestructureRestElement());
+ 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;
+ }
+ }
+ }
+}
+
+void Codegen::destructurePattern(Pattern *p, const Reference &rhs)
+{
+ RegisterScope scope(this);
+ if (auto *o = AST::cast<ObjectPattern *>(p))
+ destructurePropertyList(rhs, o->properties);
+ else if (auto *a = AST::cast<ArrayPattern *>(p))
+ destructureElementList(rhs, a->elements);
+ else
+ Q_UNREACHABLE();
+}
+
bool Codegen::visit(ArgumentList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(CaseBlock *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(CaseClause *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(CaseClauses *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Catch *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(DefaultClause *)
{
- Q_ASSERT(!"unreachable");
- return false;
-}
-
-bool Codegen::visit(ElementList *)
-{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Elision *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(Finally *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(FormalParameterList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(FunctionBody *)
+bool Codegen::visit(Program *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(Program *)
+bool Codegen::visit(PatternElement *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyAssignmentList *)
+bool Codegen::visit(PatternElementList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyNameAndValue *)
+bool Codegen::visit(PatternProperty *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyGetterSetter *)
+bool Codegen::visit(PatternPropertyList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(SourceElements *)
+bool Codegen::visit(ExportDeclaration *ast)
{
- Q_ASSERT(!"unreachable");
+ if (!ast->exportDefault)
+ return true;
+
+ TailCallBlocker blockTailCalls(this);
+ Reference exportedValue;
+
+ if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
+ Result r;
+ qSwap(_expr, r);
+ visit(static_cast<FunctionExpression*>(fdecl));
+ qSwap(_expr, r);
+ exportedValue = r.result();
+ } else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
+ Result r;
+ qSwap(_expr, r);
+ visit(static_cast<ClassExpression*>(classDecl));
+ qSwap(_expr, r);
+ exportedValue = r.result();
+ } else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
+ exportedValue = expression(expr);
+ }
+
+ exportedValue.loadInAccumulator();
+
+ const int defaultExportIndex = _context->locals.indexOf(_context->localNameForDefaultExport);
+ Q_ASSERT(defaultExportIndex != -1);
+ Reference defaultExportSlot = Reference::fromScopedLocal(this, defaultExportIndex, /*scope*/0);
+ defaultExportSlot.storeConsumeAccumulator();
+
return false;
}
bool Codegen::visit(StatementList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiArrayMemberList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiImport *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiHeaderItemList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiPragma *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiObjectInitializer *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiObjectMemberList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiParameterList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiProgram *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
bool Codegen::visit(UiQualifiedId *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(UiQualifiedPragmaId *)
+bool Codegen::visit(VariableDeclarationList *)
{
- Q_ASSERT(!"unreachable");
+ Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(VariableDeclaration *)
+bool Codegen::visit(ClassExpression *ast)
{
- Q_ASSERT(!"unreachable");
+ TailCallBlocker blockTailCalls(this);
+
+ Compiler::Class jsClass;
+ jsClass.nameIndex = registerString(ast->name.toString());
+
+ ClassElementList *constructor = nullptr;
+ int nComputedNames = 0;
+ int nStaticComputedNames = 0;
+
+ RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ PatternProperty *p = member->property;
+ FunctionExpression *f = p->initializer->asFunctionDefinition();
+ Q_ASSERT(f);
+ AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name);
+ if (cname) {
+ ++nComputedNames;
+ if (member->isStatic)
+ ++nStaticComputedNames;
+ }
+ QString name = p->name->asString();
+ uint nameIndex = cname ? UINT_MAX : registerString(name);
+ Compiler::Class::Method::Type type = Compiler::Class::Method::Regular;
+ if (p->type == PatternProperty::Getter)
+ type = Compiler::Class::Method::Getter;
+ else if (p->type == PatternProperty::Setter)
+ type = Compiler::Class::Method::Setter;
+ Compiler::Class::Method m{ nameIndex, type, static_cast<uint>(defineFunction(name, f, f->formals, f->body)) };
+
+ if (member->isStatic) {
+ if (name == QStringLiteral("prototype")) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a static method named 'prototype'."));
+ return false;
+ }
+ jsClass.staticMethods << m;
+ } else {
+ if (name == QStringLiteral("constructor")) {
+ if (constructor) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class."));
+ return false;
+ }
+ if (m.type != Compiler::Class::Method::Regular) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'."));
+ return false;
+ }
+ constructor = member;
+ jsClass.constructorIndex = m.functionIndex;
+ continue;
+ }
+
+ jsClass.methods << m;
+ }
+ }
+
+ int classIndex = _module->classes.size();
+ _module->classes.append(jsClass);
+
+ Reference heritage = Reference::fromStackSlot(this);
+ if (ast->heritage) {
+ bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation());
+ Reference r = expression(ast->heritage);
+ if (hasError)
+ return false;
+ r.storeOnStack(heritage.stackSlot());
+ } else {
+ Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator();
+ heritage.storeConsumeAccumulator();
+ }
+
+ int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0;
+ int currentStaticName = computedNames;
+ int currentNonStaticName = computedNames + nStaticComputedNames;
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name);
+ if (!cname)
+ continue;
+ RegisterScope scope(this);
+ bytecodeGenerator->setLocation(cname->firstSourceLocation());
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return false;
+ computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++);
+ }
+
+ Instruction::CreateClass createClass;
+ createClass.classIndex = classIndex;
+ createClass.heritage = heritage.stackSlot();
+ createClass.computedNames = computedNames;
+
+ bytecodeGenerator->addInstruction(createClass);
+
+ if (!ast->name.isEmpty()) {
+ Reference ctor = referenceForName(ast->name.toString(), true);
+ ctor.isReferenceToConst = false; // this is the definition
+ (void) ctor.storeRetainAccumulator();
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-bool Codegen::visit(VariableDeclarationList *)
+bool Codegen::visit(ClassDeclaration *ast)
{
- Q_ASSERT(!"unreachable");
+ TailCallBlocker blockTailCalls(this);
+ Reference outerVar = referenceForName(ast->name.toString(), true);
+ visit(static_cast<ClassExpression *>(ast));
+ (void) outerVar.storeRetainAccumulator();
return false;
}
@@ -1093,64 +1086,168 @@ bool Codegen::visit(Expression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
statement(ast->left);
+ blockTailCalls.unblock();
accept(ast->right);
return false;
}
-bool Codegen::visit(ArrayLiteral *ast)
+bool Codegen::visit(ArrayPattern *ast)
{
if (hasError)
return false;
- IR::ExprList *args = 0;
- IR::ExprList *current = 0;
- for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next) {
- IR::ExprList *arg = _function->New<IR::ExprList>();
- if (!current) {
- args = arg;
+ TailCallBlocker blockTailCalls(this);
+
+ PatternElementList *it = ast->elements;
+
+ int argc = 0;
+ {
+ RegisterScope scope(this);
+
+ int args = -1;
+ auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ if (!arg) {
+ auto c = Reference::fromConst(this, Value::emptyValue().asReturnedValue());
+ (void) c.storeOnStack(temp);
} else {
- current->next = arg;
+ RegisterScope scope(this);
+ Reference r = expression(arg);
+ if (hasError)
+ return;
+ (void) r.storeOnStack(temp);
}
- current = arg;
- current->expr = _block->CONST(IR::MissingType, 0);
- }
- Result expr = expression(it->expression);
- if (hasError)
- return false;
+ ++argc;
+ };
- IR::ExprList *arg = _function->New<IR::ExprList>();
- if (!current) {
- args = arg;
- } else {
- current->next = arg;
+ for (; it; it = it->next) {
+ PatternElement *e = it->element;
+ if (e && e->type == PatternElement::SpreadElement)
+ break;
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ push(nullptr);
+
+ if (!e)
+ continue;
+
+ push(e->initializer);
+ if (hasError)
+ return false;
}
- current = arg;
- IR::Expr *exp = *expr;
- if (exp->asTemp() || expr->asArgLocal() || exp->asConst()) {
- current->expr = exp;
- } else {
- unsigned value = _block->newTemp();
- move(_block->TEMP(value), exp);
- current->expr = _block->TEMP(value);
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
}
+
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+ }
+
+ if (!it) {
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
}
- for (Elision *elision = ast->elision; elision; elision = elision->next) {
- IR::ExprList *arg = _function->New<IR::ExprList>();
- if (!current) {
- args = arg;
+ Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
+
+ RegisterScope scope(this);
+ Reference array = Reference::fromStackSlot(this);
+ array.storeConsumeAccumulator();
+ Reference index = Reference::storeConstOnStack(this, Encode(argc));
+
+ auto pushAccumulator = [&]() {
+ Reference slot = Reference::fromSubscript(array, index);
+ slot.storeConsumeAccumulator();
+
+ index.loadInAccumulator();
+ Instruction::Increment inc = {};
+ bytecodeGenerator->addTracingInstruction(inc);
+ index.storeConsumeAccumulator();
+ };
+
+ while (it) {
+ for (Elision *elision = it->elision; elision; elision = elision->next) {
+ Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator();
+ pushAccumulator();
+ }
+
+ if (!it->element) {
+ it = it->next;
+ continue;
+ }
+
+ // handle spread element
+ if (it->element->type == PatternElement::SpreadElement) {
+ 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.
+ // This block should define a temporal dead zone for those variables, which is not yet implemented.
+ {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = static_cast<int>(AST::ForEachType::Of);
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+
+ {
+ auto cleanup = [this, iterator, iteratorDone]() {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ };
+ ControlFlowLoop flow(this, &end, &in, cleanup);
+
+ in.link();
+ bytecodeGenerator->addLoopStart(in);
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ next.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end);
+
+ lhsValue.loadInAccumulator();
+ pushAccumulator();
+
+ bytecodeGenerator->jump().link(in);
+ end.link();
+ }
} else {
- current->next = arg;
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ pushAccumulator();
}
- current = arg;
- current->expr = _block->CONST(IR::MissingType, 0);
+
+ it = it->next;
}
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args));
- _expr.code = _block->TEMP(t);
+ array.loadInAccumulator();
+ _expr.setResult(Reference::fromAccumulator(this));
+
return false;
}
@@ -1159,29 +1256,52 @@ bool Codegen::visit(ArrayMemberExpression *ast)
if (hasError)
return false;
- Result base = expression(ast->base);
- Result index = expression(ast->expression);
+ TailCallBlocker blockTailCalls(this);
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ if (base.isSuper()) {
+ Reference index = expression(ast->expression).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(index));
+ return false;
+ }
+ base = base.storeOnStack();
+ if (hasError)
+ return false;
+ if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
+ QString s = str->value.toString();
+ uint arrayIndex = QV4::String::toArrayIndex(s);
+ if (arrayIndex == UINT_MAX) {
+ _expr.setResult(Reference::fromMember(base, str->value.toString()));
+ return false;
+ }
+ Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
+ _expr.setResult(Reference::fromSubscript(base, index));
+ return false;
+ }
+ Reference index = expression(ast->expression);
if (hasError)
return false;
- _expr.code = subscript(*base, *index);
+ _expr.setResult(Reference::fromSubscript(base, index));
return false;
}
-static IR::AluOp baseOp(int op)
+static QSOperator::Op baseOp(int op)
{
switch ((QSOperator::Op) op) {
- case QSOperator::InplaceAnd: return IR::OpBitAnd;
- case QSOperator::InplaceSub: return IR::OpSub;
- case QSOperator::InplaceDiv: return IR::OpDiv;
- case QSOperator::InplaceAdd: return IR::OpAdd;
- case QSOperator::InplaceLeftShift: return IR::OpLShift;
- case QSOperator::InplaceMod: return IR::OpMod;
- case QSOperator::InplaceMul: return IR::OpMul;
- case QSOperator::InplaceOr: return IR::OpBitOr;
- case QSOperator::InplaceRightShift: return IR::OpRShift;
- case QSOperator::InplaceURightShift: return IR::OpURShift;
- case QSOperator::InplaceXor: return IR::OpBitXor;
- default: return IR::OpInvalid;
+ case QSOperator::InplaceAnd: return QSOperator::BitAnd;
+ case QSOperator::InplaceSub: return QSOperator::Sub;
+ case QSOperator::InplaceDiv: return QSOperator::Div;
+ case QSOperator::InplaceAdd: return QSOperator::Add;
+ case QSOperator::InplaceLeftShift: return QSOperator::LShift;
+ case QSOperator::InplaceMod: return QSOperator::Mod;
+ case QSOperator::InplaceExp: return QSOperator::Exp;
+ case QSOperator::InplaceMul: return QSOperator::Mul;
+ case QSOperator::InplaceOr: return QSOperator::BitOr;
+ case QSOperator::InplaceRightShift: return QSOperator::RShift;
+ case QSOperator::InplaceURightShift: return QSOperator::URShift;
+ case QSOperator::InplaceXor: return QSOperator::BitXor;
+ default: return QSOperator::Invalid;
}
}
@@ -1190,100 +1310,124 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
+
if (ast->op == QSOperator::And) {
if (_expr.accept(cx)) {
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- condition(ast->left, iftrue, _expr.iffalse);
- _block = iftrue;
- condition(ast->right, _expr.iftrue, _expr.iffalse);
+ auto iftrue = bytecodeGenerator->newLabel();
+ condition(ast->left, &iftrue, _expr.iffalse(), true);
+ iftrue.link();
+ blockTailCalls.unblock();
+ condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
} else {
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
-
- const unsigned r = _block->newTemp();
+ auto iftrue = bytecodeGenerator->newLabel();
+ auto endif = bytecodeGenerator->newLabel();
- Result lhs = expression(ast->left);
+ Reference left = expression(ast->left);
if (hasError)
return false;
- move(_block->TEMP(r), *lhs);
- setLocation(cjump(_block->TEMP(r), iftrue, endif), ast->operatorToken);
- _block = iftrue;
- Result rhs = expression(ast->right);
+ left.loadInAccumulator();
+
+ bytecodeGenerator->setLocation(ast->operatorToken);
+ bytecodeGenerator->jumpFalse().link(endif);
+ iftrue.link();
+
+ blockTailCalls.unblock();
+ Reference right = expression(ast->right);
if (hasError)
return false;
- move(_block->TEMP(r), *rhs);
- _block->JUMP(endif);
+ right.loadInAccumulator();
+
+ endif.link();
- _expr.code = _block->TEMP(r);
- _block = endif;
+ _expr.setResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Or) {
if (_expr.accept(cx)) {
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- condition(ast->left, _expr.iftrue, iffalse);
- _block = iffalse;
- condition(ast->right, _expr.iftrue, _expr.iffalse);
+ auto iffalse = bytecodeGenerator->newLabel();
+ condition(ast->left, _expr.iftrue(), &iffalse, false);
+ iffalse.link();
+ condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
} else {
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
+ auto iffalse = bytecodeGenerator->newLabel();
+ auto endif = bytecodeGenerator->newLabel();
- const unsigned r = _block->newTemp();
- Result lhs = expression(ast->left);
+ Reference left = expression(ast->left);
if (hasError)
return false;
- move(_block->TEMP(r), *lhs);
- setLocation(cjump(_block->TEMP(r), endif, iffalse), ast->operatorToken);
- _block = iffalse;
- Result rhs = expression(ast->right);
+ left.loadInAccumulator();
+
+ bytecodeGenerator->setLocation(ast->operatorToken);
+ bytecodeGenerator->jumpTrue().link(endif);
+ iffalse.link();
+
+ blockTailCalls.unblock();
+ Reference right = expression(ast->right);
+ if (hasError)
+ return false;
+ right.loadInAccumulator();
+
+ endif.link();
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ }
+ return false;
+ } else if (ast->op == QSOperator::Assign) {
+ if (AST::Pattern *p = ast->left->patternCast()) {
+ RegisterScope scope(this);
+ Reference right = expression(ast->right);
if (hasError)
return false;
- move(_block->TEMP(r), *rhs);
- _block->JUMP(endif);
+ right = right.storeOnStack();
+ destructurePattern(p, right);
+ if (!_expr.accept(nx)) {
+ right.loadInAccumulator();
+ _expr.setResult(Reference::fromAccumulator(this));
+ }
+ return false;
+ }
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
- _block = endif;
- _expr.code = _block->TEMP(r);
+ if (!left.isLValue()) {
+ throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
+ return false;
}
+ left = left.asLValue();
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
+ return false;
+ blockTailCalls.unblock();
+ Reference r = expression(ast->right);
+ if (hasError)
+ return false;
+ r.loadInAccumulator();
+ if (_expr.accept(nx))
+ _expr.setResult(left.storeConsumeAccumulator());
+ else
+ _expr.setResult(left.storeRetainAccumulator());
return false;
}
- IR::Expr* left = *expression(ast->left);
+ Reference left = expression(ast->left);
if (hasError)
return false;
switch (ast->op) {
case QSOperator::Or:
case QSOperator::And:
+ case QSOperator::Assign:
+ Q_UNREACHABLE(); // handled separately above
break;
- case QSOperator::Assign: {
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
- return false;
- Result right = expression(ast->right);
- if (hasError)
- return false;
- if (!left->isLValue()) {
- throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
- return false;
- }
-
- if (_expr.accept(nx)) {
- move(left, *right);
- } else {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), *right);
- move(left, _block->TEMP(t));
- _expr.code = _block->TEMP(t);
- }
- break;
- }
-
case QSOperator::InplaceAnd:
case QSOperator::InplaceSub:
case QSOperator::InplaceDiv:
case QSOperator::InplaceAdd:
case QSOperator::InplaceLeftShift:
case QSOperator::InplaceMod:
+ case QSOperator::InplaceExp:
case QSOperator::InplaceMul:
case QSOperator::InplaceOr:
case QSOperator::InplaceRightShift:
@@ -1291,25 +1435,36 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::InplaceXor: {
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
return false;
- Result right = expression(ast->right);
- if (hasError)
- return false;
- if (!left->isLValue()) {
+
+ if (!left.isLValue()) {
throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue"));
return false;
}
+ left = left.asLValue();
+
+ Reference tempLeft = left.storeOnStack();
+ Reference right = expression(ast->right);
+
+ if (hasError)
+ return false;
+
+ binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
+ _expr.setResult(left.storeRetainAccumulator());
- if (_expr.accept(nx)) {
- move(left, *right, baseOp(ast->op));
- } else {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), *right);
- move(left, _block->TEMP(t), baseOp(ast->op));
- _expr.code = left;
- }
break;
}
+ case QSOperator::BitAnd:
+ case QSOperator::BitOr:
+ case QSOperator::BitXor:
+ if (left.isConstant()) {
+ Reference right = expression(ast->right);
+ if (hasError)
+ return false;
+ _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ break;
+ }
+ // intentional fall-through!
case QSOperator::In:
case QSOperator::InstanceOf:
case QSOperator::Equal:
@@ -1319,53 +1474,417 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::Le:
case QSOperator::Lt:
case QSOperator::StrictEqual:
- case QSOperator::StrictNotEqual: {
- if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), left), ast->operatorToken);
- left = _block->TEMP(t);
+ case QSOperator::StrictNotEqual:
+ case QSOperator::Add:
+ case QSOperator::Div:
+ case QSOperator::Exp:
+ case QSOperator::Mod:
+ case QSOperator::Mul:
+ case QSOperator::Sub:
+ case QSOperator::LShift:
+ case QSOperator::RShift:
+ case QSOperator::URShift: {
+ Reference right;
+ if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
+ visit(rhs);
+ right = _expr.result();
+ } else {
+ left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
+ right = expression(ast->right);
}
-
- Result right = expression(ast->right);
if (hasError)
return false;
- if (_expr.accept(cx)) {
- setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken);
+ _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+
+ break;
+ }
+
+ } // switch
+
+ return false;
+}
+
+Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Reference &right)
+{
+ switch (oper) {
+ case QSOperator::Add: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Add add;
+ add.lhs = left.stackSlot();
+ bytecodeGenerator->addTracingInstruction(add);
+ break;
+ }
+ case QSOperator::Sub: {
+ if (right.isConstant() && right.constant == Encode(int(1))) {
+ left.loadInAccumulator();
+ Instruction::Decrement dec = {};
+ bytecodeGenerator->addTracingInstruction(dec);
} else {
- _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken);
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Sub sub;
+ sub.lhs = left.stackSlot();
+ bytecodeGenerator->addTracingInstruction(sub);
}
break;
}
-
- case QSOperator::Add:
+ case QSOperator::Exp: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Exp exp;
+ exp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(exp);
+ break;
+ }
+ case QSOperator::Mul: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Mul mul;
+ mul.lhs = left.stackSlot();
+ bytecodeGenerator->addTracingInstruction(mul);
+ break;
+ }
+ case QSOperator::Div: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Div div;
+ div.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(div);
+ break;
+ }
+ case QSOperator::Mod: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Mod mod;
+ mod.lhs = left.stackSlot();
+ bytecodeGenerator->addTracingInstruction(mod);
+ break;
+ }
case QSOperator::BitAnd:
+ if (right.isConstant()) {
+ int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ if (left.isConstant()) {
+ int result = Value::fromReturnedValue(left.constant).toInt32() & rightAsInt;
+ return Reference::fromConst(this, Encode(result));
+ }
+ left.loadInAccumulator();
+ Instruction::BitAndConst bitAnd;
+ bitAnd.rhs = rightAsInt;
+ bytecodeGenerator->addInstruction(bitAnd);
+ } else {
+ right.loadInAccumulator();
+ Instruction::BitAnd bitAnd;
+ bitAnd.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(bitAnd);
+ }
+ break;
case QSOperator::BitOr:
+ if (right.isConstant()) {
+ int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ if (left.isConstant()) {
+ int result = Value::fromReturnedValue(left.constant).toInt32() | rightAsInt;
+ return Reference::fromConst(this, Encode(result));
+ }
+ left.loadInAccumulator();
+ Instruction::BitOrConst bitOr;
+ bitOr.rhs = rightAsInt;
+ bytecodeGenerator->addInstruction(bitOr);
+ } else {
+ right.loadInAccumulator();
+ Instruction::BitOr bitOr;
+ bitOr.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(bitOr);
+ }
+ break;
case QSOperator::BitXor:
- case QSOperator::Div:
- case QSOperator::LShift:
- case QSOperator::Mod:
- case QSOperator::Mul:
+ if (right.isConstant()) {
+ int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ if (left.isConstant()) {
+ int result = Value::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
+ return Reference::fromConst(this, Encode(result));
+ }
+ left.loadInAccumulator();
+ Instruction::BitXorConst bitXor;
+ bitXor.rhs = rightAsInt;
+ bytecodeGenerator->addInstruction(bitXor);
+ } else {
+ right.loadInAccumulator();
+ Instruction::BitXor bitXor;
+ bitXor.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(bitXor);
+ }
+ break;
+ case QSOperator::URShift:
+ if (right.isConstant()) {
+ left.loadInAccumulator();
+ Instruction::UShrConst ushr;
+ ushr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ bytecodeGenerator->addInstruction(ushr);
+ } else {
+ right.loadInAccumulator();
+ Instruction::UShr ushr;
+ ushr.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(ushr);
+ }
+ break;
case QSOperator::RShift:
- case QSOperator::Sub:
- case QSOperator::URShift: {
- if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), left), ast->operatorToken);
- left = _block->TEMP(t);
+ if (right.isConstant()) {
+ left.loadInAccumulator();
+ Instruction::ShrConst shr;
+ shr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ bytecodeGenerator->addInstruction(shr);
+ } else {
+ right.loadInAccumulator();
+ Instruction::Shr shr;
+ shr.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(shr);
}
+ break;
+ case QSOperator::LShift:
+ if (right.isConstant()) {
+ left.loadInAccumulator();
+ Instruction::ShlConst shl;
+ shl.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ bytecodeGenerator->addInstruction(shl);
+ } else {
+ right.loadInAccumulator();
+ Instruction::Shl shl;
+ shl.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(shl);
+ }
+ break;
+ case QSOperator::InstanceOf: {
+ Instruction::CmpInstanceOf binop;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ binop.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(binop);
+ break;
+ }
+ case QSOperator::In: {
+ Instruction::CmpIn binop;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ binop.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(binop);
+ break;
+ }
+ case QSOperator::StrictEqual: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpStrictEqual cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::StrictNotEqual: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpStrictNotEqual cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Equal: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpEq cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::NotEqual: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpNe cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Gt: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpGt cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Ge: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpGe cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Lt: {
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpLt cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ }
+ case QSOperator::Le:
+ if (_expr.accept(cx))
+ return jumpBinop(oper, left, right);
+
+ Instruction::CmpLe cmp;
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
- Result right = expression(ast->right);
- if (hasError)
- return false;
+ return Reference::fromAccumulator(this);
+}
- _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken);
- break;
+static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
+{
+ switch (oper) {
+ case QSOperator::StrictEqual: return QSOperator::StrictEqual;
+ case QSOperator::StrictNotEqual: return QSOperator::StrictNotEqual;
+ case QSOperator::Equal: return QSOperator::Equal;
+ case QSOperator::NotEqual: return QSOperator::NotEqual;
+ case QSOperator::Gt: return QSOperator::Le;
+ case QSOperator::Ge: return QSOperator::Lt;
+ case QSOperator::Lt: return QSOperator::Ge;
+ case QSOperator::Le: return QSOperator::Gt;
+ default: Q_UNIMPLEMENTED(); return QSOperator::Invalid;
}
+}
- } // switch
+Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
+{
+ if (left.isConstant()) {
+ oper = operatorForSwappedOperands(oper);
+ qSwap(left, right);
+ }
- return false;
+ if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
+ Value c = Value::fromReturnedValue(right.constant);
+ if (c.isNull() || c.isUndefined()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeNull cmp;
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+ } else if (c.isInt32()) {
+ left.loadInAccumulator();
+ if (oper == QSOperator::Equal) {
+ Instruction::CmpEqInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ } else if (oper == QSOperator::NotEqual) {
+ Instruction::CmpNeInt cmp;
+ cmp.lhs = c.int_32();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ return Reference();
+ }
+
+ }
+ }
+
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+
+ switch (oper) {
+ case QSOperator::StrictEqual: {
+ Instruction::CmpStrictEqual cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::StrictNotEqual: {
+ Instruction::CmpStrictNotEqual cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Equal: {
+ Instruction::CmpEq cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::NotEqual: {
+ Instruction::CmpNe cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Gt: {
+ Instruction::CmpGt cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Ge: {
+ Instruction::CmpGe cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Lt: {
+ Instruction::CmpLt cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ case QSOperator::Le: {
+ Instruction::CmpLe cmp;
+ cmp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(cmp);
+ addCJump();
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+ return Reference();
}
bool Codegen::visit(CallExpression *ast)
@@ -1373,53 +1892,257 @@ bool Codegen::visit(CallExpression *ast)
if (hasError)
return false;
- Result base = expression(ast->base);
- IR::ExprList *args = 0, **args_it = &args;
- for (ArgumentList *it = ast->arguments; it; it = it->next) {
- Result arg = expression(it->expression);
- if (hasError)
- return false;
- IR::Expr *actual = argument(*arg);
- *args_it = _function->New<IR::ExprList>();
- (*args_it)->init(actual);
- args_it = &(*args_it)->next;
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ Reference base = expression(ast->base);
+
+ if (hasError)
+ return false;
+ switch (base.type) {
+ case Reference::Member:
+ case Reference::Subscript:
+ case Reference::QmlScopeObject:
+ case Reference::QmlContextObject:
+ base = base.asLValue();
+ break;
+ case Reference::Name:
+ break;
+ case Reference::Super:
+ handleConstruct(base, ast->arguments);
+ return false;
+ case Reference::SuperProperty:
+ break;
+ default:
+ base = base.storeOnStack();
+ break;
}
+
+ int thisObject = bytecodeGenerator->newRegister();
+ int functionObject = bytecodeGenerator->newRegister();
+
+ auto calldata = pushArgs(ast->arguments);
if (hasError)
return false;
- _expr.code = call(*base, args);
+
+ blockTailCalls.unblock();
+ if (calldata.hasSpread || _tailCallsAreAllowed) {
+ Reference baseObject = base.baseObject();
+ if (!baseObject.isStackSlot()) {
+ baseObject.storeOnStack(thisObject);
+ baseObject = Reference::fromStackSlot(this, thisObject);
+ }
+ if (!base.isStackSlot()) {
+ base.storeOnStack(functionObject);
+ base = Reference::fromStackSlot(this, functionObject);
+ }
+
+ if (calldata.hasSpread) {
+ Instruction::CallWithSpread call;
+ call.func = base.stackSlot();
+ call.thisObject = baseObject.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else {
+ Instruction::TailCall call;
+ call.func = base.stackSlot();
+ call.thisObject = baseObject.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+
+ }
+
+ handleCall(base, calldata, functionObject, thisObject);
return false;
}
-bool Codegen::visit(ConditionalExpression *ast)
+void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject)
+{
+ //### Do we really need all these call instructions? can's we load the callee in a temp?
+ if (base.type == Reference::QmlScopeObject) {
+ Instruction::CallScopeObjectProperty call;
+ call.base = base.qmlBase.stackSlot();
+ call.name = base.qmlCoreIndex;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else if (base.type == Reference::QmlContextObject) {
+ Instruction::CallContextObjectProperty call;
+ call.base = base.qmlBase.stackSlot();
+ call.name = base.qmlCoreIndex;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else if (base.type == Reference::Member) {
+ if (!disable_lookups && useFastLookups) {
+ Instruction::CallPropertyLookup call;
+ call.base = base.propertyBase.stackSlot();
+ call.lookupIndex = registerGetterLookup(base.propertyNameIndex);
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else {
+ Instruction::CallProperty call;
+ call.base = base.propertyBase.stackSlot();
+ call.name = base.propertyNameIndex;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ }
+ } else if (base.type == Reference::Subscript) {
+ Instruction::CallElement call;
+ call.base = base.elementBase;
+ call.index = base.elementSubscript.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else if (base.type == Reference::Name) {
+ if (base.name == QStringLiteral("eval")) {
+ Instruction::CallPossiblyDirectEval call;
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else if (!disable_lookups && useFastLookups && base.global) {
+ Instruction::CallGlobalLookup call;
+ call.index = registerGlobalGetterLookup(base.nameAsIndex());
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else {
+ Instruction::CallName call;
+ call.name = base.nameAsIndex();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ }
+ } else if (base.type == Reference::SuperProperty) {
+ Reference receiver = base.baseObject();
+ if (!base.isStackSlot()) {
+ base.storeOnStack(slotForFunction);
+ base = Reference::fromStackSlot(this, slotForFunction);
+ }
+ if (!receiver.isStackSlot()) {
+ receiver.storeOnStack(slotForThisObject);
+ receiver = Reference::fromStackSlot(this, slotForThisObject);
+ }
+ Instruction::CallWithReceiver call;
+ call.name = base.stackSlot();
+ call.thisObject = receiver.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ } else {
+ Q_ASSERT(base.isStackSlot());
+ Instruction::CallValue call;
+ call.name = base.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addTracingInstruction(call);
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
+}
+
+Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
{
- if (hasError)
- return true;
+ bool hasSpread = false;
+ int argc = 0;
+ for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ hasSpread = true;
+ ++argc;
+ }
+ ++argc;
+ }
+
+ if (!argc)
+ return { 0, 0, false };
+
+ int calldata = bytecodeGenerator->newRegisterArray(argc);
+
+ argc = 0;
+ for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ Reference::fromConst(this, Value::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
+ ++argc;
+ }
+ RegisterScope scope(this);
+ Reference e = expression(it->expression);
+ if (hasError)
+ break;
+ if (!argc && !it->next && !hasSpread) {
+ // avoid copy for functions taking a single argument
+ if (e.isStackSlot())
+ return { 1, e.stackSlot(), hasSpread };
+ }
+ (void) e.storeOnStack(calldata + argc);
+ ++argc;
+ }
+
+ return { argc, calldata, hasSpread };
+}
+
+Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
+{
+ int argc = 0;
+ for (TemplateLiteral *it = args; it; it = it->next)
+ ++argc;
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
+ if (!argc)
+ return { 0, 0, false };
- const unsigned t = _block->newTemp();
+ int calldata = bytecodeGenerator->newRegisterArray(argc);
- condition(ast->expression, iftrue, iffalse);
+ argc = 0;
+ for (TemplateLiteral *it = args; it && it->expression; it = it->next) {
+ RegisterScope scope(this);
+ Reference e = expression(it->expression);
+ if (hasError)
+ break;
+ (void) e.storeOnStack(calldata + argc);
+ ++argc;
+ }
+
+ return { argc, calldata, false };
+}
- _block = iftrue;
- Result ok = expression(ast->ok);
+bool Codegen::visit(ConditionalExpression *ast)
+{
if (hasError)
return false;
- move(_block->TEMP(t), *ok);
- _block->JUMP(endif);
- _block = iffalse;
- Result ko = expression(ast->ko);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel();
+ condition(ast->expression, &iftrue, &iffalse, true);
+
+ blockTailCalls.unblock();
+
+ iftrue.link();
+ Reference ok = expression(ast->ok);
if (hasError)
return false;
- move(_block->TEMP(t), *ko);
- _block->JUMP(endif);
+ ok.loadInAccumulator();
+ BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
- _block = endif;
+ iffalse.link();
+ Reference ko = expression(ast->ko);
+ if (hasError) {
+ jump_endif.link(); // dummy link, to prevent assert in Jump destructor from triggering
+ return false;
+ }
+ ko.loadInAccumulator();
- _expr.code = _block->TEMP(t);
+ jump_endif.link();
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
@@ -1429,48 +2152,69 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError)
return false;
- IR::Expr* expr = *expression(ast->expression);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- // Temporaries cannot be deleted
- IR::ArgLocal *al = expr->asArgLocal();
- if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) {
+
+ switch (expr.type) {
+ case Reference::SuperProperty:
+ // ### this should throw a reference error at runtime.
+ return false;
+ case Reference::StackSlot:
+ if (!expr.stackSlotIsLocalOrArgument)
+ break;
+ // fall through
+ case Reference::ScopedLocal:
// Trying to delete a function argument might throw.
- if (_function->isStrict) {
+ if (_context->isStrict) {
throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
return false;
}
- _expr.code = _block->CONST(IR::BoolType, 0);
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
- }
- if (_function->isStrict && expr->asName()) {
- throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
+ case Reference::Name: {
+ if (_context->isStrict) {
+ throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
+ return false;
+ }
+ Instruction::DeleteName del;
+ del.name = expr.nameAsIndex();
+ bytecodeGenerator->addInstruction(del);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-
- // [[11.4.1]] Return true if it's not a reference
- if (expr->asConst() || expr->asString()) {
- _expr.code = _block->CONST(IR::BoolType, 1);
+ case Reference::Member: {
+ //### maybe add a variant where the base can be in the accumulator?
+ expr = expr.asLValue();
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = expr.propertyNameIndex;
+ bytecodeGenerator->addInstruction(instr);
+ Reference index = Reference::fromStackSlot(this);
+ index.storeConsumeAccumulator();
+ Instruction::DeleteProperty del;
+ del.base = expr.propertyBase.stackSlot();
+ del.index = index.stackSlot();
+ bytecodeGenerator->addInstruction(del);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-
- // Return values from calls are also not a reference, but we have to
- // perform the call to allow for side effects.
- if (expr->asCall()) {
- _block->EXP(expr);
- _expr.code = _block->CONST(IR::BoolType, 1);
+ case Reference::Subscript: {
+ //### maybe add a variant where the index can be in the accumulator?
+ expr = expr.asLValue();
+ Instruction::DeleteProperty del;
+ del.base = expr.elementBase;
+ del.index = expr.elementSubscript.stackSlot();
+ bytecodeGenerator->addInstruction(del);
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
- if (expr->asTemp() ||
- (expr->asArgLocal() &&
- expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) {
- _expr.code = _block->CONST(IR::BoolType, 1);
- return false;
+ default:
+ break;
}
-
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(reference(expr));
- _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args);
+ // [[11.4.1]] Return true if it's not a reference
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -1479,86 +2223,189 @@ bool Codegen::visit(FalseLiteral *)
if (hasError)
return false;
- if (_expr.accept(cx)) {
- _block->JUMP(_expr.iffalse);
- } else {
- _expr.code = _block->CONST(IR::BoolType, 0);
- }
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
}
-bool Codegen::visit(FieldMemberExpression *ast)
+bool Codegen::visit(SuperLiteral *)
{
if (hasError)
return false;
- Result base = expression(ast->base);
- if (!hasError)
- _expr.code = member(*base, _function->newString(ast->name.toString()));
+ _expr.setResult(Reference::fromSuper(this));
return false;
}
-bool Codegen::visit(FunctionExpression *ast)
+bool Codegen::visit(FieldMemberExpression *ast)
{
if (hasError)
return false;
- int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
- _expr.code = _block->CLOSURE(function);
+ TailCallBlocker blockTailCalls(this);
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ Q_ASSERT(ast->name == QLatin1String("target"));
+
+ if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
+ Reference r = referenceForName(QStringLiteral("new.target"), false);
+ r.isReadonly = true;
+ _expr.setResult(r);
+ return false;
+ }
+
+ Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
+ _expr.setResult(r);
+ return false;
+ }
+ }
+
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ if (base.isSuper()) {
+ Instruction::LoadRuntimeString load;
+ load.stringId = registerString(ast->name.toString());
+ bytecodeGenerator->addInstruction(load);
+ Reference property = Reference::fromAccumulator(this).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(property));
+ return false;
+ }
+ _expr.setResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
-IR::Expr *Codegen::identifier(const QString &name, int line, int col)
+bool Codegen::visit(TaggedTemplate *ast)
{
if (hasError)
- return 0;
+ return false;
- uint scope = 0;
- Environment *e = _variableEnvironment;
- IR::Function *f = _function;
+ RegisterScope scope(this);
- while (f && e->parent) {
- if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name))
- return _block->NAME(name, line, col);
+ int functionObject = -1, thisObject = -1;
- int index = e->findMember(name);
- Q_ASSERT (index < e->members.size());
- if (index != -1) {
- IR::ArgLocal *al = _block->LOCAL(index, scope);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval"))
- al->isArgumentsOrEval = true;
- return al;
- }
- const int argIdx = f->indexOfArgument(QStringRef(&name));
- if (argIdx != -1)
- return _block->ARG(argIdx, scope);
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ switch (base.type) {
+ case Reference::Member:
+ case Reference::Subscript:
+ base = base.asLValue();
+ break;
+ case Reference::Name:
+ break;
+ case Reference::SuperProperty:
+ thisObject = bytecodeGenerator->newRegister();
+ functionObject = bytecodeGenerator->newRegister();
+ break;
+ default:
+ base = base.storeOnStack();
+ break;
+ }
+
+ createTemplateObject(ast->templateLiteral);
+ int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot();
+ Q_UNUSED(templateObjectTemp);
+ auto calldata = pushTemplateArgs(ast->templateLiteral);
+ if (hasError)
+ return false;
+ ++calldata.argc;
+ Q_ASSERT(calldata.argv == templateObjectTemp + 1);
+ --calldata.argv;
+
+ handleCall(base, calldata, functionObject, thisObject);
+ return false;
+}
+
+void Codegen::createTemplateObject(TemplateLiteral *t)
+{
+ TemplateObject obj;
+
+ for (TemplateLiteral *it = t; it; it = it->next) {
+ obj.strings.append(registerString(it->value.toString()));
+ obj.rawStrings.append(registerString(it->rawValue.toString()));
+ }
+
+ int index = _module->templateObjects.size();
+ _module->templateObjects.append(obj);
+
+ Instruction::GetTemplateObject getTemplateObject;
+ getTemplateObject.index = index;
+ bytecodeGenerator->addInstruction(getTemplateObject);
+}
+
+bool Codegen::visit(FunctionExpression *ast)
+{
+ if (hasError)
+ return false;
+
+ TailCallBlocker blockTailCalls(this);
+
+ RegisterScope scope(this);
- if (!f->isStrict && f->hasDirectEval)
- return _block->NAME(name, line, col);
+ int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
+ if (hasError)
+ return false;
+ loadClosure(function);
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+}
- ++scope;
- e = e->parent;
- f = f->outer;
+Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, const SourceLocation &accessLocation)
+{
+ Context::ResolvedName resolved = _context->resolveName(name, accessLocation);
+
+ 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"));
+ Reference r;
+ switch (resolved.type) {
+ case Context::ResolvedName::Local:
+ r = Reference::fromScopedLocal(this, resolved.index, resolved.scope); break;
+ case Context::ResolvedName::Stack:
+ r = Reference::fromStackSlot(this, resolved.index, true /*isLocal*/); break;
+ case Context::ResolvedName::Import:
+ r = Reference::fromImport(this, resolved.index); break;
+ default: Q_UNREACHABLE();
+ }
+ if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name))
+ r.isVolatile = true;
+ r.isArgOrEval = resolved.isArgOrEval;
+ r.isReferenceToConst = resolved.isConst;
+ r.requiresTDZCheck = resolved.requiresTDZCheck;
+ r.name = name; // used to show correct name at run-time when TDZ check fails.
+ return r;
}
// This hook allows implementing QML lookup semantics
- if (IR::Expr *fallback = fallbackNameLookup(name, line, col))
+ Reference fallback = fallbackNameLookup(name);
+ if (fallback.type != Reference::Invalid)
return fallback;
- if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding)
- return _block->GLOBALNAME(name, line, col);
-
- // global context or with. Lookup by name
- return _block->NAME(name, line, col);
+ Reference r = Reference::fromName(this, name);
+ r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global);
+ if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name))
+ r.global = true;
+ return r;
+}
+void Codegen::loadClosure(int closureId)
+{
+ if (closureId >= 0) {
+ Instruction::LoadClosure load;
+ load.value = closureId;
+ bytecodeGenerator->addInstruction(load);
+ } else {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ }
}
-IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col)
+Codegen::Reference Codegen::fallbackNameLookup(const QString &name)
{
Q_UNUSED(name)
- Q_UNUSED(line)
- Q_UNUSED(col)
- return 0;
+ return Reference();
}
bool Codegen::visit(IdentifierExpression *ast)
@@ -1566,7 +2413,7 @@ bool Codegen::visit(IdentifierExpression *ast)
if (hasError)
return false;
- _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn);
+ _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@@ -1579,21 +2426,63 @@ bool Codegen::visit(NestedExpression *ast)
return false;
}
+void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
+{
+ Reference constructor;
+ if (base.isSuper()) {
+ Instruction::LoadSuperConstructor super;
+ bytecodeGenerator->addInstruction(super);
+ constructor = Reference::fromAccumulator(this).storeOnStack();
+ } else {
+ constructor = base.storeOnStack();
+ }
+
+ auto calldata = pushArgs(arguments);
+ if (hasError)
+ return;
+
+ if (base.isSuper())
+ Reference::fromStackSlot(this, CallData::NewTarget).loadInAccumulator();
+ else
+ constructor.loadInAccumulator();
+
+ if (calldata.hasSpread) {
+ Instruction::ConstructWithSpread create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ } else {
+ Instruction::Construct create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ }
+ if (base.isSuper())
+ // set the result up as the thisObject
+ Reference::fromAccumulator(this).storeOnStack(CallData::This);
+
+ _expr.setResult(Reference::fromAccumulator(this));
+}
+
bool Codegen::visit(NewExpression *ast)
{
if (hasError)
return false;
- Result base = expression(ast->expression);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ Reference base = expression(ast->expression);
if (hasError)
return false;
- IR::Expr *expr = *base;
- if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
+ if (base.isSuper()) {
+ throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
+ return false;
}
- _expr.code = _block->NEW(expr, 0);
+
+ handleConstruct(base, nullptr);
return false;
}
@@ -1602,29 +2491,18 @@ bool Codegen::visit(NewMemberExpression *ast)
if (hasError)
return false;
- Result base = expression(ast->base);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ Reference base = expression(ast->base);
if (hasError)
return false;
- IR::Expr *expr = *base;
- if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), expr);
- expr = _block->TEMP(t);
+ if (base.isSuper()) {
+ throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
+ return false;
}
- IR::ExprList *args = 0, **args_it = &args;
- for (ArgumentList *it = ast->arguments; it; it = it->next) {
- Result arg = expression(it->expression);
- if (hasError)
- return false;
- IR::Expr *actual = argument(*arg);
- *args_it = _function->New<IR::ExprList>();
- (*args_it)->init(actual);
- args_it = &(*args_it)->next;
- }
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), _block->NEW(expr, args));
- _expr.code = _block->TEMP(t);
+ handleConstruct(base, ast->arguments);
return false;
}
@@ -1633,12 +2511,8 @@ bool Codegen::visit(NotExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- const unsigned r = _block->newTemp();
- setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken);
- _expr.code = _block->TEMP(r);
+ TailCallBlocker blockTailCalls(this);
+ _expr.setResult(unop(Not, expression(ast->expression)));
return false;
}
@@ -1647,8 +2521,10 @@ bool Codegen::visit(NullExpression *)
if (hasError)
return false;
- if (_expr.accept(cx)) _block->JUMP(_expr.iffalse);
- else _expr.code = _block->CONST(IR::NullType, 0);
+ if (_expr.accept(cx))
+ bytecodeGenerator->jump().link(*_expr.iffalse());
+ else
+ _expr.setResult(Reference::fromConst(this, Encode::null()));
return false;
}
@@ -1658,164 +2534,120 @@ bool Codegen::visit(NumericLiteral *ast)
if (hasError)
return false;
- if (_expr.accept(cx)) {
- if (ast->value) _block->JUMP(_expr.iftrue);
- else _block->JUMP(_expr.iffalse);
- } else {
- _expr.code = _block->CONST(IR::NumberType, ast->value);
- }
+ _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
return false;
}
-struct ObjectPropertyValue {
- ObjectPropertyValue()
- : value(0)
- , getter(-1)
- , setter(-1)
- {}
-
- IR::Expr *value;
- int getter; // index in _module->functions or -1 if not set
- int setter;
-
- bool hasGetter() const { return getter >= 0; }
- bool hasSetter() const { return setter >= 0; }
-};
-
-bool Codegen::visit(ObjectLiteral *ast)
+bool Codegen::visit(ObjectPattern *ast)
{
if (hasError)
return false;
- QMap<QString, ObjectPropertyValue> valueMap;
-
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString name = it->assignment->name->asString();
- if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
- Result value = expression(nv->value);
- if (hasError)
- return false;
- ObjectPropertyValue &v = valueMap[name];
- if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) {
- throwSyntaxError(nv->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
+ TailCallBlocker blockTailCalls(this);
- valueMap[name].value = *value;
- } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
- const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0);
- ObjectPropertyValue &v = valueMap[name];
- if (v.value ||
- (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) ||
- (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) {
- throwSyntaxError(gs->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
- if (gs->type == PropertyGetterSetter::Getter)
- v.getter = function;
- else
- v.setter = function;
- } else {
- Q_UNREACHABLE();
- }
- }
+ RegisterScope scope(this);
- // The linked-list arguments to builtin_define_object_literal
- // begin with a CONST counting the number of key/value pairs, followed by the
- // key value pairs, followed by the array entries.
- IR::ExprList *args = _function->New<IR::ExprList>();
+ QStringList members;
- IR::Const *entryCountParam = _function->New<IR::Const>();
- entryCountParam->init(IR::SInt32Type, 0);
- args->expr = entryCountParam;
- args->next = 0;
+ int argc = 0;
+ int args = 0;
+ auto push = [this, &args, &argc](const Reference &arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (argc == 0)
+ args = temp;
+ (void) arg.storeOnStack(temp);
+ ++argc;
+ };
- IR::ExprList *keyValueEntries = 0;
- IR::ExprList *currentKeyValueEntry = 0;
- int keyValueEntryCount = 0;
- IR::ExprList *arrayEntries = 0;
+ PatternPropertyList *it = ast->properties;
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ if (cname || p->type != PatternProperty::Literal)
+ break;
+ QString name = p->name->asString();
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX)
+ break;
+ if (members.contains(name))
+ break;
+ members.append(name);
- IR::ExprList *currentArrayEntry = 0;
+ {
+ RegisterScope innerScope(this);
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
+ }
+ push(Reference::fromAccumulator(this));
+ }
- for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) {
- IR::ExprList **currentPtr = 0;
- uint keyAsIndex = QV4::String::toArrayIndex(it.key());
- if (keyAsIndex != UINT_MAX) {
- if (!arrayEntries) {
- arrayEntries = _function->New<IR::ExprList>();
- currentArrayEntry = arrayEntries;
- } else {
- currentArrayEntry->next = _function->New<IR::ExprList>();
- currentArrayEntry = currentArrayEntry->next;
- }
- currentPtr = &currentArrayEntry;
- IR::Const *idx = _function->New<IR::Const>();
- idx->init(IR::UInt32Type, keyAsIndex);
- (*currentPtr)->expr = idx;
+ int classId = jsUnitGenerator->registerJSClass(members);
+
+ // handle complex property setters
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ ObjectLiteralArgument argType = ObjectLiteralArgument::Value;
+ if (p->type == PatternProperty::Method)
+ argType = ObjectLiteralArgument::Method;
+ else if (p->type == PatternProperty::Getter)
+ argType = ObjectLiteralArgument::Getter;
+ else if (p->type == PatternProperty::Setter)
+ argType = ObjectLiteralArgument::Setter;
+
+ Reference::fromConst(this, Encode(int(argType))).loadInAccumulator();
+ push(Reference::fromAccumulator(this));
+
+ if (cname) {
+ RegisterScope innerScope(this);
+ Reference name = expression(cname->expression);
+ if (hasError)
+ return false;
+ name.loadInAccumulator();
} else {
- if (!keyValueEntries) {
- keyValueEntries = _function->New<IR::ExprList>();
- currentKeyValueEntry = keyValueEntries;
- } else {
- currentKeyValueEntry->next = _function->New<IR::ExprList>();
- currentKeyValueEntry = currentKeyValueEntry->next;
+ QString name = p->name->asString();
+#if 0
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX) {
+ Reference::fromConst(this, Encode(arrayIndex)).loadInAccumulator();
+ } else
+#endif
+ {
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(name);
+ bytecodeGenerator->addInstruction(instr);
}
- currentPtr = &currentKeyValueEntry;
- (*currentPtr)->expr = _block->NAME(it.key(), 0, 0);
- keyValueEntryCount++;
}
-
- IR::ExprList *&current = *currentPtr;
- if (it->value) {
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->CONST(IR::BoolType, true);
-
- unsigned value = _block->newTemp();
- move(_block->TEMP(value), it->value);
-
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->TEMP(value);
- } else {
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->CONST(IR::BoolType, false);
-
- unsigned getter = _block->newTemp();
- unsigned setter = _block->newTemp();
- move(_block->TEMP(getter), it->hasGetter() ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0));
- move(_block->TEMP(setter), it->hasSetter() ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0));
-
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->TEMP(getter);
- current->next = _function->New<IR::ExprList>();
- current = current->next;
- current->expr = _block->TEMP(setter);
+ push(Reference::fromAccumulator(this));
+ {
+ RegisterScope innerScope(this);
+ if (p->type != PatternProperty::Literal) {
+ // need to get the closure id for the method
+ FunctionExpression *f = p->initializer->asFunctionDefinition();
+ Q_ASSERT(f);
+ int function = defineFunction(f->name.toString(), f, f->formals, f->body);
+ if (hasError)
+ return false;
+ Reference::fromConst(this, Encode(function)).loadInAccumulator();
+ } else {
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
+ }
}
-
- it = valueMap.erase(it);
- }
-
- entryCountParam->value = keyValueEntryCount;
-
- if (keyValueEntries)
- args->next = keyValueEntries;
- if (arrayEntries) {
- if (currentKeyValueEntry)
- currentKeyValueEntry->next = arrayEntries;
- else
- args->next = arrayEntries;
+ push(Reference::fromAccumulator(this));
}
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal,
- ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args));
-
- _expr.code = _block->TEMP(t);
+ Instruction::DefineObjectLiteral call;
+ call.internalClassId = classId;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+ Reference result = Reference::fromAccumulator(this);
+ _expr.setResult(result);
return false;
}
@@ -1824,25 +2656,17 @@ bool Codegen::visit(PostDecrementExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->base);
+ Reference expr = expression(ast->base);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- const unsigned oldValue = _block->newTemp();
- setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken);
-
- const unsigned newValue = _block->newTemp();
- setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken);
- setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken);
-
- if (!_expr.accept(nx))
- _expr.code = _block->TEMP(oldValue);
+ _expr.setResult(unop(PostDecrement, expr));
return false;
}
@@ -1852,53 +2676,35 @@ bool Codegen::visit(PostIncrementExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->base);
+ Reference expr = expression(ast->base);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- const unsigned oldValue = _block->newTemp();
- setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken);
-
- const unsigned newValue = _block->newTemp();
- setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken);
- setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken);
-
- if (!_expr.accept(nx))
- _expr.code = _block->TEMP(oldValue);
-
+ _expr.setResult(unop(PostIncrement, expr));
return false;
}
bool Codegen::visit(PreDecrementExpression *ast)
-{
- if (hasError)
+{ if (hasError)
return false;
- Result expr = expression(ast->expression);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- IR::Expr *op = binop(IR::OpSub, *expr, _block->CONST(IR::NumberType, 1), ast->decrementToken);
- if (_expr.accept(nx)) {
- setLocation(move(*expr, op), ast->decrementToken);
- } else {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), op), ast->decrementToken);
- setLocation(move(*expr, _block->TEMP(t)), ast->decrementToken);
- _expr.code = _block->TEMP(t);
- }
+ _expr.setResult(unop(PreDecrement, expr));
return false;
}
@@ -1907,25 +2713,17 @@ bool Codegen::visit(PreIncrementExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- if (!expr->isLValue()) {
+ if (!expr.isLValue()) {
throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
return false;
}
- if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken))
+ if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- IR::Expr *op = binop(IR::OpAdd, unop(IR::OpUPlus, *expr, ast->incrementToken), _block->CONST(IR::NumberType, 1), ast->incrementToken);
- if (_expr.accept(nx)) {
- setLocation(move(*expr, op), ast->incrementToken);
- } else {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), op), ast->incrementToken);
- setLocation(move(*expr, _block->TEMP(t)), ast->incrementToken);
- _expr.code = _block->TEMP(t);
- }
+ _expr.setResult(unop(PreIncrement, expr));
return false;
}
@@ -1934,7 +2732,14 @@ bool Codegen::visit(RegExpLiteral *ast)
if (hasError)
return false;
- _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags);
+ auto r = Reference::fromStackSlot(this);
+ r.isReadonly = true;
+ _expr.setResult(r);
+
+ Instruction::MoveRegExp instr;
+ instr.regExpId = jsUnitGenerator->registerRegExp(ast);
+ instr.destReg = r.stackSlot();
+ bytecodeGenerator->addInstruction(instr);
return false;
}
@@ -1943,30 +2748,85 @@ bool Codegen::visit(StringLiteral *ast)
if (hasError)
return false;
- _expr.code = _block->STRING(_function->newString(ast->value.toString()));
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+ _expr.setResult(r);
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
return false;
}
-bool Codegen::visit(ThisExpression *ast)
+bool Codegen::visit(TemplateLiteral *ast)
{
if (hasError)
return false;
- _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn);
+ TailCallBlocker blockTailCalls(this);
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
+
+ if (ast->expression) {
+ RegisterScope scope(this);
+ int temp = bytecodeGenerator->newRegister();
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
+
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return false;
+
+ if (ast->next) {
+ int temp2 = bytecodeGenerator->newRegister();
+ expr.storeOnStack(temp2);
+ visit(ast->next);
+
+ Instruction::Add instr;
+ instr.lhs = temp2;
+ bytecodeGenerator->addTracingInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ }
+
+ Instruction::Add instr;
+ instr.lhs = temp;
+ bytecodeGenerator->addTracingInstruction(instr);
+ }
+
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+
+ _expr.setResult(r);
return false;
+
}
-bool Codegen::visit(TildeExpression *ast)
+bool Codegen::visit(ThisExpression *)
{
if (hasError)
return false;
- Result expr = expression(ast->expression);
+ if (_context->isArrowFunction) {
+ Reference r = referenceForName(QStringLiteral("this"), false);
+ r.isReadonly = true;
+ _expr.setResult(r);
+ return false;
+ }
+ _expr.setResult(Reference::fromThis(this));
+ return false;
+}
+
+bool Codegen::visit(TildeExpression *ast)
+{
if (hasError)
return false;
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken);
- _expr.code = _block->TEMP(t);
+
+ TailCallBlocker blockTailCalls(this);
+ _expr.setResult(unop(Compl, expression(ast->expression)));
return false;
}
@@ -1975,11 +2835,7 @@ bool Codegen::visit(TrueLiteral *)
if (hasError)
return false;
- if (_expr.accept(cx)) {
- _block->JUMP(_expr.iftrue);
- } else {
- _expr.code = _block->CONST(IR::BoolType, 1);
- }
+ _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -1988,12 +2844,25 @@ bool Codegen::visit(TypeOfExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ Reference expr = expression(ast->expression);
if (hasError)
return false;
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(reference(*expr));
- _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args);
+
+ if (expr.type == Reference::Name) {
+ // special handling as typeof doesn't throw here
+ Instruction::TypeofName instr;
+ instr.name = expr.nameAsIndex();
+ bytecodeGenerator->addInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ Instruction::TypeofValue instr;
+ bytecodeGenerator->addInstruction(instr);
+ }
+ _expr.setResult(Reference::fromAccumulator(this));
+
return false;
}
@@ -2002,12 +2871,8 @@ bool Codegen::visit(UnaryMinusExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), unop(IR::OpUMinus, *expr, ast->minusToken)), ast->minusToken);
- _expr.code = _block->TEMP(t);
+ TailCallBlocker blockTailCalls(this);
+ _expr.setResult(unop(UMinus, expression(ast->expression)));
return false;
}
@@ -2016,12 +2881,8 @@ bool Codegen::visit(UnaryPlusExpression *ast)
if (hasError)
return false;
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), unop(IR::OpUPlus, *expr, ast->plusToken)), ast->plusToken);
- _expr.code = _block->TEMP(t);
+ TailCallBlocker blockTailCalls(this);
+ _expr.setResult(unop(UPlus, expression(ast->expression)));
return false;
}
@@ -2030,8 +2891,11 @@ bool Codegen::visit(VoidExpression *ast)
if (hasError)
return false;
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
statement(ast->expression);
- _expr.code = _block->CONST(IR::UndefinedType, 0);
+ _expr.setResult(Reference::fromConst(this, Encode::undefined()));
return false;
}
@@ -2040,162 +2904,265 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (hasError)
return false;
- if (_variableEnvironment->compilationMode == QmlBinding)
- move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0));
+ // no need to block tail calls: the function body isn't visited here.
+ RegisterScope scope(this);
+
+ if (_functionContext->contextType == ContextType::Binding)
+ referenceForName(ast->name.toString(), true).loadInAccumulator();
_expr.accept(nx);
return false;
}
-int Codegen::defineFunction(const QString &name, AST::Node *ast,
- AST::FormalParameterList *formals,
- AST::SourceElements *body,
- const QStringList &inheritedLocals)
-{
- Loop *loop = 0;
- qSwap(_loop, loop);
- QStack<IR::BasicBlock *> exceptionHandlers;
- qSwap(_exceptionHandlers, exceptionHandlers);
-
- ScopeAndFinally *scopeAndFinally = 0;
-
- enterEnvironment(ast);
- IR::Function *function = _module->newFunction(name, _function);
- int functionIndex = _module->functions.count() - 1;
-
- IR::BasicBlock *entryBlock = function->newBasicBlock(0);
- IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock);
- function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode
- || _module->debugMode; // Conditional breakpoints are like eval in the function
- function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed);
- function->usesThis = _variableEnvironment->usesThis;
- function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount);
- function->isStrict = _variableEnvironment->isStrict;
- function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression;
- function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding;
-
- AST::SourceLocation loc = ast->firstSourceLocation();
- function->line = loc.startLine;
- function->column = loc.startColumn;
-
- if (function->usesArgumentsObject)
- _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope);
-
- // variables in global code are properties of the global context object, not locals as with other functions.
- if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) {
- unsigned t = 0;
- for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) {
- const QString &local = it.key();
- function->LOCAL(local);
- (*it).index = t;
- entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(IR::UndefinedType, 0));
- ++t;
- }
- } else {
- if (!_variableEnvironment->isStrict) {
- for (const QString &inheritedLocal : qAsConst(inheritedLocals)) {
- function->LOCAL(inheritedLocal);
- unsigned tempIndex = entryBlock->newTemp();
- Environment::Member member = { Environment::UndefinedMember,
- static_cast<int>(tempIndex), 0,
- AST::VariableDeclaration::VariableScope::FunctionScope };
- _variableEnvironment->members.insert(inheritedLocal, member);
- }
- }
+bool Codegen::visit(YieldExpression *ast)
+{
+ if (inFormalParameterList) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield is not allowed inside parameter lists"));
+ return false;
+ }
- IR::ExprList *args = 0;
- for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) {
- const QString &local = it.key();
- IR::ExprList *next = function->New<IR::ExprList>();
- next->expr = entryBlock->NAME(local, 0, 0);
- next->next = args;
- args = next;
- }
- if (args) {
- IR::ExprList *next = function->New<IR::ExprList>();
- next->expr = entryBlock->CONST(IR::BoolType, false); // ### Investigate removal of bool deletable
- next->next = args;
- args = next;
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+ Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
+ if (hasError)
+ return false;
- entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args));
- }
+ Reference acc = Reference::fromAccumulator(this);
+
+ if (ast->isYieldStar) {
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference lhsValue = Reference::fromConst(this, Encode::undefined()).storeOnStack();
+
+ expr.loadInAccumulator();
+ Instruction::GetIterator getIterator;
+ getIterator.iterator = static_cast<int>(AST::ForEachType::Of);
+ bytecodeGenerator->addInstruction(getIterator);
+ iterator.storeConsumeAccumulator();
+ Instruction::LoadUndefined load;
+ bytecodeGenerator->addInstruction(load);
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label loop = bytecodeGenerator->label();
+
+ lhsValue.loadInAccumulator();
+ Instruction::YieldStar yield;
+ bytecodeGenerator->addInstruction(yield);
+
+ in.link();
+
+ Instruction::IteratorNextForYieldStar next;
+ next.object = lhsValue.stackSlot();
+ next.iterator = iterator.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+
+ BytecodeGenerator::Jump done = bytecodeGenerator->jumpTrue();
+ bytecodeGenerator->jumpNotUndefined().link(loop);
+ lhsValue.loadInAccumulator();
+ emitReturn(acc);
+
+
+ done.link();
+
+ lhsValue.loadInAccumulator();
+ _expr.setResult(acc);
+ return false;
}
- unsigned returnAddress = entryBlock->newTemp();
+ expr.loadInAccumulator();
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
+ Instruction::Resume resume;
+ BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
+ emitReturn(acc);
+ jump.link();
+ _expr.setResult(acc);
+ return false;
+}
+
+static bool endsWithReturn(Module *module, Node *node)
+{
+ if (!node)
+ return false;
+ if (AST::cast<ReturnStatement *>(node))
+ return true;
+ if (AST::cast<ThrowStatement *>(node))
+ return true;
+ if (Program *p = AST::cast<Program *>(node))
+ return endsWithReturn(module, p->statements);
+ if (StatementList *sl = AST::cast<StatementList *>(node)) {
+ while (sl->next)
+ sl = sl->next;
+ return endsWithReturn(module, sl->statement);
+ }
+ if (Block *b = AST::cast<Block *>(node)) {
+ Context *blockContext = module->contextMap.value(node);
+ if (blockContext->requiresExecutionContext)
+ // we need to emit a return statement here, because of the
+ // unwind handler
+ return false;
+ return endsWithReturn(module, b->statements);
+ }
+ if (IfStatement *is = AST::cast<IfStatement *>(node))
+ return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko);
+ return false;
+}
+
+int Codegen::defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::StatementList *body)
+{
+ enterContext(ast);
+
+ if (_context->functionIndex >= 0)
+ // already defined
+ return leaveContext();
+
+ _context->name = name;
+ _module->functions.append(_context);
+ _context->functionIndex = _module->functions.count() - 1;
- entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0));
- setLocation(exitBlock->RET(exitBlock->TEMP(returnAddress)), ast->lastSourceLocation());
+ Context *savedFunctionContext = _functionContext;
+ _functionContext = _context;
+ ControlFlow *savedControlFlow = controlFlow;
+ controlFlow = nullptr;
- qSwap(_function, function);
- qSwap(_block, entryBlock);
- qSwap(_exitBlock, exitBlock);
+ if (_context->contextType == ContextType::Global || _context->contextType == ContextType::ScriptImportedByQML) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ }
+ if (_module->debugMode) // allow the debugger to see overwritten arguments
+ _context->argumentsCanEscape = true;
+
+ // When a user writes the following QML signal binding:
+ // onSignal: function() { doSomethingUsefull }
+ // we will generate a binding function that just returns the closure. However, that's not useful
+ // 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);
+
+ BytecodeGenerator bytecode(_context->line, _module->debugMode);
+ BytecodeGenerator *savedBytecodeGenerator;
+ savedBytecodeGenerator = bytecodeGenerator;
+ bytecodeGenerator = &bytecode;
+ bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
+ _returnLabel = nullptr;
+
+ bool savedFunctionEndsWithReturn = functionEndsWithReturn;
+ functionEndsWithReturn = endsWithReturn(_module, body);
+ bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size());
+
+ // reserve the js stack frame (Context & js Function & accumulator)
+ bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
+
+ bool _inFormalParameterList = false;
+ qSwap(_inFormalParameterList, inFormalParameterList);
+
+ int returnAddress = -1;
+ bool _requiresReturnValue = _context->requiresImplicitReturnValue();
+ qSwap(requiresReturnValue, _requiresReturnValue);
+ returnAddress = bytecodeGenerator->newRegister();
qSwap(_returnAddress, returnAddress);
- qSwap(_scopeAndFinally, scopeAndFinally);
- for (FormalParameterList *it = formals; it; it = it->next) {
- _function->RECEIVE(it->name.toString());
+ // register the lexical scope for global code
+ if (!_context->parent && _context->requiresExecutionContext) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
}
- for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) {
- if (member.function) {
- const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
- member.function->body ? member.function->body->elements : 0);
- if (! _variableEnvironment->parent) {
- move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn),
- _block->CLOSURE(function));
+ TailCallBlocker maybeBlockTailCalls(this, _context->canHaveTailCalls());
+
+ RegisterScope registerScope(this);
+ _context->emitBlockHeader(this);
+
+ {
+ QScopedValueRollback<bool> inFormals(inFormalParameterList, true);
+ TailCallBlocker blockTailCalls(this); // we're not in the FunctionBody or ConciseBody yet
+
+ int argc = 0;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (!e) {
+ if (!formals->next)
+ // trailing comma
+ break;
+ Q_UNREACHABLE();
+ }
+
+ Reference arg = referenceForName(e->bindingIdentifier.toString(), true);
+ if (e->type == PatternElement::RestElement) {
+ Q_ASSERT(!formals->next);
+ Instruction::CreateRestParameter rest;
+ rest.argIndex = argc;
+ bytecodeGenerator->addInstruction(rest);
+ arg.storeConsumeAccumulator();
} else {
- Q_ASSERT(member.index >= 0);
- move(_block->LOCAL(member.index, 0), _block->CLOSURE(function));
+ if (e->bindingTarget || e->initializer) {
+ initializeAndDestructureBindingElement(e, arg);
+ if (hasError)
+ break;
+ }
}
+ formals = formals->next;
+ ++argc;
}
}
- if (_function->usesArgumentsObject) {
- move(identifier(QStringLiteral("arguments"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn),
- _block->CALL(_block->NAME(IR::Name::builtin_setup_argument_object,
- ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0));
- }
- if (_function->usesThis && !_function->isStrict) {
- // make sure we convert this to an object
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_convert_this_to_object,
- ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0));
+
+ if (_context->isGenerator) {
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
}
beginFunctionBodyHook();
- sourceElements(body);
+ statementList(body);
- _function->addBasicBlock(_exitBlock);
-
- _block->JUMP(_exitBlock);
-
- qSwap(_function, function);
- qSwap(_block, entryBlock);
- qSwap(_exitBlock, exitBlock);
- qSwap(_returnAddress, returnAddress);
- qSwap(_scopeAndFinally, scopeAndFinally);
- qSwap(_exceptionHandlers, exceptionHandlers);
- qSwap(_loop, loop);
+ if (!hasError) {
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
+ _context->emitBlockFooter(this);
- leaveEnvironment();
+ if (_returnLabel || !functionEndsWithReturn) {
+ if (_returnLabel)
+ _returnLabel->link();
- return functionIndex;
-}
+ if (_returnLabel || requiresReturnValue) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
+ } else {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ }
-bool Codegen::visit(FunctionSourceElement *ast)
-{
- if (hasError)
- return false;
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
- statement(ast->declaration);
- return false;
-}
+ Q_ASSERT(_context == _functionContext);
+ bytecodeGenerator->finalize(_context);
+ _context->registerCountInFunction = bytecodeGenerator->registerCount();
+ _context->nTraceInfos = bytecodeGenerator->traceInfoCount();
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ 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();
+ }
+ }
-bool Codegen::visit(StatementSourceElement *ast)
-{
- if (hasError)
- return false;
+ qSwap(_returnAddress, returnAddress);
+ qSwap(requiresReturnValue, _requiresReturnValue);
+ qSwap(_inFormalParameterList, inFormalParameterList);
+ bytecodeGenerator = savedBytecodeGenerator;
+ delete _returnLabel;
+ _returnLabel = savedReturnLabel;
+ controlFlow = savedControlFlow;
+ functionEndsWithReturn = savedFunctionEndsWithReturn;
+ _functionContext = savedFunctionContext;
- statement(ast->statement);
- return false;
+ return leaveContext();
}
bool Codegen::visit(Block *ast)
@@ -2203,9 +3170,10 @@ bool Codegen::visit(Block *ast)
if (hasError)
return false;
- for (StatementList *it = ast->statements; it; it = it->next) {
- statement(it->statement);
- }
+ RegisterScope scope(this);
+
+ ControlFlowBlock controlFlow(this, ast);
+ statementList(ast->statements);
return false;
}
@@ -2214,25 +3182,23 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
- if (!_loop) {
+ // no need to block tail calls here: children aren't visited
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
}
- Loop *loop = 0;
- if (ast->label.isEmpty())
- loop = _loop;
- else {
- for (loop = _loop; loop; loop = loop->parent) {
- if (loop->labelledStatement && loop->labelledStatement->label == ast->label)
- break;
- }
- if (!loop) {
+
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
+ if (ast->label.isEmpty())
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
+ else
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
- return false;
- }
+ return false;
}
- unwindException(loop->scopeAndFinally);
- _block->JUMP(loop->breakBlock);
+
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
+
return false;
}
@@ -2241,31 +3207,25 @@ bool Codegen::visit(ContinueStatement *ast)
if (hasError)
return false;
- Loop *loop = 0;
- if (ast->label.isEmpty()) {
- for (loop = _loop; loop; loop = loop->parent) {
- if (loop->continueBlock)
- break;
- }
- } else {
- for (loop = _loop; loop; loop = loop->parent) {
- if (loop->labelledStatement && loop->labelledStatement->label == ast->label) {
- if (!loop->continueBlock)
- loop = 0;
- break;
- }
- }
- if (!loop) {
- throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
- return false;
- }
+ // no need to block tail calls here: children aren't visited
+ RegisterScope scope(this);
+
+ if (!controlFlow) {
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
+ return false;
}
- if (!loop) {
- throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop"));
+
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
+ if (ast->label.isEmpty())
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
+ else
+ throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop"));
return false;
}
- unwindException(loop->scopeAndFinally);
- _block->JUMP(loop->continueBlock);
+
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
+
return false;
}
@@ -2278,47 +3238,59 @@ bool Codegen::visit(DebuggerStatement *)
bool Codegen::visit(DoWhileStatement *ast)
{
if (hasError)
- return true;
+ return false;
- IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
- enterLoop(ast, loopend, loopcond);
+ BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label cond = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
- _block->JUMP(loopbody);
+ ControlFlowLoop flow(this, &end, &cond);
- _block = loopbody;
- statement(ast->statement);
- setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken);
+ // special case that is not a loop:
+ // do {...} while (false)
+ if (!AST::cast<FalseLiteral *>(ast->expression))
+ bytecodeGenerator->addLoopStart(body);
- _block = loopcond;
- condition(ast->expression, loopbody, loopend);
-
- _block = loopend;
+ body.link();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken);
+
+ cond.link();
+ if (AST::cast<TrueLiteral *>(ast->expression)) {
+ // do {} while (true) -> just jump back to the loop body, no need to generate a condition
+ bytecodeGenerator->jump().link(body);
+ } else if (AST::cast<FalseLiteral *>(ast->expression)) {
+ // do {} while (false) -> fall through, no need to generate a condition
+ } else {
+ TailCallBlocker blockTailCalls(this);
+ condition(ast->expression, &body, &end, false);
+ }
- leaveLoop();
+ end.link();
return false;
}
bool Codegen::visit(EmptyStatement *)
{
- if (hasError)
- return true;
-
return false;
}
bool Codegen::visit(ExpressionStatement *ast)
{
if (hasError)
- return true;
+ return false;
- if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) {
- Result e = expression(ast->expression);
- if (*e)
- move(_block->TEMP(_returnAddress), *e);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ if (requiresReturnValue) {
+ Reference e = expression(ast->expression);
+ if (hasError)
+ return false;
+ (void) e.storeOnStack(_returnAddress);
} else {
statement(ast->expression);
}
@@ -2328,81 +3300,138 @@ bool Codegen::visit(ExpressionStatement *ast)
bool Codegen::visit(ForEachStatement *ast)
{
if (hasError)
- return true;
+ return false;
- IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
- int objectToIterateOn = _block->newTemp();
- Result expr = expression(ast->expression);
- if (hasError)
- return false;
- move(_block->TEMP(objectToIterateOn), *expr);
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(objectToIterateOn));
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ Reference lhsValue = Reference::fromStackSlot(this);
- int iterator = _block->newTemp();
- move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args));
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables.
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return false;
- enterLoop(ast, foreachend, foreachin);
- _block->JUMP(foreachin);
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = static_cast<int>(ast->type);
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
- _block = foreachbody;
- int temp = _block->newTemp();
- Result init = expression(ast->initialiser);
- if (hasError)
- return false;
- move(*init, _block->TEMP(temp));
- statement(ast->statement);
- setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken);
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+
+ {
+ auto cleanup = [ast, iterator, iteratorDone, this]() {
+ if (ast->type == ForEachType::Of) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ }
+ };
+ ControlFlowLoop flow(this, &end, &in, cleanup);
+ bytecodeGenerator->addLoopStart(in);
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ next.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end);
+
+ // each iteration gets it's own context, as per spec
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ExpressionNode *e = ast->lhs->expressionCast()) {
+ if (AST::Pattern *p = e->patternCast()) {
+ RegisterScope scope(this);
+ destructurePattern(p, lhsValue);
+ } else {
+ Reference lhs = expression(e);
+ if (hasError)
+ goto error;
+ if (!lhs.isLValue()) {
+ throwReferenceError(e->firstSourceLocation(), QStringLiteral("Invalid left-hand side expression for 'in' expression"));
+ goto error;
+ }
+ lhs = lhs.asLValue();
+ lhsValue.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
+ }
+ } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
+ initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true);
+ if (hasError)
+ goto error;
+ } else {
+ Q_UNREACHABLE();
+ }
+
+ blockTailCalls.unblock();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+ }
+
+ bytecodeGenerator->jump().link(in);
- _block = foreachin;
+ error:
+ end.link();
- args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(iterator));
- move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args));
- int null = _block->newTemp();
- move(_block->TEMP(null), _block->CONST(IR::NullType, 0));
- setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken);
- _block = foreachend;
+ // all execution paths need to end up here (normal loop exit, break, and exceptions) in
+ // order to reset the unwind handler, and to close the iterator in calse of an for-of loop.
+ }
- leaveLoop();
return false;
}
bool Codegen::visit(ForStatement *ast)
{
if (hasError)
- return true;
+ return false;
- IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
- statement(ast->initialiser);
- _block->JUMP(forcond);
+ ControlFlowBlock controlFlow(this, ast);
- enterLoop(ast, forend, forstep);
+ if (ast->initialiser)
+ statement(ast->initialiser);
+ else if (ast->declarations)
+ variableDeclarationList(ast->declarations);
- _block = forcond;
- if (ast->condition)
- condition(ast->condition, forbody, forend);
- else
- _block->JUMP(forbody);
+ BytecodeGenerator::Label cond = bytecodeGenerator->label();
+ BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
- _block = forbody;
+ ControlFlowLoop flow(this, &end, &step);
+ bytecodeGenerator->addLoopStart(cond);
+ condition(ast->condition, &body, &end, true);
+
+ body.link();
+ blockTailCalls.unblock();
statement(ast->statement);
- setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken);
+ blockTailCalls.reblock();
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- _block = forstep;
+ step.link();
+ if (_context->requiresExecutionContext) {
+ Instruction::CloneBlockContext clone;
+ bytecodeGenerator->addInstruction(clone);
+ }
statement(ast->expression);
- _block->JUMP(forcond);
-
- _block = forend;
+ bytecodeGenerator->jump().link(cond);
- leaveLoop();
+ end.link();
return false;
}
@@ -2410,38 +3439,46 @@ bool Codegen::visit(ForStatement *ast)
bool Codegen::visit(IfStatement *ast)
{
if (hasError)
- return true;
+ return false;
- IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0;
- IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
- condition(ast->expression, iftrue, ast->ko ? iffalse : endif);
+ BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel();
+ condition(ast->expression, &trueLabel, &falseLabel, true);
+ blockTailCalls.unblock();
- _block = iftrue;
+ trueLabel.link();
statement(ast->ok);
- setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken);
-
if (ast->ko) {
- _block = iffalse;
- statement(ast->ko);
- setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken);
+ if (endsWithReturn(_module, ast)) {
+ falseLabel.link();
+ statement(ast->ko);
+ } else {
+ BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
+ falseLabel.link();
+ statement(ast->ko);
+ jump_endif.link();
+ }
+ } else {
+ falseLabel.link();
}
- _block = endif;
-
return false;
}
bool Codegen::visit(LabelledStatement *ast)
{
if (hasError)
- return true;
+ return false;
+
+ RegisterScope scope(this);
// check that no outer loop contains the label
- Loop *l = _loop;
+ ControlFlow *l = controlFlow;
while (l) {
- if (l->labelledStatement && l->labelledStatement->label == ast->label) {
+ if (l->label() == ast->label) {
QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
throwSyntaxError(ast->firstSourceLocation(), error);
return false;
@@ -2454,364 +3491,205 @@ bool Codegen::visit(LabelledStatement *ast)
AST::cast<AST::WhileStatement *>(ast->statement) ||
AST::cast<AST::DoWhileStatement *>(ast->statement) ||
AST::cast<AST::ForStatement *>(ast->statement) ||
- AST::cast<AST::ForEachStatement *>(ast->statement) ||
- AST::cast<AST::LocalForStatement *>(ast->statement) ||
- AST::cast<AST::LocalForEachStatement *>(ast->statement)) {
+ AST::cast<AST::ForEachStatement *>(ast->statement)) {
statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
} else {
- IR::BasicBlock *breakBlock = _function->newBasicBlock(exceptionHandler());
- enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0);
+ BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel();
+ ControlFlowLoop flow(this, &breakLabel);
statement(ast->statement);
- _block->JUMP(breakBlock);
- _block = breakBlock;
- leaveLoop();
+ breakLabel.link();
}
return false;
}
-bool Codegen::visit(LocalForEachStatement *ast)
-{
- if (hasError)
- return true;
-
- IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler());
-
- variableDeclaration(ast->declaration);
-
- int iterator = _block->newTemp();
- move(_block->TEMP(iterator), *expression(ast->expression));
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(iterator));
- move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args));
-
- _block->JUMP(foreachin);
- enterLoop(ast, foreachend, foreachin);
-
- _block = foreachbody;
- int temp = _block->newTemp();
- move(identifier(ast->declaration->name.toString()), _block->TEMP(temp));
- statement(ast->statement);
- setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken);
-
- _block = foreachin;
-
- args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(iterator));
- move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args));
- int null = _block->newTemp();
- move(_block->TEMP(null), _block->CONST(IR::NullType, 0));
- setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken);
- _block = foreachend;
-
- leaveLoop();
- return false;
-}
-
-bool Codegen::visit(LocalForStatement *ast)
+void Codegen::emitReturn(const Reference &expr)
{
- if (hasError)
- return true;
-
- IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler());
-
- variableDeclarationList(ast->declarations);
- _block->JUMP(forcond);
-
- enterLoop(ast, forend, forstep);
-
- _block = forcond;
- if (ast->condition)
- condition(ast->condition, forbody, forend);
- else
- _block->JUMP(forbody);
-
- _block = forbody;
- statement(ast->statement);
- setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken);
-
- _block = forstep;
- statement(ast->expression);
- _block->JUMP(forcond);
-
- _block = forend;
-
- leaveLoop();
-
- return false;
+ ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
+ if (target.linkLabel.isValid() && target.unwindLevel) {
+ Q_ASSERT(_returnAddress >= 0);
+ (void) expr.storeOnStack(_returnAddress);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
+ } else {
+ expr.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
}
bool Codegen::visit(ReturnStatement *ast)
{
if (hasError)
- return true;
+ return false;
- if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) {
+ if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
return false;
}
+ Reference expr;
if (ast->expression) {
- Result expr = expression(ast->expression);
- move(_block->TEMP(_returnAddress), *expr);
+ expr = expression(ast->expression);
+ if (hasError)
+ return false;
+ } else {
+ expr = Reference::fromConst(this, Encode::undefined());
}
- // Since we're leaving, don't let any finally statements we emit as part of the unwinding
- // jump to exception handlers at run-time if they throw.
- IR::BasicBlock *unwindBlock = _function->newBasicBlock(/*no exception handler*/Q_NULLPTR);
- _block->JUMP(unwindBlock);
- _block = unwindBlock;
+ emitReturn(expr);
- unwindException(0);
-
- _block->JUMP(_exitBlock);
return false;
}
bool Codegen::visit(SwitchStatement *ast)
{
if (hasError)
- return true;
+ return false;
- IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler());
+ if (requiresReturnValue)
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
if (ast->block) {
- int lhs = _block->newTemp();
- move(_block->TEMP(lhs), *expression(ast->expression));
- IR::BasicBlock *switchcond = _function->newBasicBlock(exceptionHandler());
- _block->JUMP(switchcond);
- IR::BasicBlock *previousBlock = 0;
+ BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
- QHash<Node *, IR::BasicBlock *> blockMap;
+ Reference lhs = expression(ast->expression);
+ if (hasError)
+ return false;
+ lhs = lhs.storeOnStack();
- enterLoop(ast, switchend, 0);
+ ControlFlowBlock controlFlow(this, ast->block);
+ // set up labels for all clauses
+ QHash<Node *, BytecodeGenerator::Label> blockMap;
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next)
+ blockMap[it->clause] = bytecodeGenerator->newLabel();
+ if (ast->block->defaultClause)
+ blockMap[ast->block->defaultClause] = bytecodeGenerator->newLabel();
+ for (CaseClauses *it = ast->block->moreClauses; it; it = it->next)
+ blockMap[it->clause] = bytecodeGenerator->newLabel();
+
+ // do the switch conditions
for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
CaseClause *clause = it->clause;
-
- _block = _function->newBasicBlock(exceptionHandler());
- blockMap[clause] = _block;
-
- if (previousBlock && !previousBlock->isTerminated())
- previousBlock->JUMP(_block);
-
- for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
- statement(it2->statement);
-
- previousBlock = _block;
- }
-
- if (ast->block->defaultClause) {
- _block = _function->newBasicBlock(exceptionHandler());
- blockMap[ast->block->defaultClause] = _block;
-
- if (previousBlock && !previousBlock->isTerminated())
- previousBlock->JUMP(_block);
-
- for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next)
- statement(it2->statement);
-
- previousBlock = _block;
+ Reference rhs = expression(clause->expression);
+ if (hasError)
+ return false;
+ rhs.loadInAccumulator();
+ bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
}
for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
CaseClause *clause = it->clause;
+ Reference rhs = expression(clause->expression);
+ if (hasError)
+ return false;
+ rhs.loadInAccumulator();
+ bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
+ }
- _block = _function->newBasicBlock(exceptionHandler());
- blockMap[clause] = _block;
+ if (DefaultClause *defaultClause = ast->block->defaultClause)
+ bytecodeGenerator->jump().link(blockMap.value(defaultClause));
+ else
+ bytecodeGenerator->jump().link(switchEnd);
- if (previousBlock && !previousBlock->isTerminated())
- previousBlock->JUMP(_block);
+ ControlFlowLoop flow(this, &switchEnd);
- for (StatementList *it2 = clause->statements; it2; it2 = it2->next)
- statement(it2->statement);
+ insideSwitch = true;
+ blockTailCalls.unblock();
+ for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
+ CaseClause *clause = it->clause;
+ blockMap[clause].link();
- previousBlock = _block;
+ statementList(clause->statements);
}
- leaveLoop();
-
- _block->JUMP(switchend);
+ if (ast->block->defaultClause) {
+ DefaultClause *clause = ast->block->defaultClause;
+ blockMap[clause].link();
- _block = switchcond;
- for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
- CaseClause *clause = it->clause;
- Result rhs = expression(clause->expression);
- IR::BasicBlock *iftrue = blockMap[clause];
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken);
- _block = iffalse;
+ statementList(clause->statements);
}
for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
CaseClause *clause = it->clause;
- Result rhs = expression(clause->expression);
- IR::BasicBlock *iftrue = blockMap[clause];
- IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler());
- setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken);
- _block = iffalse;
- }
+ blockMap[clause].link();
- if (DefaultClause *defaultClause = ast->block->defaultClause) {
- setLocation(_block->JUMP(blockMap[ast->block->defaultClause]), defaultClause->defaultToken);
+ statementList(clause->statements);
}
- }
+ insideSwitch = false;
+
+ switchEnd.link();
- _block->JUMP(switchend);
+ }
- _block = switchend;
return false;
}
bool Codegen::visit(ThrowStatement *ast)
{
if (hasError)
- return true;
+ return false;
- Result expr = expression(ast->expression);
- move(_block->TEMP(_returnAddress), *expr);
- IR::ExprList *throwArgs = _function->New<IR::ExprList>();
- throwArgs->expr = _block->TEMP(_returnAddress);
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs));
- return false;
-}
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
-bool Codegen::visit(TryStatement *ast)
-{
+ Reference expr = expression(ast->expression);
if (hasError)
- return true;
-
- _function->hasTry = true;
-
- if (_function->isStrict && ast->catchExpression &&
- (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) {
- throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
return false;
- }
- IR::BasicBlock *surroundingExceptionHandler = exceptionHandler();
-
- // We always need a finally body to clean up the exception handler
- // exceptions thrown in finally get caught by the surrounding catch block
- IR::BasicBlock *finallyBody = 0;
- IR::BasicBlock *catchBody = 0;
- IR::BasicBlock *catchExceptionHandler = 0;
- IR::BasicBlock *end = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock);
-
- if (ast->finallyExpression)
- finallyBody = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock);
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
+ return false;
+}
- if (ast->catchExpression) {
- // exception handler for the catch body
- catchExceptionHandler = _function->newBasicBlock(0, IR::Function::DontInsertBlock);
- pushExceptionHandler(catchExceptionHandler);
- catchBody = _function->newBasicBlock(catchExceptionHandler, IR::Function::DontInsertBlock);
- popExceptionHandler();
- pushExceptionHandler(catchBody);
- } else {
- Q_ASSERT(finallyBody);
- pushExceptionHandler(finallyBody);
+void Codegen::handleTryCatch(TryStatement *ast)
+{
+ Q_ASSERT(ast);
+ RegisterScope scope(this);
+ {
+ ControlFlowCatch catchFlow(this, ast->catchExpression);
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before catch is generated
+ statement(ast->statement);
}
+}
- IR::BasicBlock *tryBody = _function->newBasicBlock(exceptionHandler());
- _block->JUMP(tryBody);
-
- ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression);
- _scopeAndFinally = &tcf;
-
- _block = tryBody;
- statement(ast->statement);
- _block->JUMP(finallyBody ? finallyBody : end);
-
- popExceptionHandler();
+void Codegen::handleTryFinally(TryStatement *ast)
+{
+ RegisterScope scope(this);
+ ControlFlowFinally finally(this, ast->finallyExpression);
+ TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before finally is generated
if (ast->catchExpression) {
- pushExceptionHandler(catchExceptionHandler);
- _function->addBasicBlock(catchBody);
- _block = catchBody;
-
- ++_function->insideWithOrCatch;
- IR::ExprList *catchArgs = _function->New<IR::ExprList>();
- catchArgs->init(_block->STRING(_function->newString(ast->catchExpression->name.toString())));
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchArgs));
- {
- ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope);
- _scopeAndFinally = &scope;
- statement(ast->catchExpression->statement);
- _scopeAndFinally = scope.parent;
- }
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- --_function->insideWithOrCatch;
- _block->JUMP(finallyBody ? finallyBody : end);
- popExceptionHandler();
-
- _function->addBasicBlock(catchExceptionHandler);
- catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- if (finallyBody || surroundingExceptionHandler)
- catchExceptionHandler->JUMP(finallyBody ? finallyBody : surroundingExceptionHandler);
- else
- catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0));
+ handleTryCatch(ast);
+ } else {
+ RegisterScope scope(this);
+ statement(ast->statement);
}
+}
- _scopeAndFinally = tcf.parent;
-
- if (finallyBody) {
- _function->addBasicBlock(finallyBody);
- _block = finallyBody;
-
- int hasException = _block->newTemp();
- move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0));
+bool Codegen::visit(TryStatement *ast)
+{
+ if (hasError)
+ return false;
- if (ast->finallyExpression && ast->finallyExpression->statement)
- statement(ast->finallyExpression->statement);
+ RegisterScope scope(this);
- IR::ExprList *arg = _function->New<IR::ExprList>();
- arg->expr = _block->TEMP(hasException);
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), arg));
- _block->JUMP(end);
+ if (ast->finallyExpression && ast->finallyExpression->statement) {
+ handleTryFinally(ast);
+ } else {
+ handleTryCatch(ast);
}
- _function->addBasicBlock(end);
- _block = end;
-
return false;
}
-void Codegen::unwindException(Codegen::ScopeAndFinally *outest)
-{
- int savedDepthForWidthOrCatch = _function->insideWithOrCatch;
- ScopeAndFinally *scopeAndFinally = _scopeAndFinally;
- qSwap(_scopeAndFinally, scopeAndFinally);
- while (_scopeAndFinally != outest) {
- switch (_scopeAndFinally->type) {
- case ScopeAndFinally::WithScope:
- // fall through
- case ScopeAndFinally::CatchScope:
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0)));
- _scopeAndFinally = _scopeAndFinally->parent;
- --_function->insideWithOrCatch;
- break;
- case ScopeAndFinally::TryScope: {
- ScopeAndFinally *tc = _scopeAndFinally;
- _scopeAndFinally = tc->parent;
- if (tc->finally && tc->finally->statement)
- statement(tc->finally->statement);
- break;
- }
- }
- }
- qSwap(_scopeAndFinally, scopeAndFinally);
- _function->insideWithOrCatch = savedDepthForWidthOrCatch;
-}
-
bool Codegen::visit(VariableStatement *ast)
{
if (hasError)
- return true;
+ return false;
variableDeclarationList(ast->declarations);
return false;
@@ -2820,128 +3698,110 @@ bool Codegen::visit(VariableStatement *ast)
bool Codegen::visit(WhileStatement *ast)
{
if (hasError)
- return true;
+ return false;
- IR::BasicBlock *whilecond = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *whilebody = _function->newBasicBlock(exceptionHandler());
- IR::BasicBlock *whileend = _function->newBasicBlock(exceptionHandler());
+ if (AST::cast<FalseLiteral *>(ast->expression))
+ return false;
- enterLoop(ast, whileend, whilecond);
+ RegisterScope scope(this);
- _block->JUMP(whilecond);
- _block = whilecond;
- condition(ast->expression, whilebody, whileend);
+ BytecodeGenerator::Label start = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label cond = bytecodeGenerator->label();
+ ControlFlowLoop flow(this, &end, &cond);
+ bytecodeGenerator->addLoopStart(cond);
- _block = whilebody;
- statement(ast->statement);
- setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken);
+ if (!AST::cast<TrueLiteral *>(ast->expression)) {
+ TailCallBlocker blockTailCalls(this);
+ condition(ast->expression, &start, &end, true);
+ }
- _block = whileend;
- leaveLoop();
+ start.link();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->whileToken);
+ bytecodeGenerator->jump().link(cond);
+ end.link();
return false;
}
bool Codegen::visit(WithStatement *ast)
{
if (hasError)
- return true;
+ return false;
- _function->hasWith = true;
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
- const int withObject = _block->newTemp();
- Result src = expression(ast->expression);
+ Reference src = expression(ast->expression);
if (hasError)
return false;
- _block->MOVE(_block->TEMP(withObject), *src);
-
- // need an exception handler for with to cleanup the with scope
- IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler());
- withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- if (!exceptionHandler())
- withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0));
- else
- withExceptionHandler->JUMP(exceptionHandler());
-
- pushExceptionHandler(withExceptionHandler);
-
- IR::BasicBlock *withBlock = _function->newBasicBlock(exceptionHandler());
-
- _block->JUMP(withBlock);
- _block = withBlock;
- IR::ExprList *args = _function->New<IR::ExprList>();
- args->init(_block->TEMP(withObject));
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args));
+ src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
+ src.loadInAccumulator();
- ++_function->insideWithOrCatch;
+ enterContext(ast);
{
- ScopeAndFinally scope(_scopeAndFinally);
- _scopeAndFinally = &scope;
+ blockTailCalls.unblock();
+ ControlFlowWith flow(this);
statement(ast->statement);
- _scopeAndFinally = scope.parent;
}
- --_function->insideWithOrCatch;
- _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0));
- popExceptionHandler();
-
- IR::BasicBlock *next = _function->newBasicBlock(exceptionHandler());
- _block->JUMP(next);
- _block = next;
+ leaveContext();
return false;
}
bool Codegen::visit(UiArrayBinding *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiObjectBinding *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiObjectDefinition *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiPublicMember *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiScriptBinding *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
bool Codegen::visit(UiSourceElement *)
{
- Q_ASSERT(!"not implemented");
+ Q_UNIMPLEMENTED();
return false;
}
-bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc)
+bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const SourceLocation& loc)
{
- if (!_variableEnvironment->isStrict)
- return false;
- if (IR::Name *n = expr->asName()) {
- if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments"))
- return false;
- } else if (IR::ArgLocal *al = expr->asArgLocal()) {
- if (!al->isArgumentsOrEval)
- return false;
- } else {
+ if (!_context->isStrict)
return false;
+ bool isArgOrEval = false;
+ if (r.type == Reference::Name) {
+ QString str = jsUnitGenerator->stringForIndex(r.nameAsIndex());
+ if (str == QLatin1String("eval") || str == QLatin1String("arguments")) {
+ isArgOrEval = true;
+ }
+ } else if (r.type == Reference::ScopedLocal || r.isRegister()) {
+ isArgOrEval = r.isArgOrEval;
}
- throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- return true;
+ if (isArgOrEval)
+ throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ return isArgOrEval;
}
void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail)
@@ -2973,6 +3833,117 @@ QList<QQmlJS::DiagnosticMessage> Codegen::errors() const
return _errors;
}
+QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData)
+{
+ CompiledData::Unit *unitData = nullptr;
+ if (generateUnitData)
+ unitData = jsUnitGenerator->generateUnit();
+ CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit(unitData);
+
+ QQmlRefPointer<CompiledData::CompilationUnit> unit;
+ unit.adopt(compilationUnit);
+ return unit;
+}
+
+QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
+{
+ QQmlRefPointer<CompiledData::CompilationUnit> result;
+ result.adopt(new CompiledData::CompilationUnit);
+ return result;
+}
+
+class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
+{
+ VolatileMemoryLocations locs;
+
+public:
+ Codegen::VolatileMemoryLocations scan(AST::Node *s)
+ {
+ s->accept(this);
+ return locs;
+ }
+
+ bool visit(ArrayMemberExpression *) override
+ {
+ locs.setAllVolatile();
+ return false;
+ }
+
+ bool visit(FieldMemberExpression *) override
+ {
+ locs.setAllVolatile();
+ return false;
+ }
+
+ bool visit(PostIncrementExpression *e) override
+ {
+ collectIdentifiers(locs.specificLocations, e->base);
+ return false;
+ }
+
+ bool visit(PostDecrementExpression *e) override
+ {
+ collectIdentifiers(locs.specificLocations, e->base);
+ return false;
+ }
+
+ bool visit(PreIncrementExpression *e) override
+ {
+ collectIdentifiers(locs.specificLocations, e->expression);
+ return false;
+ }
+
+ bool visit(PreDecrementExpression *e) override
+ {
+ collectIdentifiers(locs.specificLocations, e->expression);
+ return false;
+ }
+
+ bool visit(BinaryExpression *e) override
+ {
+ switch (e->op) {
+ case QSOperator::InplaceAnd:
+ case QSOperator::InplaceSub:
+ case QSOperator::InplaceDiv:
+ case QSOperator::InplaceAdd:
+ case QSOperator::InplaceLeftShift:
+ case QSOperator::InplaceMod:
+ case QSOperator::InplaceMul:
+ case QSOperator::InplaceOr:
+ case QSOperator::InplaceRightShift:
+ case QSOperator::InplaceURightShift:
+ case QSOperator::InplaceXor:
+ collectIdentifiers(locs.specificLocations, e);
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
+private:
+ void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const {
+ class Collector: public QQmlJS::AST::Visitor {
+ QVector<QStringView> &ids;
+ public:
+ Collector(QVector<QStringView> &ids): ids(ids) {}
+ virtual bool visit(IdentifierExpression *ie) {
+ ids.append(ie->name);
+ return false;
+ }
+ };
+ Collector collector(ids);
+ node->accept(&collector);
+ }
+};
+
+Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const
+{
+ VolatileMemoryLocationScanner scanner;
+ return scanner.scan(ast);
+}
+
+
#ifndef V4_BOOTSTRAP
QList<QQmlError> Codegen::qmlErrors() const
@@ -2998,20 +3969,489 @@ QList<QQmlError> Codegen::qmlErrors() const
return qmlErrors;
}
-void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail)
+#endif // V4_BOOTSTRAP
+
+bool Codegen::RValue::operator==(const RValue &other) const
{
- if (hasError)
+ switch (type) {
+ case Accumulator:
+ return other.isAccumulator();
+ case StackSlot:
+ return other.isStackSlot() && theStackSlot == other.theStackSlot;
+ case Const:
+ return other.isConst() && constant == other.constant;
+ default:
+ return false;
+ }
+}
+
+Codegen::RValue Codegen::RValue::storeOnStack() const
+{
+ switch (type) {
+ case Accumulator:
+ return RValue::fromStackSlot(codegen, Reference::fromAccumulator(codegen).storeOnStack().stackSlot());
+ case StackSlot:
+ return *this;
+ case Const:
+ return RValue::fromStackSlot(codegen, Reference::storeConstOnStack(codegen, constant).stackSlot());
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void Codegen::RValue::loadInAccumulator() const
+{
+ switch (type) {
+ case Accumulator:
+ // nothing to do
return;
- hasError = true;
- engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn);
+ case StackSlot:
+ return Reference::fromStackSlot(codegen, theStackSlot).loadInAccumulator();
+ case Const:
+ return Reference::fromConst(codegen, constant).loadInAccumulator();
+ default:
+ Q_UNREACHABLE();
+ }
+
}
-void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail)
+bool Codegen::Reference::operator==(const Codegen::Reference &other) const
{
- if (hasError)
+ if (type != other.type)
+ return false;
+ switch (type) {
+ case Invalid:
+ case Accumulator:
+ break;
+ case Super:
+ return true;
+ case SuperProperty:
+ return property == other.property;
+ case StackSlot:
+ return theStackSlot == other.theStackSlot;
+ case ScopedLocal:
+ return index == other.index && scope == other.scope;
+ case Name:
+ return nameAsIndex() == other.nameAsIndex();
+ case Member:
+ return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex;
+ case Subscript:
+ return elementBase == other.elementBase && elementSubscript == other.elementSubscript;
+ case Import:
+ return index == other.index;
+ case Const:
+ return constant == other.constant;
+ case QmlScopeObject:
+ case QmlContextObject:
+ return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex
+ && capturePolicy == other.capturePolicy;
+ }
+ return true;
+}
+
+Codegen::RValue Codegen::Reference::asRValue() const
+{
+ switch (type) {
+ case Invalid:
+ Q_UNREACHABLE();
+ case Accumulator:
+ return RValue::fromAccumulator(codegen);
+ case StackSlot:
+ return RValue::fromStackSlot(codegen, stackSlot());
+ case Const:
+ return RValue::fromConst(codegen, constant);
+ default:
+ loadInAccumulator();
+ return RValue::fromAccumulator(codegen);
+ }
+}
+
+Codegen::Reference Codegen::Reference::asLValue() const
+{
+ switch (type) {
+ case Invalid:
+ case Accumulator:
+ Q_UNREACHABLE();
+ case Super:
+ codegen->throwSyntaxError(AST::SourceLocation(), QStringLiteral("Super lvalues not implemented."));
+ return *this;
+ case Member:
+ if (!propertyBase.isStackSlot()) {
+ Reference r = *this;
+ r.propertyBase = propertyBase.storeOnStack();
+ return r;
+ }
+ return *this;
+ case Subscript:
+ if (!elementSubscript.isStackSlot()) {
+ Reference r = *this;
+ r.elementSubscript = elementSubscript.storeOnStack();
+ return r;
+ }
+ return *this;
+ default:
+ return *this;
+ }
+}
+
+Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
+{
+ storeAccumulator(); // it doesn't matter what happens here, just do it.
+ return Reference();
+}
+
+Codegen::Reference Codegen::Reference::baseObject() const
+{
+ if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
+ return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
+ } else if (type == Reference::Member) {
+ RValue rval = propertyBase;
+ if (!rval.isValid())
+ return Reference::fromConst(codegen, Encode::undefined());
+ if (rval.isAccumulator())
+ return Reference::fromAccumulator(codegen);
+ if (rval.isStackSlot())
+ return Reference::fromStackSlot(codegen, rval.stackSlot());
+ if (rval.isConst())
+ return Reference::fromConst(codegen, rval.constantValue());
+ Q_UNREACHABLE();
+ } else if (type == Reference::Subscript) {
+ return Reference::fromStackSlot(codegen, elementBase.stackSlot());
+ } else if (type == Reference::SuperProperty) {
+ return Reference::fromStackSlot(codegen, CallData::This);
+ } else {
+ return Reference::fromConst(codegen, Encode::undefined());
+ }
+}
+
+Codegen::Reference Codegen::Reference::storeOnStack() const
+{ return doStoreOnStack(-1); }
+
+void Codegen::Reference::storeOnStack(int slotIndex) const
+{ doStoreOnStack(slotIndex); }
+
+Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
+{
+ Q_ASSERT(isValid());
+
+ if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile) && !requiresTDZCheck)
+ return *this;
+
+ if (isStackSlot() && !requiresTDZCheck) { // temp-to-temp move
+ Reference dest = Reference::fromStackSlot(codegen, slotIndex);
+ Instruction::MoveReg move;
+ move.srcReg = stackSlot();
+ move.destReg = dest.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(move);
+ return dest;
+ }
+
+ Reference slot = Reference::fromStackSlot(codegen, slotIndex);
+ if (isConstant()) {
+ Instruction::MoveConst move;
+ move.constIndex = codegen->registerConstant(constant);
+ move.destTemp = slot.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(move);
+ } else {
+ loadInAccumulator();
+ slot.storeConsumeAccumulator();
+ }
+ return slot;
+}
+
+Codegen::Reference Codegen::Reference::storeRetainAccumulator() const
+{
+ if (storeWipesAccumulator()) {
+ // a store will
+ auto tmp = Reference::fromStackSlot(codegen);
+ tmp.storeAccumulator(); // this is safe, and won't destory the accumulator
+ storeAccumulator();
+ return tmp;
+ } else {
+ // ok, this is safe, just do the store.
+ storeAccumulator();
+ return *this;
+ }
+}
+
+bool Codegen::Reference::storeWipesAccumulator() const
+{
+ switch (type) {
+ default:
+ case Invalid:
+ case Const:
+ case Accumulator:
+ Q_UNREACHABLE();
+ return false;
+ case StackSlot:
+ case ScopedLocal:
+ return false;
+ case Name:
+ case Member:
+ case Subscript:
+ case QmlScopeObject:
+ case QmlContextObject:
+ return true;
+ }
+}
+
+void Codegen::Reference::storeAccumulator() const
+{
+ 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);
return;
- hasError = true;
- engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn);
+ }
+ switch (type) {
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ Instruction::StoreSuperProperty store;
+ store.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(store);
+ return;
+ case StackSlot: {
+ Instruction::StoreReg store;
+ store.reg = theStackSlot;
+ codegen->bytecodeGenerator->addInstruction(store);
+ return;
+ }
+ case ScopedLocal: {
+ if (scope == 0) {
+ Instruction::StoreLocal store;
+ store.index = index;
+ codegen->bytecodeGenerator->addInstruction(store);
+ } else {
+ Instruction::StoreScopedLocal store;
+ store.index = index;
+ store.scope = scope;
+ codegen->bytecodeGenerator->addInstruction(store);
+ }
+ return;
+ }
+ case Name: {
+ Context *c = codegen->currentContext();
+ if (c->isStrict) {
+ Instruction::StoreNameStrict store;
+ store.name = nameAsIndex();
+ codegen->bytecodeGenerator->addInstruction(store);
+ } else {
+ Instruction::StoreNameSloppy store;
+ store.name = nameAsIndex();
+ codegen->bytecodeGenerator->addInstruction(store);
+ }
+ } return;
+ case Member:
+ if (!disable_lookups && codegen->useFastLookups) {
+ Instruction::SetLookup store;
+ store.base = propertyBase.stackSlot();
+ store.index = codegen->registerSetterLookup(propertyNameIndex);
+ codegen->bytecodeGenerator->addInstruction(store);
+ } else {
+ Instruction::StoreProperty store;
+ store.base = propertyBase.stackSlot();
+ store.name = propertyNameIndex;
+ codegen->bytecodeGenerator->addInstruction(store);
+ }
+ return;
+ case Subscript: {
+ Instruction::StoreElement store;
+ store.base = elementBase;
+ store.index = elementSubscript.stackSlot();
+ codegen->bytecodeGenerator->addTracingInstruction(store);
+ } return;
+ case QmlScopeObject: {
+ Instruction::StoreScopeObjectProperty store;
+ store.base = qmlBase;
+ store.propertyIndex = qmlCoreIndex;
+ codegen->bytecodeGenerator->addInstruction(store);
+ } return;
+ case QmlContextObject: {
+ Instruction::StoreContextObjectProperty store;
+ store.base = qmlBase;
+ store.propertyIndex = qmlCoreIndex;
+ codegen->bytecodeGenerator->addInstruction(store);
+ } return;
+ case Invalid:
+ case Accumulator:
+ case Const:
+ case Import:
+ break;
+ }
+
+ Q_UNREACHABLE();
}
-#endif // V4_BOOTSTRAP
+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;
+ case SuperProperty:
+ tdzCheckStackSlot(property, subscriptRequiresTDZCheck);
+ Instruction::LoadSuperProperty load;
+ load.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
+ case Const: {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ if (constant == Encode::null()) {
+ Instruction::LoadNull load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (constant == Encode(true)) {
+ Instruction::LoadTrue load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (constant == Encode(false)) {
+ Instruction::LoadFalse load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else if (constant == Encode::undefined()) {
+ Instruction::LoadUndefined load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ } else {
+ Value p = Value::fromReturnedValue(constant);
+ if (p.isNumber()) {
+ double d = p.asDouble();
+ int i = static_cast<int>(d);
+ if (d == i && (d != 0 || !std::signbit(d))) {
+ if (!i) {
+ Instruction::LoadZero load;
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
+ }
+ Instruction::LoadInt load;
+ load.value = Value::fromReturnedValue(constant).toInt32();
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
+ }
+ }
+ Instruction::LoadConst load;
+ load.index = codegen->registerConstant(constant);
+ codegen->bytecodeGenerator->addInstruction(load);
+ }
+QT_WARNING_POP
+ } return;
+ case StackSlot: {
+ Instruction::LoadReg load;
+ load.reg = stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(requiresTDZCheck);
+ } return;
+ case ScopedLocal: {
+ if (!scope) {
+ Instruction::LoadLocal load;
+ load.index = index;
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ } else {
+ Instruction::LoadScopedLocal load;
+ load.index = index;
+ load.scope = scope;
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ }
+ tdzCheck(requiresTDZCheck);
+ return;
+ }
+ case Name:
+ if (global) {
+ // these value properties of the global object are immutable, we we can directly convert them
+ // to their numeric value here
+ if (name == QStringLiteral("undefined")) {
+ Reference::fromConst(codegen, Encode::undefined()).loadInAccumulator();
+ return;
+ } else if (name == QStringLiteral("Infinity")) {
+ Reference::fromConst(codegen, Encode(qInf())).loadInAccumulator();
+ return;
+ } else if (name == QStringLiteral("Nan")) {
+ Reference::fromConst(codegen, Encode(qQNaN())).loadInAccumulator();
+ return;
+ }
+ }
+ if (!disable_lookups && global) {
+ Instruction::LoadGlobalLookup load;
+ load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ } else {
+ Instruction::LoadName load;
+ load.name = nameAsIndex();
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ }
+ return;
+ case Member:
+ propertyBase.loadInAccumulator();
+ tdzCheck(requiresTDZCheck);
+ if (!disable_lookups && codegen->useFastLookups) {
+ Instruction::GetLookup load;
+ load.index = codegen->registerGetterLookup(propertyNameIndex);
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ } else {
+ Instruction::LoadProperty load;
+ load.name = propertyNameIndex;
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ }
+ return;
+ case Import: {
+ Instruction::LoadImport load;
+ load.index = index;
+ codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(requiresTDZCheck);
+ } return;
+ case Subscript: {
+ tdzCheckStackSlot(elementBase, requiresTDZCheck);
+ elementSubscript.loadInAccumulator();
+ tdzCheck(subscriptRequiresTDZCheck);
+ Instruction::LoadElement load;
+ load.base = elementBase;
+ codegen->bytecodeGenerator->addTracingInstruction(load);
+ } return;
+ case QmlScopeObject: {
+ Instruction::LoadScopeObjectProperty load;
+ load.base = qmlBase;
+ load.propertyIndex = qmlCoreIndex;
+ load.captureRequired = capturePolicy == CaptureAtRuntime;
+ codegen->bytecodeGenerator->addInstruction(load);
+ if (capturePolicy == CaptureAheadOfTime)
+ codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
+ } return;
+ case QmlContextObject: {
+ Instruction::LoadContextObjectProperty load;
+ load.base = qmlBase;
+ load.propertyIndex = qmlCoreIndex;
+ load.captureRequired = capturePolicy == CaptureAtRuntime;
+ codegen->bytecodeGenerator->addInstruction(load);
+ if (capturePolicy == CaptureAheadOfTime)
+ codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
+ } return;
+ case Invalid:
+ break;
+ }
+ Q_UNREACHABLE();
+}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 239ed5c4b9..4d7001fe64 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -51,303 +51,524 @@
//
#include "private/qv4global_p.h"
-#include "qv4jsir_p.h"
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
-#include <private/qqmljsengine_p.h>
-#include <QtCore/QStringList>
-#include <QStack>
-#ifndef V4_BOOTSTRAP
-#include <qqmlerror.h>
-#endif
+#include <private/qv4compiler_p.h>
+#include <private/qv4compilercontext_p.h>
#include <private/qv4util_p.h>
+#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4stackframe_p.h>
QT_BEGIN_NAMESPACE
-namespace QQmlJS {
-namespace AST {
-class UiParameterList;
+using namespace QQmlJS;
+
+namespace QV4 {
+
+namespace Moth {
+struct Instruction;
+}
+
+namespace CompiledData {
+struct CompilationUnit;
}
+namespace Compiler {
+
+struct ControlFlow;
+struct ControlFlowCatch;
+struct ControlFlowFinally;
-class Q_QML_PRIVATE_EXPORT Codegen: protected AST::Visitor
+class Q_QML_PRIVATE_EXPORT Codegen: protected QQmlJS::AST::Visitor
{
+protected:
+ using BytecodeGenerator = QV4::Moth::BytecodeGenerator;
+ using Instruction = QV4::Moth::Instruction;
public:
- Codegen(bool strict);
-
- enum CompilationMode {
- GlobalCode,
- EvalCode,
- FunctionCode,
- QmlBinding // This is almost the same as EvalCode, except:
- // * function declarations are moved to the return address when encountered
- // * return statements are allowed everywhere (like in FunctionCode)
- // * variable declarations are treated as true locals (like in FunctionCode)
- };
+ Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict);
+
void generateFromProgram(const QString &fileName,
+ const QString &finalUrl,
const QString &sourceCode,
AST::Program *ast,
- QV4::IR::Module *module,
- CompilationMode mode = GlobalCode,
- const QStringList &inheritedLocals = QStringList());
- void generateFromFunctionExpression(const QString &fileName,
- const QString &sourceCode,
- AST::FunctionExpression *ast,
- QV4::IR::Module *module);
+ Module *module,
+ ContextType contextType = ContextType::Global);
-protected:
- enum Format { ex, cx, nx };
- struct Result {
- QV4::IR::Expr *code;
- QV4::IR::BasicBlock *iftrue;
- QV4::IR::BasicBlock *iffalse;
- Format format;
- Format requested;
-
- explicit Result(Format requested = ex)
- : code(0)
- , iftrue(0)
- , iffalse(0)
- , format(ex)
- , requested(requested) {}
-
- explicit Result(QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse)
- : code(0)
- , iftrue(iftrue)
- , iffalse(iffalse)
- , format(ex)
- , requested(cx) {}
-
- inline QV4::IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; }
- inline QV4::IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; }
+ void generateFromModule(const QString &fileName,
+ const QString &finalUrl,
+ const QString &sourceCode,
+ AST::ESModule *ast,
+ Module *module);
- bool accept(Format f)
- {
- if (requested == f) {
- format = f;
+public:
+ class VolatileMemoryLocationScanner;
+ class VolatileMemoryLocations {
+ friend VolatileMemoryLocationScanner;
+ bool allVolatile = false;
+ QVector<QStringView> specificLocations;
+ public:
+ bool isVolatile(const QStringView &name) {
+ if (allVolatile)
return true;
- }
- return false;
+ return specificLocations.contains(name);
}
+
+ void add(const QStringRef &name) { if (!allVolatile) specificLocations.append(name); }
+ void setAllVolatile() { allVolatile = true; }
};
+ class RValue {
+ Codegen *codegen;
+ enum Type {
+ Invalid,
+ Accumulator,
+ StackSlot,
+ Const
+ } type;
+ union {
+ Moth::StackSlot theStackSlot;
+ QV4::ReturnedValue constant;
+ };
+
+ public:
+ static RValue fromStackSlot(Codegen *codegen, Moth::StackSlot stackSlot) {
+ RValue r;
+ r.codegen = codegen;
+ r.type = StackSlot;
+ r.theStackSlot = stackSlot;
+ return r;
+ }
+ static RValue fromAccumulator(Codegen *codegen) {
+ RValue r;
+ r.codegen = codegen;
+ r.type = Accumulator;
+ return r;
+ }
+ static RValue fromConst(Codegen *codegen, QV4::ReturnedValue value) {
+ RValue r;
+ r.codegen = codegen;
+ r.type = Const;
+ r.constant = value;
+ return r;
+ }
- struct Environment {
- Environment *parent;
+ bool operator==(const RValue &other) const;
- enum MemberType {
- UndefinedMember,
- VariableDefinition,
- VariableDeclaration,
- FunctionDefinition
- };
+ bool isValid() const { return type != Invalid; }
+ bool isAccumulator() const { return type == Accumulator; }
+ bool isStackSlot() const { return type == StackSlot; }
+ bool isConst() const { return type == Const; }
- struct Member {
- MemberType type;
- int index;
- AST::FunctionExpression *function;
- AST::VariableDeclaration::VariableScope scope;
+ Moth::StackSlot stackSlot() const {
+ Q_ASSERT(isStackSlot());
+ return theStackSlot;
+ }
+
+ QV4::ReturnedValue constantValue() const {
+ Q_ASSERT(isConst());
+ return constant;
+ }
+
+ Q_REQUIRED_RESULT RValue storeOnStack() const;
+ void loadInAccumulator() const;
+ };
+ struct Reference {
+ enum Type {
+ Invalid,
+ Accumulator,
+ Super,
+ SuperProperty,
+ StackSlot,
+ ScopedLocal,
+ Name,
+ Member,
+ Subscript,
+ Import,
+ QmlScopeObject,
+ QmlContextObject,
+ LastLValue = QmlContextObject,
+ Const
+ } type = Invalid;
+
+ bool isLValue() const { return !isReadonly && type > Accumulator; }
+
+ Reference(Codegen *cg, Type type = Invalid) : type(type), constant(0), codegen(cg) {}
+ Reference(): constant(0) {}
+ Reference(const Reference &) = default;
+ Reference(Reference &&) = default;
+ Reference &operator =(const Reference &) = default;
+ Reference &operator =(Reference &&) = default;
+
+ bool operator==(const Reference &other) const;
+ bool operator!=(const Reference &other) const
+ { return !(*this == other); }
+
+ bool isValid() const { return type != Invalid; }
+ bool loadTriggersSideEffect() const {
+ switch (type) {
+ case QmlScopeObject:
+ return capturePolicy != DontCapture;
+ case QmlContextObject:
+ return capturePolicy != DontCapture;
+ case Name:
+ case Member:
+ case Subscript:
+ case SuperProperty:
+ return true;
+ default:
+ return requiresTDZCheck;
+ }
+ }
+ bool isConstant() const { return type == Const; }
+ bool isAccumulator() const { return type == Accumulator; }
+ bool isSuper() const { return type == Super; }
+ bool isSuperProperty() const { return type == SuperProperty; }
+ bool isStackSlot() const { return type == StackSlot; }
+ bool isRegister() const {
+ return isStackSlot();
+ }
- bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; }
+ enum PropertyCapturePolicy {
+ /*
+ We're reading a property from the scope or context object, but it's a CONSTANT property,
+ so we don't need to register a dependency at all.
+ */
+ DontCapture,
+ /*
+ We're reading the property of a QObject, and we know that it's the
+ scope object or context object, which we know very well. Instead of registering a
+ property capture every time, we can do that ahead of time and then register all those
+ captures in one shot in registerQmlDependencies().
+ */
+ CaptureAheadOfTime,
+ /*
+ We're reading the property of a QObject, and we're not quite sure where
+ the QObject comes from or what it is. So, when reading that property at run-time,
+ make sure that we capture where we read that property so that if it changes we can
+ re-evaluate the entire expression.
+ */
+ CaptureAtRuntime
};
- typedef QMap<QString, Member> MemberMap;
-
- MemberMap members;
- AST::FormalParameterList *formals;
- int maxNumberOfArguments;
- bool hasDirectEval;
- bool hasNestedFunctions;
- bool isStrict;
- bool isNamedFunctionExpression;
- bool usesThis;
- enum UsesArgumentsObject {
- ArgumentsObjectUnknown,
- ArgumentsObjectNotUsed,
- ArgumentsObjectUsed
+
+ static Reference fromAccumulator(Codegen *cg) {
+ return Reference(cg, Accumulator);
+ }
+ static Reference fromSuper(Codegen *cg) {
+ return Reference(cg, Super);
+ }
+ static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) {
+ Reference r(cg, StackSlot);
+ if (tempIndex == -1)
+ tempIndex = cg->bytecodeGenerator->newRegister();
+ r.theStackSlot = Moth::StackSlot::createRegister(tempIndex);
+ 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(Value) - 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;
+ r.scope = scope;
+ return r;
+ }
+ static Reference fromImport(Codegen *cg, int index) {
+ Reference r(cg, Import);
+ r.index = index;
+ return r;
+ }
+ static Reference fromName(Codegen *cg, const QString &name) {
+ Reference r(cg, Name);
+ r.name = name;
+ return r;
+ }
+ static Reference fromMember(const Reference &baseRef, const QString &name) {
+ Reference r(baseRef.codegen, Member);
+ r.propertyBase = baseRef.asRValue();
+ r.propertyNameIndex = r.codegen->registerString(name);
+ r.requiresTDZCheck = baseRef.requiresTDZCheck;
+ return r;
+ }
+ static Reference fromSuperProperty(const Reference &property) {
+ Q_ASSERT(property.isStackSlot());
+ Reference r(property.codegen, SuperProperty);
+ r.property = property.stackSlot();
+ r.subscriptRequiresTDZCheck = property.requiresTDZCheck;
+ return r;
+ }
+ static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
+ Q_ASSERT(baseRef.isStackSlot());
+ Reference r(baseRef.codegen, Subscript);
+ r.elementBase = baseRef.stackSlot();
+ r.elementSubscript = subscript.asRValue();
+ r.requiresTDZCheck = baseRef.requiresTDZCheck;
+ r.subscriptRequiresTDZCheck = subscript.requiresTDZCheck;
+ return r;
+ }
+ static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) {
+ Reference r(cg, Const);
+ r.constant = constant;
+ r.isReadonly = true;
+ return r;
+ }
+ static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
+ Reference r(base.codegen, QmlScopeObject);
+ r.qmlBase = base.storeOnStack().stackSlot();
+ r.qmlCoreIndex = coreIndex;
+ r.qmlNotifyIndex = notifyIndex;
+ r.capturePolicy = capturePolicy;
+ return r;
+ }
+ static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
+ Reference r(base.codegen, QmlContextObject);
+ r.qmlBase = base.storeOnStack().stackSlot();
+ r.qmlCoreIndex = coreIndex;
+ r.qmlNotifyIndex = notifyIndex;
+ r.capturePolicy = capturePolicy;
+ return r;
+ }
+ static Reference fromThis(Codegen *cg) {
+ Reference r = fromStackSlot(cg, CallData::This);
+ r.isReadonly = true;
+ // ### Optimize this. Functions that are not derived constructors or arrow functions can't have an
+ // empty this object
+ r.requiresTDZCheck = true;
+ return r;
+ }
+
+ RValue asRValue() const;
+ Reference asLValue() const;
+
+ Q_REQUIRED_RESULT static Reference storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant)
+ { return Reference::fromConst(cg, constant).storeOnStack(); }
+
+ static void storeConstOnStack(Codegen *cg, QV4::ReturnedValue constant, int stackSlot)
+ { Reference::fromConst(cg, constant).storeOnStack(stackSlot); }
+
+ Q_REQUIRED_RESULT Reference storeOnStack() const;
+ void storeOnStack(int tempIndex) const;
+ Q_REQUIRED_RESULT Reference storeRetainAccumulator() const;
+ Reference storeConsumeAccumulator() const;
+
+ Q_REQUIRED_RESULT Reference baseObject() const;
+
+ bool storeWipesAccumulator() const;
+ void loadInAccumulator() const;
+
+ int nameAsIndex() const {
+ Q_ASSERT(type == Name);
+ return codegen->registerString(name);
+ }
+
+ Moth::StackSlot stackSlot() const {
+ if (Q_UNLIKELY(!isStackSlot()))
+ Q_UNREACHABLE();
+ return theStackSlot;
+ }
+
+ union {
+ Moth::StackSlot theStackSlot;
+ QV4::ReturnedValue constant;
+ struct { // Scoped arguments/Local
+ int index;
+ int scope;
+ };
+ struct {
+ RValue propertyBase;
+ int propertyNameIndex;
+ };
+ struct {
+ Moth::StackSlot elementBase;
+ RValue elementSubscript;
+ };
+ struct { // QML scope/context object case
+ Moth::StackSlot qmlBase;
+ qint16 qmlCoreIndex;
+ qint16 qmlNotifyIndex;
+ PropertyCapturePolicy capturePolicy;
+ };
+ Moth::StackSlot property; // super property
};
+ QString name;
+ mutable bool isArgOrEval = false;
+ bool isReadonly = false;
+ bool isReferenceToConst = false;
+ bool requiresTDZCheck = false;
+ bool subscriptRequiresTDZCheck = false;
+ bool stackSlotIsLocalOrArgument = false;
+ bool isVolatile = false;
+ bool global = false;
+ Codegen *codegen = nullptr;
+
+ private:
+ void storeAccumulator() const;
+ Reference doStoreOnStack(int tempIndex) const;
+ };
- UsesArgumentsObject usesArgumentsObject;
-
- CompilationMode compilationMode;
-
- Environment(Environment *parent, CompilationMode mode)
- : parent(parent)
- , formals(0)
- , maxNumberOfArguments(0)
- , hasDirectEval(false)
- , hasNestedFunctions(false)
- , isStrict(false)
- , isNamedFunctionExpression(false)
- , usesThis(false)
- , usesArgumentsObject(ArgumentsObjectUnknown)
- , compilationMode(mode)
- {
- if (parent && parent->isStrict)
- isStrict = true;
+ struct RegisterScope {
+ RegisterScope(Codegen *cg)
+ : generator(cg->bytecodeGenerator),
+ regCountForScope(generator->currentReg) {}
+ ~RegisterScope() {
+ generator->currentReg = regCountForScope;
}
+ BytecodeGenerator *generator;
+ int regCountForScope;
+ };
+
+ struct ObjectPropertyValue {
+ ObjectPropertyValue() {}
+
+ Reference rvalue;
+ int getter = -1; // index in _module->functions or -1 if not set
+ int setter = -1;
+ uint keyAsIndex = UINT_MAX;
+
+ bool hasGetter() const { return getter >= 0; }
+ bool hasSetter() const { return setter >= 0; }
+ };
+protected:
+
+ enum Format { ex, cx, nx };
+ class Result {
+ Reference _result;
+
+ const BytecodeGenerator::Label *_iftrue = nullptr;
+ const BytecodeGenerator::Label *_iffalse = nullptr;
+ Format _format = ex;
+ Format _requested;
+ bool _trueBlockFollowsCondition = false;
+
+ public:
+ explicit Result(const Reference &lrvalue)
+ : _result(lrvalue)
+ , _requested(ex)
+ {}
- int findMember(const QString &name) const
+ explicit Result(Format requested = ex)
+ : _requested(requested) {}
+
+ explicit Result(const BytecodeGenerator::Label *iftrue,
+ const BytecodeGenerator::Label *iffalse,
+ bool trueBlockFollowsCondition)
+ : _iftrue(iftrue)
+ , _iffalse(iffalse)
+ , _requested(cx)
+ , _trueBlockFollowsCondition(trueBlockFollowsCondition)
{
- MemberMap::const_iterator it = members.find(name);
- if (it == members.end())
- return -1;
- Q_ASSERT((*it).index != -1 || !parent);
- return (*it).index;
+ Q_ASSERT(iftrue);
+ Q_ASSERT(iffalse);
}
- bool memberInfo(const QString &name, const Member **m) const
- {
- Q_ASSERT(m);
- MemberMap::const_iterator it = members.find(name);
- if (it == members.end()) {
- *m = 0;
- return false;
- }
- *m = &(*it);
- return true;
+ const BytecodeGenerator::Label *iftrue() const {
+ Q_ASSERT(_requested == cx);
+ return _iftrue;
}
- bool lookupMember(const QString &name, Environment **scope, int *index, int *distance)
+ const BytecodeGenerator::Label *iffalse() const {
+ Q_ASSERT(_requested == cx);
+ return _iffalse;
+ }
+
+ Format format() const {
+ return _format;
+ }
+
+ bool accept(Format f)
{
- Environment *it = this;
- *distance = 0;
- for (; it; it = it->parent, ++(*distance)) {
- int idx = it->findMember(name);
- if (idx != -1) {
- *scope = it;
- *index = idx;
- return true;
- }
+ if (_requested == f) {
+ _format = f;
+ return true;
}
return false;
}
- void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0)
- {
- if (! name.isEmpty()) {
- if (type != FunctionDefinition) {
- for (AST::FormalParameterList *it = formals; it; it = it->next)
- if (it->name == name)
- return;
- }
- MemberMap::iterator it = members.find(name);
- if (it == members.end()) {
- Member m;
- m.index = -1;
- m.type = type;
- m.function = function;
- m.scope = scope;
- members.insert(name, m);
- } else {
- Q_ASSERT(scope == (*it).scope);
- if ((*it).type <= type) {
- (*it).type = type;
- (*it).function = function;
- }
- }
- }
+ bool trueBlockFollowsCondition() const {
+ return _trueBlockFollowsCondition;
}
- };
- Environment *newEnvironment(AST::Node *node, Environment *parent, CompilationMode compilationMode)
- {
- Environment *env = new Environment(parent, compilationMode);
- _envMap.insert(node, env);
- return env;
- }
+ const Reference &result() const {
+ return _result;
+ }
- struct UiMember {
+ void setResult(const Reference &result) {
+ _result = result;
+ }
};
- struct ScopeAndFinally {
- enum ScopeType {
- WithScope,
- TryScope,
- CatchScope
- };
-
- ScopeAndFinally *parent;
- AST::Finally *finally;
- ScopeType type;
+ void enterContext(AST::Node *node);
+ int leaveContext();
+public:
+ Context *enterBlock(AST::Node *node);
+ int leaveBlock() { return leaveContext(); }
+protected:
+ void leaveLoop();
- ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), type(t) {}
- ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally)
- : parent(parent), finally(finally), type(TryScope)
- {}
+ enum UnaryOperation {
+ UPlus,
+ UMinus,
+ PreIncrement,
+ PreDecrement,
+ PostIncrement,
+ PostDecrement,
+ Not,
+ Compl
};
- struct Loop {
- AST::LabelledStatement *labelledStatement;
- AST::Statement *node;
- QV4::IR::BasicBlock *breakBlock;
- QV4::IR::BasicBlock *continueBlock;
- Loop *parent;
- ScopeAndFinally *scopeAndFinally;
+ Reference unop(UnaryOperation op, const Reference &expr);
- Loop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock, Loop *parent)
- : labelledStatement(0), node(node), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {}
- };
-
- void enterEnvironment(AST::Node *node);
- void leaveEnvironment();
+ void addCJump();
- void enterLoop(AST::Statement *node, QV4::IR::BasicBlock *breakBlock, QV4::IR::BasicBlock *continueBlock);
- void leaveLoop();
- QV4::IR::BasicBlock *exceptionHandler() const
- {
- if (_exceptionHandlers.isEmpty())
- return 0;
- return _exceptionHandlers.top();
- }
- void pushExceptionHandler(QV4::IR::BasicBlock *handler)
- {
- handler->setExceptionHandler(true);
- _exceptionHandlers.push(handler);
- }
- void popExceptionHandler()
- {
- Q_ASSERT(!_exceptionHandlers.isEmpty());
- _exceptionHandlers.pop();
+public:
+ int registerString(const QString &name) {
+ return jsUnitGenerator->registerString(name);
}
-
- QV4::IR::Expr *member(QV4::IR::Expr *base, const QString *name);
- QV4::IR::Expr *subscript(QV4::IR::Expr *base, QV4::IR::Expr *index);
- QV4::IR::Expr *argument(QV4::IR::Expr *expr);
- QV4::IR::Expr *reference(QV4::IR::Expr *expr);
- QV4::IR::Expr *unop(QV4::IR::AluOp op, QV4::IR::Expr *expr, const AST::SourceLocation &loc = AST::SourceLocation());
- QV4::IR::Expr *binop(QV4::IR::AluOp op, QV4::IR::Expr *left, QV4::IR::Expr *right, const AST::SourceLocation &loc = AST::SourceLocation());
- QV4::IR::Expr *call(QV4::IR::Expr *base, QV4::IR::ExprList *args);
- QV4::IR::Stmt *move(QV4::IR::Expr *target, QV4::IR::Expr *source, QV4::IR::AluOp op = QV4::IR::OpInvalid);
- QV4::IR::Stmt *cjump(QV4::IR::Expr *cond, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse);
+ 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); }
// Returns index in _module->functions
- int defineFunction(const QString &name, AST::Node *ast,
- AST::FormalParameterList *formals,
- AST::SourceElements *body,
- const QStringList &inheritedLocals = QStringList());
-
- void unwindException(ScopeAndFinally *outest);
+ virtual int defineFunction(const QString &name, AST::Node *ast,
+ AST::FormalParameterList *formals,
+ AST::StatementList *body);
+protected:
void statement(AST::Statement *ast);
void statement(AST::ExpressionNode *ast);
- void condition(AST::ExpressionNode *ast, QV4::IR::BasicBlock *iftrue, QV4::IR::BasicBlock *iffalse);
- Result expression(AST::ExpressionNode *ast);
- Result sourceElement(AST::SourceElement *ast);
- UiMember uiObjectMember(AST::UiObjectMember *ast);
+ void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
+ const BytecodeGenerator::Label *iffalse,
+ bool trueBlockFollowsCondition);
+ Reference expression(AST::ExpressionNode *ast);
void accept(AST::Node *node);
- void functionBody(AST::FunctionBody *ast);
void program(AST::Program *ast);
- void sourceElements(AST::SourceElements *ast);
- void variableDeclaration(AST::VariableDeclaration *ast);
+ void statementList(AST::StatementList *ast);
+ void variableDeclaration(AST::PatternElement *ast);
void variableDeclarationList(AST::VariableDeclarationList *ast);
- QV4::IR::Expr *identifier(const QString &name, int line = 0, int col = 0);
- // Hook provided to implement QML lookup semantics
- virtual QV4::IR::Expr *fallbackNameLookup(const QString &name, int line, int col);
+ Reference targetForPatternElement(AST::PatternElement *p);
+ void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false);
+ void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false);
+ void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false);
+ void destructurePattern(AST::Pattern *p, const Reference &rhs);
+
+ Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
+
+ // Hooks provided to implement QML lookup semantics
+ virtual bool canAccelerateGlobalLookups() const { return true; }
+ virtual Reference fallbackNameLookup(const QString &name);
+
virtual void beginFunctionBodyHook() {}
+ void emitReturn(const Reference &expr);
+
// nodes
bool visit(AST::ArgumentList *ast) override;
bool visit(AST::CaseBlock *ast) override;
@@ -355,16 +576,10 @@ protected:
bool visit(AST::CaseClauses *ast) override;
bool visit(AST::Catch *ast) override;
bool visit(AST::DefaultClause *ast) override;
- bool visit(AST::ElementList *ast) override;
bool visit(AST::Elision *ast) override;
bool visit(AST::Finally *ast) override;
bool visit(AST::FormalParameterList *ast) override;
- bool visit(AST::FunctionBody *ast) override;
bool visit(AST::Program *ast) override;
- bool visit(AST::PropertyNameAndValue *ast) override;
- bool visit(AST::PropertyAssignmentList *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- bool visit(AST::SourceElements *ast) override;
bool visit(AST::StatementList *ast) override;
bool visit(AST::UiArrayMemberList *ast) override;
bool visit(AST::UiImport *ast) override;
@@ -375,20 +590,27 @@ protected:
bool visit(AST::UiParameterList *ast) override;
bool visit(AST::UiProgram *ast) override;
bool visit(AST::UiQualifiedId *ast) override;
- bool visit(AST::UiQualifiedPragmaId *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
bool visit(AST::VariableDeclarationList *ast) override;
+ bool visit(AST::PatternElement *ast) override;
+ bool visit(AST::PatternElementList *ast) override;
+ bool visit(AST::PatternProperty *ast) override;
+ bool visit(AST::PatternPropertyList *ast) override;
+
+ bool visit(AST::ExportDeclaration *ast) override;
+
// expressions
bool visit(AST::Expression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
+ bool visit(AST::ArrayPattern *ast) override;
bool visit(AST::ArrayMemberExpression *ast) override;
bool visit(AST::BinaryExpression *ast) override;
bool visit(AST::CallExpression *ast) override;
bool visit(AST::ConditionalExpression *ast) override;
bool visit(AST::DeleteExpression *ast) override;
bool visit(AST::FalseLiteral *ast) override;
+ bool visit(AST::SuperLiteral *ast) override;
bool visit(AST::FieldMemberExpression *ast) override;
+ bool visit(AST::TaggedTemplate *ast) override;
bool visit(AST::FunctionExpression *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::NestedExpression *ast) override;
@@ -397,13 +619,14 @@ protected:
bool visit(AST::NotExpression *ast) override;
bool visit(AST::NullExpression *ast) override;
bool visit(AST::NumericLiteral *ast) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
bool visit(AST::PostDecrementExpression *ast) override;
bool visit(AST::PostIncrementExpression *ast) override;
bool visit(AST::PreDecrementExpression *ast) override;
bool visit(AST::PreIncrementExpression *ast) override;
bool visit(AST::RegExpLiteral *ast) override;
bool visit(AST::StringLiteral *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::TildeExpression *ast) override;
bool visit(AST::TrueLiteral *ast) override;
@@ -412,10 +635,9 @@ protected:
bool visit(AST::UnaryPlusExpression *ast) override;
bool visit(AST::VoidExpression *ast) override;
bool visit(AST::FunctionDeclaration *ast) override;
-
- // source elements
- bool visit(AST::FunctionSourceElement *ast) override;
- bool visit(AST::StatementSourceElement *ast) override;
+ bool visit(AST::YieldExpression *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ bool visit(AST::ClassDeclaration *ast) override;
// statements
bool visit(AST::Block *ast) override;
@@ -429,8 +651,6 @@ protected:
bool visit(AST::ForStatement *ast) override;
bool visit(AST::IfStatement *ast) override;
bool visit(AST::LabelledStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
bool visit(AST::ReturnStatement *ast) override;
bool visit(AST::SwitchStatement *ast) override;
bool visit(AST::ThrowStatement *ast) override;
@@ -447,7 +667,7 @@ protected:
bool visit(AST::UiScriptBinding *ast) override;
bool visit(AST::UiSourceElement *ast) override;
- bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(QV4::IR::Expr* expr, const AST::SourceLocation &loc);
+ bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc);
virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
@@ -457,117 +677,129 @@ public:
QList<QQmlError> qmlErrors() const;
#endif
-protected:
- Result _expr;
- QString _property;
- UiMember _uiMember;
- QV4::IR::Module *_module;
- QV4::IR::Function *_function;
- QV4::IR::BasicBlock *_block;
- QV4::IR::BasicBlock *_exitBlock;
- unsigned _returnAddress;
- Environment *_variableEnvironment;
- Loop *_loop;
- AST::LabelledStatement *_labelledStatement;
- ScopeAndFinally *_scopeAndFinally;
- QHash<AST::Node *, Environment *> _envMap;
- QHash<AST::FunctionExpression *, int> _functionMap;
- QStack<QV4::IR::BasicBlock *> _exceptionHandlers;
- bool _strictMode;
+ Reference binopHelper(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(AST::ArgumentList *args);
+ void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
- bool _fileNameIsUrl;
- bool hasError;
- QList<QQmlJS::DiagnosticMessage> _errors;
+ Arguments pushTemplateArgs(AST::TemplateLiteral *args);
+ void createTemplateObject(AST::TemplateLiteral *t);
- class ScanFunctions: protected Visitor
- {
- typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
- public:
- ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode);
- void operator()(AST::Node *node);
+ void setUseFastLookups(bool b) { useFastLookups = b; }
- void enterEnvironment(AST::Node *node, CompilationMode compilationMode);
- void leaveEnvironment();
+ void handleTryCatch(AST::TryStatement *ast);
+ void handleTryFinally(AST::TryStatement *ast);
- void enterQmlScope(AST::Node *ast, const QString &name)
- { enterFunction(ast, name, /*formals*/0, /*body*/0, /*expr*/0, /*isExpression*/false); }
- void enterQmlFunction(AST::FunctionDeclaration *ast)
- { enterFunction(ast, false, false); }
+ Reference referenceForName(const QString &name, bool lhs, const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation());
- protected:
- using Visitor::visit;
- using Visitor::endVisit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
+ static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
- void checkDirectivePrologue(AST::SourceElements *ast);
+ Context *currentContext() const { return _context; }
+ BytecodeGenerator *generator() const { return bytecodeGenerator; }
- void checkName(const QStringRef &name, const AST::SourceLocation &loc);
- void checkForArguments(AST::FormalParameterList *parameters);
+ void loadClosure(int index);
- bool visit(AST::Program *ast) override;
- void endVisit(AST::Program *) override;
+ Module *module() const { return _module; }
- bool visit(AST::CallExpression *ast) override;
- bool visit(AST::NewMemberExpression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
- bool visit(AST::IdentifierExpression *ast) override;
- bool visit(AST::ExpressionStatement *ast) override;
- bool visit(AST::FunctionExpression *ast) override;
+ BytecodeGenerator::Label returnLabel() {
+ if (!_returnLabel)
+ _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel());
+ return *_returnLabel;
+ }
- void enterFunction(AST::FunctionExpression *ast, bool enterName, bool isExpression = true);
+ void setGlobalNames(const QSet<QString>& globalNames) {
+ m_globalNames = globalNames;
+ }
- void endVisit(AST::FunctionExpression *) override;
- bool visit(AST::ObjectLiteral *ast) override;
+protected:
+ friend class ScanFunctions;
+ friend struct ControlFlow;
+ friend struct ControlFlowCatch;
+ friend struct ControlFlowFinally;
+ Result _expr;
+ VolatileMemoryLocations _volatileMemoryLocations;
+ Module *_module;
+ int _returnAddress;
+ Context *_context;
+ Context *_functionContext = nullptr;
+ AST::LabelledStatement *_labelledStatement;
+ QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
+ BytecodeGenerator *bytecodeGenerator = nullptr;
+ Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
+ bool _strictMode;
+ bool useFastLookups = true;
+ bool requiresReturnValue = false;
+ bool insideSwitch = false;
+ bool inFormalParameterList = false;
+ bool functionEndsWithReturn = false;
+ bool _tailCallsAreAllowed = true;
+ QSet<QString> m_globalNames;
- bool visit(AST::PropertyGetterSetter *ast) override;
- void endVisit(AST::PropertyGetterSetter *) override;
+ ControlFlow *controlFlow = nullptr;
- bool visit(AST::FunctionDeclaration *ast) override;
- void endVisit(AST::FunctionDeclaration *) override;
+ bool _fileNameIsUrl;
+ bool hasError;
+ QList<QQmlJS::DiagnosticMessage> _errors;
- bool visit(AST::WithStatement *ast) override;
+ class TailCallBlocker
+ {
+ public:
+ TailCallBlocker(Codegen *cg, bool onoff = false)
+ : _cg(cg)
+ , _saved(_cg->_tailCallsAreAllowed)
+ , _onoff(onoff)
+ { _cg->_tailCallsAreAllowed = onoff; }
- bool visit(AST::DoWhileStatement *ast) override;
- bool visit(AST::ForStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
- bool visit(AST::ForEachStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
- bool visit(AST::ThisExpression *ast) override;
+ ~TailCallBlocker()
+ { _cg->_tailCallsAreAllowed = _saved; }
- bool visit(AST::Block *ast) override;
+ void unblock() const
+ { _cg->_tailCallsAreAllowed = _saved; }
- protected:
- void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr, bool isExpression);
+ void reblock() const
+ { _cg->_tailCallsAreAllowed = _onoff; }
- // fields:
+ private:
Codegen *_cg;
- const QString _sourceCode;
- Environment *_variableEnvironment;
- QStack<Environment *> _envStack;
-
- bool _allowFuncDecls;
- CompilationMode defaultProgramMode;
+ bool _saved;
+ bool _onoff;
};
-};
+ class RecursionDepthCheck {
+ public:
+ RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc)
+ : _cg(cg)
+ {
+#ifdef QT_NO_DEBUG
+ const int depthLimit = 4000; // limit to ~1000 deep
+#else
+ const int depthLimit = 1000; // limit to ~250 deep
+#endif // QT_NO_DEBUG
+
+ ++_cg->_recursionDepth;
+ if (_cg->_recursionDepth > depthLimit)
+ _cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
-#ifndef V4_BOOTSTRAP
-class RuntimeCodegen : public Codegen
-{
-public:
- RuntimeCodegen(QV4::ExecutionEngine *engine, bool strict)
- : Codegen(strict)
- , engine(engine)
- {}
+ ~RecursionDepthCheck()
+ { --_cg->_recursionDepth; }
+
+ private:
+ Codegen *_cg;
+ };
+ int _recursionDepth = 0;
+ friend class RecursionDepthCheck;
- void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
- void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
private:
- QV4::ExecutionEngine *engine;
+ VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
-#endif // V4_BOOTSTRAP
+
+}
}
diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp
index d94f7ac238..350f6f9485 100644
--- a/src/qml/compiler/qv4compilationunitmapper.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper.cpp
@@ -59,36 +59,4 @@ CompilationUnitMapper::~CompilationUnitMapper()
close();
}
-bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString)
-{
- if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) {
- *errorString = QStringLiteral("Magic bytes in the header do not match");
- return false;
- }
-
- if (header->version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
- *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
- return false;
- }
-
- if (header->qtVersion != quint32(QT_VERSION)) {
- *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header->qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
- return false;
- }
-
- if (header->sourceTimeStamp) {
- // Files from the resource system do not have any time stamps, so fall back to the application
- // executable.
- if (!sourceTimeStamp.isValid())
- sourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
-
- if (sourceTimeStamp.isValid() && sourceTimeStamp.toMSecsSinceEpoch() != header->sourceTimeStamp) {
- *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
- return false;
- }
- }
-
- return true;
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h
index b24f98df7c..80f914c141 100644
--- a/src/qml/compiler/qv4compilationunitmapper_p.h
+++ b/src/qml/compiler/qv4compilationunitmapper_p.h
@@ -72,8 +72,6 @@ public:
void close();
private:
- static bool verifyHeader(const QV4::CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString);
-
#if defined(Q_OS_UNIX)
size_t length;
#endif
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
index 38dabc41cf..6768bc9596 100644
--- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -42,7 +42,7 @@
#include <sys/mman.h>
#include <functional>
#include <private/qcore_unix_p.h>
-#include <private/qdeferredcleanup_p.h>
+#include <QScopeGuard>
#include <QDateTime>
#include "qv4compileddata_p.h"
@@ -61,7 +61,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- QDeferredCleanup cleanup([fd]{
+ auto cleanup = qScopeGuard([fd]{
qt_safe_close(fd) ;
});
@@ -73,7 +73,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!verifyHeader(&header, sourceTimeStamp, errorString))
+ if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
// Data structure and qt version matched, so now we can access the rest of the file safely.
@@ -92,8 +92,16 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- munmap(dataPtr, length);
+ // Do not unmap the data here.
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ munmap(dataPtr, length);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
index d7a93ae233..779c1288fe 100644
--- a/src/qml/compiler/qv4compilationunitmapper_win.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -40,7 +40,7 @@
#include "qv4compilationunitmapper_p.h"
#include "qv4compileddata_p.h"
-#include <private/qdeferredcleanup_p.h>
+#include <QScopeGuard>
#include <QFileInfo>
#include <QDateTime>
#include <qt_windows.h>
@@ -71,7 +71,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- QDeferredCleanup fileHandleCleanup([handle]{
+ auto fileHandleCleanup = qScopeGuard([handle]{
CloseHandle(handle);
});
@@ -87,27 +87,22 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- if (!verifyHeader(&header, sourceTimeStamp, errorString))
+ if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
- const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? PAGE_EXECUTE_READ : PAGE_READONLY;
- const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ;
-
// Data structure and qt version matched, so now we can access the rest of the file safely.
- HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0);
+ HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0);
if (!fileMappingHandle) {
*errorString = qt_error_string(GetLastError());
return nullptr;
}
- QDeferredCleanup mappingCleanup([fileMappingHandle]{
+ auto mappingCleanup = qScopeGuard([fileMappingHandle]{
CloseHandle(fileMappingHandle);
});
- dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0);
+ dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
if (!dataPtr) {
*errorString = qt_error_string(GetLastError());
return nullptr;
@@ -118,8 +113,15 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- UnmapViewOfFile(dataPtr);
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ UnmapViewOfFile(dataPtr);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index a0dd4c426c..5dd6fca023 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qv4compileddata_p.h"
-#include "qv4jsir_p.h"
#include <private/qv4value_p.h>
#ifndef V4_BOOTSTRAP
#include <private/qv4engine_p.h>
@@ -46,9 +45,11 @@
#include <private/qv4objectproto_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4regexpobject_p.h>
-#include <private/qqmlpropertycache_p.h>
+#include <private/qv4regexp_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmlengine_p.h>
+#include <private/qv4vme_moth_p.h>
+#include <private/qv4module_p.h>
#include "qv4compilationunitmapper_p.h"
#include <QQmlPropertyMap>
#include <QDateTime>
@@ -57,19 +58,18 @@
#include <QScopedValueRollback>
#include <QStandardPaths>
#include <QDir>
+#include <private/qv4identifiertable_p.h>
#endif
#include <private/qqmlirbuilder_p.h>
#include <QCoreApplication>
#include <QCryptographicHash>
#include <QSaveFile>
+#include <QScopeGuard>
-#include <algorithm>
+// generated by qmake:
+#include "qml_compile_hash_p.h"
-#if defined(QT_BUILD_INTERNAL)
-#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST)
-#include <dlfcn.h>
-#endif
-#endif
+#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -77,42 +77,56 @@ namespace QV4 {
namespace CompiledData {
-#if !defined(V4_BOOTSTRAP)
-static QString cacheFilePath(const QUrl &url)
+#if defined(QML_COMPILE_HASH)
+# ifdef Q_OS_LINUX
+// Place on a separate section on Linux so it's easier to check from outside
+// what the hash version is.
+__attribute__((section(".qml_compile_hash")))
+# endif
+const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH;
+static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
+#else
+# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
+
+
+CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, const QString &finalUrlString)
{
- const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- const QString localCachePath = localSourcePath + QLatin1Char('c');
- if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
- return localCachePath;
- QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
- fileNameHash.addData(localSourcePath.toUtf8());
- QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
- QDir::root().mkpath(directory);
- return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix();
+ setUnitData(unitData, nullptr, fileName, finalUrlString);
}
-#endif
#ifndef V4_BOOTSTRAP
-CompilationUnit::CompilationUnit()
- : data(0)
- , engine(0)
- , runtimeLookups(0)
- , runtimeRegularExpressions(0)
- , runtimeClasses(0)
- , totalBindingsCount(0)
- , totalParserStatusCount(0)
- , totalObjectCount(0)
- , metaTypeId(-1)
- , listMetaTypeId(-1)
- , isRegisteredWithEngine(false)
-{}
-
CompilationUnit::~CompilationUnit()
{
unlink();
- if (data && !(data->flags & QV4::CompiledData::Unit::StaticData))
- free(const_cast<Unit *>(data));
- data = 0;
+
+ if (data) {
+ if (data->qmlUnit() != qmlData)
+ free(const_cast<QmlUnit *>(qmlData));
+ qmlData = nullptr;
+
+ if (!(data->flags & QV4::CompiledData::Unit::StaticData))
+ free(const_cast<Unit *>(data));
+ }
+ data = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+ constants = nullptr;
+#endif
+
+ delete [] imports;
+ imports = nullptr;
+}
+
+QString CompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
}
QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
@@ -122,26 +136,21 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
Q_ASSERT(!runtimeStrings);
Q_ASSERT(data);
- runtimeStrings = (QV4::Heap::String **)malloc(data->stringTableSize * sizeof(QV4::Heap::String*));
+ const quint32 stringCount = totalStringCount();
+ runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*));
// memset the strings to 0 in case a GC run happens while we're within the loop below
- memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::Heap::String*));
- for (uint i = 0; i < data->stringTableSize; ++i)
- runtimeStrings[i] = engine->newIdentifier(data->stringAt(i));
+ memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*));
+ for (uint i = 0; i < stringCount; ++i)
+ runtimeStrings[i] = engine->newString(stringAt(i));
runtimeRegularExpressions = new QV4::Value[data->regexpTableSize];
// memset the regexps to 0 in case a GC run happens while we're within the loop below
memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
- int flags = 0;
- if (re->flags & CompiledData::RegExp::RegExp_Global)
- flags |= IR::RegExp::RegExp_Global;
- if (re->flags & CompiledData::RegExp::RegExp_IgnoreCase)
- flags |= IR::RegExp::RegExp_IgnoreCase;
- if (re->flags & CompiledData::RegExp::RegExp_Multiline)
- flags |= IR::RegExp::RegExp_Multiline;
- QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), flags);
- runtimeRegularExpressions[i] = ro;
+ uint f = re->flags;
+ const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
+ runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), flags);
}
if (data->lookupTableSize) {
@@ -158,66 +167,108 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
l->setter = QV4::Lookup::setterGeneric;
else if (type == CompiledData::Lookup::Type_GlobalGetter)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
- else if (type == CompiledData::Lookup::Type_IndexedGetter)
- l->indexedGetter = QV4::Lookup::indexedGetterGeneric;
- else if (type == CompiledData::Lookup::Type_IndexedSetter)
- l->indexedSetter = QV4::Lookup::indexedSetterGeneric;
-
- for (int j = 0; j < QV4::Lookup::Size; ++j)
- l->classList[j] = 0;
- l->level = -1;
- l->index = UINT_MAX;
l->nameIndex = compiledLookups[i].nameIndex;
}
}
if (data->jsClassTableSize) {
- runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*));
+ runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ // memset the regexps to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
- QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object];
+ runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
for (int j = 0; j < memberCount; ++j, ++member)
- klass = klass->addMember(runtimeStrings[member->nameOffset]->identifier, member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
-
- runtimeClasses[i] = klass;
+ runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
}
}
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- Value *bigEndianConstants = new Value[data->constantTableSize];
- const LEUInt64 *littleEndianConstants = data->constants();
- for (uint i = 0; i < data->constantTableSize; ++i)
- bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]);
- constants = bigEndianConstants;
-#else
- constants = reinterpret_cast<const Value*>(data->constants());
-#endif
+ runtimeFunctions.resize(data->functionTableSize);
+ for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
+ const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
+ runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction);
+ }
+
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope);
+
+ runtimeBlocks.resize(data->blockTableSize);
+ for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
+ const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
+ ic = engine->internalClasses(EngineBase::Class_CallContext);
- linkBackendToEngine(engine);
+ // first locals
+ const quint32_le *localsIndices = compiledBlock->localsTable();
+ for (quint32 j = 0; j < compiledBlock->nLocals; ++j)
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]), Attr_NotConfigurable);
+ runtimeBlocks[i] = ic->d();
+ }
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Constant table";
+ Moth::dumpConstantTable(constants, data->constantTableSize);
+ qDebug() << "=== String table";
+ for (uint i = 0, end = totalStringCount(); i < end; ++i)
+ qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
+ qDebug() << "=== Closure table";
+ for (uint i = 0; i < data->functionTableSize; ++i)
+ qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
+ qDebug() << "root function at index " << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0);
+ }
if (data->indexOfRootFunction != -1)
return runtimeFunctions[data->indexOfRootFunction];
else
- return 0;
+ return nullptr;
+}
+
+Heap::Object *CompilationUnit::templateObjectAt(int index) const
+{
+ Q_ASSERT(index < int(data->templateObjectTableSize));
+ if (!templateObjects.size())
+ templateObjects.resize(data->templateObjectTableSize);
+ Heap::Object *o = templateObjects.at(index);
+ if (o)
+ return o;
+
+ // create the template object
+ Scope scope(engine);
+ const CompiledData::TemplateObject *t = data->templateObjectAt(index);
+ Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size));
+ Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size));
+ ScopedValue s(scope);
+ for (uint i = 0; i < t->size; ++i) {
+ s = runtimeStrings[t->stringIndexAt(i)];
+ a->arraySet(i, s);
+ s = runtimeStrings[t->rawStringIndexAt(i)];
+ raw->arraySet(i, s);
+ }
+
+ ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1);
+ a->defineReadonlyProperty(QStringLiteral("raw"), raw);
+ ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1);
+
+ templateObjects[index] = a->objectValue()->d();
+ return templateObjects.at(index);
}
void CompilationUnit::unlink()
{
if (engine)
- engine->compilationUnits.erase(engine->compilationUnits.find(this));
+ nextCompilationUnit.remove();
if (isRegisteredWithEngine) {
- Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject));
- QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine);
- qmlEngine->unregisterInternalCompositeType(this);
+ Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0));
+ if (qmlEngine)
+ qmlEngine->unregisterInternalCompositeType(this);
+ QQmlMetaType::unregisterInternalCompositeType(this);
isRegisteredWithEngine = false;
}
propertyCaches.clear();
- for (int ii = 0; ii < dependentScripts.count(); ++ii)
- dependentScripts.at(ii)->release();
dependentScripts.clear();
typeNameCache = nullptr;
@@ -225,75 +276,87 @@ void CompilationUnit::unlink()
qDeleteAll(resolvedTypes);
resolvedTypes.clear();
- engine = 0;
+ engine = nullptr;
+ qmlEngine = nullptr;
free(runtimeStrings);
- runtimeStrings = 0;
+ runtimeStrings = nullptr;
delete [] runtimeLookups;
- runtimeLookups = 0;
+ runtimeLookups = nullptr;
delete [] runtimeRegularExpressions;
- runtimeRegularExpressions = 0;
+ runtimeRegularExpressions = nullptr;
free(runtimeClasses);
- runtimeClasses = 0;
- qDeleteAll(runtimeFunctions);
+ runtimeClasses = nullptr;
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ f->destroy();
runtimeFunctions.clear();
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- delete [] constants;
-#endif
}
void CompilationUnit::markObjects(QV4::MarkStack *markStack)
{
- for (uint i = 0; i < data->stringTableSize; ++i)
- if (runtimeStrings[i])
- runtimeStrings[i]->mark(markStack);
+ if (runtimeStrings) {
+ for (uint i = 0, end = totalStringCount(); i < end; ++i)
+ if (runtimeStrings[i])
+ runtimeStrings[i]->mark(markStack);
+ }
if (runtimeRegularExpressions) {
for (uint i = 0; i < data->regexpTableSize; ++i)
runtimeRegularExpressions[i].mark(markStack);
}
-}
+ if (runtimeClasses) {
+ for (uint i = 0; i < data->jsClassTableSize; ++i)
+ if (runtimeClasses[i])
+ runtimeClasses[i]->mark(markStack);
+ }
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ if (f && f->internalClass)
+ f->internalClass->mark(markStack);
+ for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks))
+ if (c)
+ c->mark(markStack);
+
+ for (QV4::Heap::Object *o : qAsConst(templateObjects))
+ if (o)
+ o->mark(markStack);
+
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].markObjects(markStack);
+ }
-void CompilationUnit::destroy()
-{
- QQmlEngine *qmlEngine = 0;
- if (engine && engine->v8Engine)
- qmlEngine = engine->v8Engine->engine();
- if (qmlEngine)
- QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
- else
- delete this;
+ if (m_module)
+ m_module->mark(markStack);
}
-IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (it == namedObjectsPerComponentCache.end()) {
- IdentifierHash<int> namedObjectCache(engine);
- const CompiledData::Object *component = data->objectAt(componentObjectIndex);
- const LEUInt32 *namedObjectIndexPtr = component->namedObjectsInComponentTable();
- for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
- const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
- namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
- }
- it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
+ IdentifierHash namedObjectCache(engine);
+ const CompiledData::Object *component = objectAt(componentObjectIndex);
+ const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
+ for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
+ const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
}
- return *it;
+ return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
-void CompilationUnit::finalize(QQmlEnginePrivate *engine)
+void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
{
+ this->qmlEngine = qmlEngine;
+
// Add to type registry of composites
- if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject))
- engine->registerInternalCompositeType(this);
- else {
- const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject);
+ if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
+ QQmlMetaType::registerInternalCompositeType(this);
+ qmlEngine->registerInternalCompositeType(this);
+ } else {
+ const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
if (typeRef->compilationUnit) {
metaTypeId = typeRef->compilationUnit->metaTypeId;
listMetaTypeId = typeRef->compilationUnit->listMetaTypeId;
} else {
- metaTypeId = typeRef->type->typeId();
- listMetaTypeId = typeRef->type->qListTypeId();
+ metaTypeId = typeRef->type.typeId();
+ listMetaTypeId = typeRef->type.qListTypeId();
}
}
@@ -301,12 +364,12 @@ void CompilationUnit::finalize(QQmlEnginePrivate *engine)
int bindingCount = 0;
int parserStatusCount = 0;
int objectCount = 0;
- for (quint32 i = 0; i < data->nObjects; ++i) {
- const QV4::CompiledData::Object *obj = data->objectAt(i);
+ for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
+ const QV4::CompiledData::Object *obj = objectAt(i);
bindingCount += obj->nBindings;
if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
- if (QQmlType *qmlType = typeRef->type) {
- if (qmlType->parserStatusCast() != -1)
+ if (typeRef->type.isValid()) {
+ if (typeRef->type.parserStatusCast() != -1)
++parserStatusCount;
}
++objectCount;
@@ -341,58 +404,249 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe
sizeof(data->dependencyMD5Checksum)) == 0;
}
-bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString)
+QStringList CompilationUnit::moduleRequests() const
{
- if (!QQmlFile::isLocalFile(url)) {
- *errorString = QStringLiteral("File has to be a local file.");
- return false;
- }
+ QStringList requests;
+ requests.reserve(data->moduleRequestTableSize);
+ for (uint i = 0; i < data->moduleRequestTableSize; ++i)
+ requests << stringAt(data->moduleRequestTable()[i]);
+ return requests;
+}
- const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
+Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
+{
+ if (isESModule() && m_module)
+ return m_module;
- CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString);
- if (!mappedUnit)
- return false;
+ if (data->indexOfRootFunction < 0)
+ return nullptr;
- const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
- QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit);
+ if (!this->engine)
+ linkToEngine(engine);
- if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
- *errorString = QStringLiteral("QML source file has moved to a different location.");
- return false;
+ Scope scope(engine);
+ Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this));
+
+ if (isESModule())
+ m_module = module->d();
+
+ for (const QString &request: moduleRequests()) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
+ if (engine->hasException)
+ return nullptr;
+ dependentModuleUnit->instantiate(engine);
}
- {
- const QString foundArchitecture = stringAt(data->architectureIndex);
- const QString expectedArchitecture = QSysInfo::buildAbi();
- if (foundArchitecture != expectedArchitecture) {
- *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture);
- return false;
+ ScopedString importName(scope);
+
+ const uint importCount = data->importEntryTableSize;
+ imports = new const Value *[importCount];
+ memset(imports, 0, importCount * sizeof(Value *));
+ for (uint i = 0; i < importCount; ++i) {
+ const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ importName = runtimeStrings[entry.importName];
+ const Value *valuePtr = dependentModuleUnit->resolveExport(importName);
+ if (!valuePtr) {
+ QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
+ referenceErrorMessage += importName->toQString();
+ engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ return nullptr;
}
+ imports[i] = valuePtr;
}
- {
- const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex);
- const QString expectedCodeGenerator = iselFactory->codeGeneratorName;
- if (foundCodeGenerator != expectedCodeGenerator) {
- *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator);
- return false;
+ for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+
+ ScopedString importName(scope, runtimeStrings[entry.importName]);
+ if (!dependentModuleUnit->resolveExport(importName)) {
+ QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
+ referenceErrorMessage += importName->toQString();
+ engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column);
+ return nullptr;
}
}
- if (!memoryMapCode(errorString))
- return false;
+ return module->d();
+}
- dataPtrChange.commit();
- free(const_cast<Unit*>(oldDataPtr));
- backingFile.reset(cacheFile.take());
- return true;
+const Value *CompilationUnit::resolveExport(QV4::String *exportName)
+{
+ QVector<ResolveSetEntry> resolveSet;
+ return resolveExportRecursively(exportName, &resolveSet);
+}
+
+QStringList CompilationUnit::exportedNames() const
+{
+ QStringList names;
+ QVector<const CompiledData::CompilationUnit*> exportNameSet;
+ getExportedNamesRecursively(&names, &exportNameSet);
+ names.sort();
+ auto last = std::unique(names.begin(), names.end());
+ names.erase(last, names.end());
+ return names;
+}
+
+const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
+{
+ if (!m_module)
+ return nullptr;
+
+ for (const auto &entry: *resolveSet)
+ if (entry.module == this && entry.exportName->isEqualTo(exportName))
+ return nullptr;
+
+ (*resolveSet) << ResolveSetEntry(this, exportName);
+
+ if (exportName->toQString() == QLatin1String("*"))
+ return &m_module->self;
+
+ Scope scope(engine);
+
+ if (auto localExport = lookupNameInExportTable(data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) {
+ ScopedString localName(scope, runtimeStrings[localExport->localName]);
+ uint index = m_module->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey());
+ if (index == UINT_MAX)
+ return nullptr;
+ if (index >= m_module->scope->locals.size)
+ return imports[index - m_module->scope->locals.size];
+ return &m_module->scope->locals[index];
+ }
+
+ if (auto indirectExport = lookupNameInExportTable(data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(indirectExport->moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+ ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
+ return dependentModuleUnit->resolveExportRecursively(importName, resolveSet);
+ }
+
+
+ if (exportName->toQString() == QLatin1String("default"))
+ return nullptr;
+
+ const Value *starResolution = nullptr;
+
+ for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+
+ const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet);
+ // ### handle ambiguous
+ if (resolution) {
+ if (!starResolution) {
+ starResolution = resolution;
+ continue;
+ }
+ if (resolution != starResolution)
+ return nullptr;
+ }
+ }
+
+ return starResolution;
+}
+
+const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
+{
+ const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
+ auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
+ return stringAt(lhs.exportName) < name->toQString();
+ });
+ if (matchingExport == lastExportEntry || stringAt(matchingExport->exportName) != name->toQString())
+ return nullptr;
+ return matchingExport;
+}
+
+void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit*> *exportNameSet, bool includeDefaultExport) const
+{
+ if (exportNameSet->contains(this))
+ return;
+ exportNameSet->append(this);
+
+ const auto append = [names, includeDefaultExport](const QString &name) {
+ if (!includeDefaultExport && name == QLatin1String("default"))
+ return;
+ names->append(name);
+ };
+
+ for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
+ append(stringAt(entry.exportName));
+ }
+
+ for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
+ append(stringAt(entry.exportName));
+ }
+
+ for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return;
+ dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false);
+ }
+}
+
+void CompilationUnit::evaluate()
+{
+ QV4::Scope scope(engine);
+ QV4::Scoped<Module> module(scope, m_module);
+ module->evaluate();
+}
+
+void CompilationUnit::evaluateModuleRequests()
+{
+ for (const QString &request: moduleRequests()) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
+ if (engine->hasException)
+ return;
+ dependentModuleUnit->evaluate();
+ if (engine->hasException)
+ return;
+ }
}
-bool CompilationUnit::memoryMapCode(QString *errorString)
+bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
{
- *errorString = QStringLiteral("Missing code mapping backend");
+ if (!QQmlFile::isLocalFile(url)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
+
+ const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
+ for (const QString &cachePath : cachePaths) {
+ CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString);
+ if (!mappedUnit)
+ continue;
+
+ const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
+ const Unit *oldData = data;
+ auto dataPtrRevert = qScopeGuard([this, oldData](){
+ setUnitData(oldData);
+ });
+ setUnitData(mappedUnit);
+
+ if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
+ *errorString = QStringLiteral("QML source file has moved to a different location.");
+ continue;
+ }
+
+ dataPtrRevert.dismiss();
+ free(const_cast<Unit*>(oldDataPtr));
+ backingFile.reset(cacheFile.take());
+ return true;
+ }
+
return false;
}
@@ -416,7 +670,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
- const QString outputFileName = cacheFilePath(unitUrl);
+ const QString outputFileName = localCacheFilePath(unitUrl);
#endif
#if QT_CONFIG(temporaryfile)
@@ -435,17 +689,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
unitPtr->flags |= Unit::StaticData;
- prepareCodeOffsetsForDiskStorage(unitPtr);
-
qint64 headerWritten = cacheFile.write(modifiedUnit);
if (headerWritten != modifiedUnit.size()) {
*errorString = cacheFile.errorString();
return false;
}
- if (!saveCodeToDisk(&cacheFile, unitPtr, errorString))
- return false;
-
if (!cacheFile.commit()) {
*errorString = cacheFile.errorString();
return false;
@@ -459,152 +708,73 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
#endif // QT_CONFIG(temporaryfile)
}
-void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit)
+void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit,
+ const QString &fileName, const QString &finalUrlString)
{
- Q_UNUSED(unit);
-}
-
-bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString)
-{
- Q_UNUSED(device);
- Q_UNUSED(unit);
- *errorString = QStringLiteral("Saving code to disk is not supported in this configuration");
- return false;
-}
-
-Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
-{
- if (!irDocument->javaScriptCompilationUnit->data)
- return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable);
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit;
- QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(compilationUnit->data);
- auto ensureWritableUnit = [&jsUnit, &compilationUnit]() {
- if (jsUnit == compilationUnit->data) {
- char *unitCopy = (char*)malloc(jsUnit->unitSize);
- memcpy(unitCopy, jsUnit, jsUnit->unitSize);
- jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy);
- }
- };
-
- QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable;
-
- if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) {
- ensureWritableUnit();
- jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName);
- }
-
- // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example)
- // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize
- // the string table.
- QVector<quint32> changedSignals;
- QVector<QQmlJS::AST::FormalParameterList*> changedSignalParameters;
- for (QmlIR::Object *o: qAsConst(irDocument->objects)) {
- for (QmlIR::Binding *binding = o->firstBinding(); binding; binding = binding->next) {
- if (!(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression))
- continue;
-
- quint32 functionIndex = binding->value.compiledScriptIndex;
- QmlIR::CompiledFunctionOrExpression *foe = o->functionsAndExpressions->slowAt(functionIndex);
- if (!foe)
- continue;
-
- // save absolute index
- changedSignals << o->runtimeFunctionIndices.at(functionIndex);
-
- Q_ASSERT(foe->node);
- Q_ASSERT(QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node));
-
- QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals;
- changedSignalParameters << parameters;
-
- for (; parameters; parameters = parameters->next)
- stringTable.registerString(parameters->name.toString());
- }
- }
-
- QVector<quint32> signalParameterNameTable;
- quint32 signalParameterNameTableOffset = jsUnit->unitSize;
-
- // Update signal signatures
- if (!changedSignals.isEmpty()) {
- if (jsUnit == compilationUnit->data) {
- char *unitCopy = (char*)malloc(jsUnit->unitSize);
- memcpy(unitCopy, jsUnit, jsUnit->unitSize);
- jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy);
- }
-
- for (int i = 0; i < changedSignals.count(); ++i) {
- const uint functionIndex = changedSignals.at(i);
- // The data is now read-write due to the copy above, so the const_cast is ok.
- QV4::CompiledData::Function *function = const_cast<QV4::CompiledData::Function *>(jsUnit->functionAt(functionIndex));
- Q_ASSERT(function->nFormals == quint32(0));
-
- function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex];
-
- for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i);
- parameters; parameters = parameters->next) {
- signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString()));
- function->nFormals = function->nFormals + 1;
- }
-
- // Hack to ensure an activation is created.
- function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval;
-
- signalParameterNameTableOffset += function->nFormals * sizeof(quint32);
- }
- }
+ data = unitData;
+ qmlData = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+#endif
+ constants = nullptr;
+ m_fileName.clear();
+ m_finalUrlString.clear();
+ if (!data)
+ return;
- if (!signalParameterNameTable.isEmpty()) {
- ensureWritableUnit();
- Q_ASSERT(jsUnit != compilationUnit->data);
- const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32);
- uint newSize = jsUnit->unitSize + signalParameterTableSize;
- const uint oldSize = jsUnit->unitSize;
- char *unitWithSignalParameters = (char*)realloc(jsUnit, newSize);
- memcpy(unitWithSignalParameters + oldSize, signalParameterNameTable.constData(), signalParameterTableSize);
- jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitWithSignalParameters);
- jsUnit->unitSize = newSize;
- }
+ qmlData = qmlUnit ? qmlUnit : data->qmlUnit();
- if (jsUnit != compilationUnit->data)
- jsUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ Value *bigEndianConstants = new Value[data->constantTableSize];
+ const quint64_le *littleEndianConstants = data->constants();
+ for (uint i = 0; i < data->constantTableSize; ++i)
+ bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]);
+ constants = bigEndianConstants;
+#else
+ constants = reinterpret_cast<const Value*>(data->constants());
+#endif
- return jsUnit;
+ m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex);
+ m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
}
-QString Binding::valueAsString(const Unit *unit) const
+#ifndef V4_BOOTSTRAP
+QString Binding::valueAsString(const CompilationUnit *unit) const
{
switch (type) {
case Type_Script:
case Type_String:
return unit->stringAt(stringIndex);
+ case Type_Null:
+ return QStringLiteral("null");
case Type_Boolean:
return value.b ? QStringLiteral("true") : QStringLiteral("false");
case Type_Number:
- return QString::number(valueAsNumber());
+ return QString::number(valueAsNumber(unit->constants));
case Type_Invalid:
return QString();
#if !QT_CONFIG(translation)
case Type_TranslationById:
case Type_Translation:
- return unit->stringAt(stringIndex);
+ return unit->stringAt(unit->unitData()->translations()[value.translationDataIndex].stringIndex);
#else
case Type_TranslationById: {
- QByteArray id = unit->stringAt(stringIndex).toUtf8();
- return qtTrId(id.constData(), value.translationData.number);
+ const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex];
+ QByteArray id = unit->stringAt(translation.stringIndex).toUtf8();
+ return qtTrId(id.constData(), translation.number);
}
case Type_Translation: {
+ const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex];
// This code must match that in the qsTr() implementation
- const QString &path = unit->stringAt(unit->sourceFileIndex);
+ const QString &path = unit->fileName();
int lastSlash = path.lastIndexOf(QLatin1Char('/'));
QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5)
: QStringRef();
QByteArray contextUtf8 = context.toUtf8();
- QByteArray comment = unit->stringAt(value.translationData.commentIndex).toUtf8();
- QByteArray text = unit->stringAt(stringIndex).toUtf8();
+ QByteArray comment = unit->stringAt(translation.commentIndex).toUtf8();
+ QByteArray text = unit->stringAt(translation.stringIndex).toUtf8();
return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
- comment.constData(), value.translationData.number);
+ comment.constData(), translation.number);
}
#endif
default:
@@ -656,7 +826,7 @@ QString Binding::escapedString(const QString &string)
return tmp;
}
-QString Binding::valueAsScriptString(const Unit *unit) const
+QString Binding::valueAsScriptString(const CompilationUnit *unit) const
{
if (type == Type_String)
return escapedString(unit->stringAt(stringIndex));
@@ -664,13 +834,12 @@ QString Binding::valueAsScriptString(const Unit *unit) const
return valueAsString(unit);
}
-#ifndef V4_BOOTSTRAP
/*!
Returns the property cache, if one alread exists. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const
{
- if (type)
+ if (type.isValid())
return typePropertyCache;
else
return compilationUnit->rootPropertyCache();
@@ -679,12 +848,12 @@ QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
/*!
Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
{
if (typePropertyCache) {
return typePropertyCache;
- } else if (type) {
- typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject());
+ } else if (type.isValid()) {
+ typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion);
return typePropertyCache;
} else {
return compilationUnit->rootPropertyCache();
@@ -693,12 +862,12 @@ QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine
bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine)
{
- if (type) {
+ if (type.isValid()) {
bool ok = false;
hash->addData(createPropertyCache(engine)->checksum(&ok));
return ok;
}
- hash->addData(compilationUnit->data->md5Checksum, sizeof(compilationUnit->data->md5Checksum));
+ hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum));
return true;
}
@@ -714,43 +883,16 @@ bool qtTypeInherits(const QMetaObject *mo) {
void ResolvedTypeReference::doDynamicTypeCheck()
{
- const QMetaObject *mo = 0;
+ const QMetaObject *mo = nullptr;
if (typePropertyCache)
mo = typePropertyCache->firstCppMetaObject();
- else if (type)
- mo = type->metaObject();
+ else if (type.isValid())
+ mo = type.metaObject();
else if (compilationUnit)
mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo);
}
-#if defined(QT_BUILD_INTERNAL)
-
-static QByteArray ownLibraryChecksum()
-{
- static QByteArray libraryChecksum;
- static bool checksumInitialized = false;
- if (checksumInitialized)
- return libraryChecksum;
- checksumInitialized = true;
-#if !defined(QT_NO_DYNAMIC_CAST) && QT_CONFIG(dlopen)
- Dl_info libInfo;
- if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) {
- QFile library(QFile::decodeName(libInfo.dli_fname));
- if (library.open(QIODevice::ReadOnly)) {
- QCryptographicHash hash(QCryptographicHash::Md5);
- hash.addData(&library);
- libraryChecksum = hash.result();
- }
- }
-#else
- // Not implemented.
-#endif
- return libraryChecksum;
-}
-
-#endif
-
bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const
{
for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
@@ -758,18 +900,22 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e
return false;
}
- // This is a bit of a hack to make development easier. When hacking on the code generator
- // the cache files may end up being re-used. To avoid that we also add the checksum of
- // the QtQml library.
-#if defined(QT_BUILD_INTERNAL)
- hash->addData(ownLibraryChecksum());
-#endif
-
return true;
}
#endif
+void CompilationUnit::destroy()
+{
+#if !defined(V4_BOOTSTRAP)
+ if (qmlEngine)
+ QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
+ else
+#endif
+ delete this;
+}
+
+
void Unit::generateChecksum()
{
#ifndef V4_BOOTSTRAP
@@ -788,6 +934,60 @@ void Unit::generateChecksum()
#endif
}
+bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const
+{
+#ifndef V4_BOOTSTRAP
+ if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) {
+ *errorString = QStringLiteral("Magic bytes in the header do not match");
+ return false;
+ }
+
+ if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
+ *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
+ return false;
+ }
+
+ if (qtVersion != quint32(QT_VERSION)) {
+ *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
+ return false;
+ }
+
+ if (sourceTimeStamp) {
+ // Files from the resource system do not have any time stamps, so fall back to the application
+ // executable.
+ if (!expectedSourceTimeStamp.isValid())
+ expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
+
+ if (expectedSourceTimeStamp.isValid() && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) {
+ *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ return false;
+ }
+ }
+
+#if defined(QML_COMPILE_HASH)
+ if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) {
+ *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match");
+ return false;
+ }
+#else
+#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
+
+ return true;
+#else
+ Q_UNUSED(expectedSourceTimeStamp)
+ Q_UNUSED(errorString)
+ return false;
+#endif
+}
+
+Location &Location::operator=(const QQmlJS::AST::SourceLocation &astLocation)
+{
+ line = astLocation.startLine;
+ column = astLocation.startColumn;
+ return *this;
+}
+
}
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index f4ba257cf5..1aaba13241 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -62,19 +62,20 @@
#include <private/qqmlnullablevalue_p.h>
#include <private/qv4identifier_p.h>
#include <private/qflagpointer_p.h>
-#include <private/qjson_p.h>
+#include <private/qendian_p.h>
+#include <private/qqmljsastfwd_p.h>
#ifndef V4_BOOTSTRAP
#include <private/qqmltypenamecache_p.h>
-#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlpropertycachevector_p.h>
+#include "private/qintrusivelist_p.h"
#endif
QT_BEGIN_NAMESPACE
// Bump this whenever the compiler data structures change in an incompatible way.
-#define QV4_DATA_STRUCTURE_VERSION 0x11
+#define QV4_DATA_STRUCTURE_VERSION 0x1b
class QIODevice;
-class QQmlPropertyCache;
class QQmlPropertyData;
class QQmlTypeNameCache;
class QQmlScriptData;
@@ -86,9 +87,10 @@ struct Document;
}
namespace QV4 {
-namespace IR {
-struct Function;
-}
+
+namespace Heap {
+struct Module;
+};
struct Function;
class EvalISelFactory;
@@ -96,13 +98,6 @@ class CompilationUnitMapper;
namespace CompiledData {
-typedef QJsonPrivate::q_littleendian<qint16> LEInt16;
-typedef QJsonPrivate::q_littleendian<quint16> LEUInt16;
-typedef QJsonPrivate::q_littleendian<quint32> LEUInt32;
-typedef QJsonPrivate::q_littleendian<qint32> LEInt32;
-typedef QJsonPrivate::q_littleendian<quint64> LEUInt64;
-typedef QJsonPrivate::q_littleendian<qint64> LEInt64;
-
struct String;
struct Function;
struct Lookup;
@@ -122,85 +117,145 @@ struct TableIterator
bool operator!=(const TableIterator &rhs) const { return index != rhs.index; }
};
-#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
-#pragma pack(push, 1)
-#endif
-
struct Location
{
union {
- QJsonPrivate::qle_bitfield<0, 20> line;
- QJsonPrivate::qle_bitfield<20, 12> column;
+ quint32 _dummy;
+ quint32_le_bitfield<0, 20> line;
+ quint32_le_bitfield<20, 12> column;
};
- Location() { line.val = 0; column.val = 0; }
+ Location() : _dummy(0) { }
+
+ Location &operator=(const QQmlJS::AST::SourceLocation &astLocation);
inline bool operator<(const Location &other) const {
return line < other.line ||
(line == other.line && column < other.column);
}
};
+static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct RegExp
{
enum Flags : unsigned int {
+ RegExp_NoFlags = 0x0,
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
+ RegExp_Multiline = 0x04,
+ RegExp_Unicode = 0x08,
+ RegExp_Sticky = 0x10
};
union {
- QJsonPrivate::qle_bitfield<0, 4> flags;
- QJsonPrivate::qle_bitfield<4, 28> stringIndex;
+ quint32 _dummy;
+ quint32_le_bitfield<0, 5> flags;
+ quint32_le_bitfield<5, 27> stringIndex;
};
- RegExp() { flags.val = 0; stringIndex.val = 0; }
+ RegExp() : _dummy(0) { }
};
+static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Lookup
{
enum Type : unsigned int {
Type_Getter = 0x0,
Type_Setter = 0x1,
- Type_GlobalGetter = 2,
- Type_IndexedGetter = 3,
- Type_IndexedSetter = 4
+ Type_GlobalGetter = 2
};
union {
- QJsonPrivate::qle_bitfield<0, 4> type_and_flags;
- QJsonPrivate::qle_bitfield<4, 28> nameIndex;
+ quint32 _dummy;
+ quint32_le_bitfield<0, 4> type_and_flags;
+ quint32_le_bitfield<4, 28> nameIndex;
};
- Lookup() { type_and_flags.val = 0; nameIndex.val = 0; }
+ Lookup() : _dummy(0) { }
};
+static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClassMember
{
union {
- QJsonPrivate::qle_bitfield<0, 31> nameOffset;
- QJsonPrivate::qle_bitfield<31, 1> isAccessor;
+ quint32 _dummy;
+ quint32_le_bitfield<0, 31> nameOffset;
+ quint32_le_bitfield<31, 1> isAccessor;
};
- JSClassMember() { nameOffset = 0; isAccessor = 0; }
+ JSClassMember() : _dummy(0) { }
};
+static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct JSClass
{
- LEUInt32 nMembers;
+ quint32_le nMembers;
// JSClassMember[nMembers]
static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; }
};
-
+static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+// This data structure is intended to be binary compatible with QStringData/QStaticStringData on
+// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped
+// from a file must be castable to a QStringData regardless of the pointer size. With the first
+// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a
+// ptrdiff_t and thus variable in size.
+// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while
+// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain
+// the same value.
struct String
{
- LEInt32 size;
+ qint32_le refcount; // -1
+ qint32_le size;
+ quint32_le allocAndCapacityReservedFlag; // 0
+ quint32_le offsetOn32Bit;
+ quint64_le offsetOn64Bit;
// uint16 strdata[]
static int calculateSize(const QString &str) {
- return (sizeof(String) + str.length() * sizeof(quint16) + 7) & ~0x7;
+ return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7;
+ }
+};
+static_assert(sizeof(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+// Ensure compatibility with QString
+static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location");
+static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location");
+static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location");
+static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location");
+#if QT_POINTER_SIZE == 8
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location");
+#else
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location");
+#endif
+
+struct CodeOffsetToLine {
+ quint32_le codeOffset;
+ quint32_le line;
+};
+static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Block
+{
+ quint32_le nLocals;
+ quint32_le localsOffset;
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le padding;
+
+ const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+
+ static int calculateSize(int nLocals) {
+ int trailingData = nLocals*sizeof (quint32);
+ size_t size = align(align(sizeof(Block)) + size_t(trailingData));
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
}
};
+static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
@@ -208,77 +263,193 @@ struct Function
{
enum Flags : unsigned int {
IsStrict = 0x1,
- HasDirectEval = 0x2,
- UsesArgumentsObject = 0x4,
- IsNamedExpression = 0x8,
- HasCatchOrWith = 0x10,
- CanUseSimpleCall = 0x20
+ IsArrowFunction = 0x2,
+ IsGenerator = 0x4
};
- // Absolute offset into file where the code for this function is located. Only used when the function
- // is serialized.
- LEUInt64 codeOffset;
- LEUInt64 codeSize;
-
- LEUInt32 nameIndex;
- LEUInt32 nFormals;
- LEUInt32 formalsOffset;
- LEUInt32 nLocals;
- LEUInt32 localsOffset;
- LEUInt32 nInnerFunctions;
+ // Absolute offset into file where the code for this function is located.
+ quint32_le codeOffset;
+ quint32_le codeSize;
+
+ quint32_le nameIndex;
+ quint16_le length;
+ quint16_le nFormals;
+ quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData.
+ quint32_le localsOffset;
+ quint16_le nLocals;
+ quint16_le nLineNumbers;
+ size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
+ quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le firstTemporalDeadZoneRegister;
+ quint16_le sizeOfRegisterTemporalDeadZone;
+ quint16_le nRegisters;
Location location;
// Qml Extensions Begin
- LEUInt32 nDependingIdObjects;
- LEUInt32 dependingIdObjectsOffset; // Array of resolved ID objects
- LEUInt32 nDependingContextProperties;
- LEUInt32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index)
- LEUInt32 nDependingScopeProperties;
- LEUInt32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
+ // Array of resolved ID objects
+ size_t dependingIdObjectsOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
+ quint16_le nDependingIdObjects;
+ quint16_le nDependingContextProperties;
+ // Array of int pairs (property index and notify index)
+ size_t dependingContextPropertiesOffset() const { return dependingIdObjectsOffset() + nDependingIdObjects * sizeof(quint32); }
+ quint16_le nDependingScopeProperties;
+ // Array of int pairs (property index and notify index)
+ size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); }
// Qml Extensions End
-// quint32 formalsIndex[nFormals]
-// quint32 localsIndex[nLocals]
-// quint32 offsetForInnerFunctions[nInnerFunctions]
-// Function[nInnerFunctions]
+ typedef quint16_le TraceInfoCount;
+ TraceInfoCount nTraceInfos;
+ static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); }
+
+ quint32_le nLabelInfos;
+ size_t labelInfosOffset() const { return dependingScopePropertiesOffset() + nDependingScopeProperties; }
// Keep all unaligned data at the end
quint8 flags;
+ quint8 padding1;
+ quint16 padding2;
+
+ // quint32 formalsIndex[nFormals]
+ // quint32 localsIndex[nLocals]
- const LEUInt32 *formalsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + formalsOffset); }
- const LEUInt32 *localsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + localsOffset); }
- const LEUInt32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); }
- const LEUInt32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); }
- const LEUInt32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); }
+ const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
+ const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+ const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); }
+ const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset()); }
+ const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset()); }
+ const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset()); }
// --- QQmlPropertyCacheCreator interface
- const LEUInt32 *formalsBegin() const { return formalsTable(); }
- const LEUInt32 *formalsEnd() const { return formalsTable() + nFormals; }
+ const quint32_le *formalsBegin() const { return formalsTable(); }
+ const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
// ---
+ const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); }
+
+ const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
+
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
- static int calculateSize(int nFormals, int nLocals, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies) {
- return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7;
+ static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int labelInfoSize, int codeSize) {
+ int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + labelInfoSize +
+ 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine);
+ size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+static_assert(sizeof(Function) == 60, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Method {
+ enum Type {
+ Regular,
+ Getter,
+ Setter
+ };
+
+ quint32_le name;
+ quint32_le type;
+ quint32_le function;
+};
+static_assert(sizeof(Method) == 12, "Method structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Class
+{
+ quint32_le nameIndex;
+ quint32_le scopeIndex;
+ quint32_le constructorFunction;
+ quint32_le nStaticMethods;
+ quint32_le nMethods;
+ quint32_le methodTableOffset;
+
+ const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); }
+
+ static int calculateSize(int nStaticMethods, int nMethods) {
+ int trailingData = (nStaticMethods + nMethods) * sizeof(Method);
+ size_t size = align(sizeof(Class) + trailingData);
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
}
};
+static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct TemplateObject
+{
+ quint32_le size;
+
+ static int calculateSize(int size) {
+ int trailingData = 2 * size * sizeof(quint32_le);
+ size_t s = align(sizeof(TemplateObject) + trailingData);
+ Q_ASSERT(s < INT_MAX);
+ return int(s);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+
+ const quint32_le *stringTable() const {
+ return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1));
+ }
+
+ uint stringIndexAt(uint i) const {
+ return stringTable()[i];
+ }
+ uint rawStringIndexAt(uint i) const {
+ return stringTable()[size + i];
+ }
+};
+static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct ExportEntry
+{
+ quint32_le exportName;
+ quint32_le moduleRequest;
+ quint32_le importName;
+ quint32_le localName;
+ Location location;
+};
+static_assert(sizeof(ExportEntry) == 20, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct ImportEntry
+{
+ quint32_le moduleRequest;
+ quint32_le importName;
+ quint32_le localName;
+ Location location;
+};
+static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Qml data structures
-struct Q_QML_EXPORT TranslationData {
- LEUInt32 commentIndex;
- LEInt32 number;
+struct Q_QML_EXPORT TranslationData
+{
+ quint32_le stringIndex;
+ quint32_le commentIndex;
+ qint32_le number;
+ quint32_le padding;
};
+static_assert(sizeof(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Q_QML_PRIVATE_EXPORT Binding
{
- LEUInt32 propertyNameIndex;
+ quint32_le propertyNameIndex;
enum ValueType : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
Type_String,
+ Type_Null,
Type_Translation,
Type_TranslationById,
Type_Script,
@@ -297,20 +468,22 @@ struct Q_QML_PRIVATE_EXPORT Binding
IsBindingToAlias = 0x40,
IsDeferredBinding = 0x80,
IsCustomParserBinding = 0x100,
+ IsFunctionExpression = 0x200
};
union {
- QJsonPrivate::qle_bitfield<0, 16> flags;
- QJsonPrivate::qle_bitfield<16, 16> type;
+ quint32_le_bitfield<0, 16> flags;
+ quint32_le_bitfield<16, 16> type;
};
union {
bool b;
- quint64 doubleValue; // do not access directly, needs endian protected access
- LEUInt32 compiledScriptIndex; // used when Type_Script
- LEUInt32 objectIndex;
- TranslationData translationData; // used when Type_Translation
+ quint32_le constantValueIndex;
+ quint32_le compiledScriptIndex; // used when Type_Script
+ quint32_le objectIndex;
+ quint32_le translationDataIndex; // used when Type_Translation
+ quint32 nullMarker;
} value;
- LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings)
+ quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings)
Location location;
Location valueLocation;
@@ -362,26 +535,22 @@ struct Q_QML_PRIVATE_EXPORT Binding
return false;
}
+ bool isFunctionExpression() const { return (flags & IsFunctionExpression); }
+
static QString escapedString(const QString &string);
- bool evaluatesToString() const { return type == Type_String || type == Type_Translation || type == Type_TranslationById; }
+ bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
+ bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
- QString valueAsString(const Unit *unit) const;
- QString valueAsScriptString(const Unit *unit) const;
- double valueAsNumber() const
+#ifndef V4_BOOTSTRAP
+ QString valueAsString(const CompilationUnit *unit) const;
+ QString valueAsScriptString(const CompilationUnit *unit) const;
+#endif
+ double valueAsNumber(const Value *constantTable) const
{
if (type != Type_Number)
return 0.0;
- quint64 intval = qFromLittleEndian<quint64>(value.doubleValue);
- double d;
- memcpy(&d, &intval, sizeof(double));
- return d;
- }
- void setNumberValueInternal(double d)
- {
- quint64 intval;
- memcpy(&intval, &d, sizeof(double));
- value.doubleValue = qToLittleEndian<quint64>(intval);
+ return constantTable[value.constantValueIndex].doubleValue();
}
bool valueAsBoolean() const
@@ -393,18 +562,53 @@ struct Q_QML_PRIVATE_EXPORT Binding
};
+static_assert(sizeof(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct EnumValue
+{
+ quint32_le nameIndex;
+ qint32_le value;
+ Location location;
+};
+static_assert(sizeof(EnumValue) == 12, "EnumValue structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Enum
+{
+ quint32_le nameIndex;
+ quint32_le nEnumValues;
+ Location location;
+
+ const EnumValue *enumValueAt(int idx) const {
+ return reinterpret_cast<const EnumValue*>(this + 1) + idx;
+ }
+
+ static int calculateSize(int nEnumValues) {
+ return (sizeof(Enum)
+ + nEnumValues * sizeof(EnumValue)
+ + 7) & ~0x7;
+ }
+
+ // --- QQmlPropertyCacheCreatorInterface
+ const EnumValue *enumValuesBegin() const { return enumValueAt(0); }
+ const EnumValue *enumValuesEnd() const { return enumValueAt(nEnumValues); }
+ int enumValueCount() const { return nEnumValues; }
+ // ---
+};
+static_assert(sizeof(Enum) == 12, "Enum structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
struct Parameter
{
- LEUInt32 nameIndex;
- LEUInt32 type;
- LEUInt32 customTypeNameIndex;
+ quint32_le nameIndex;
+ quint32_le type;
+ quint32_le customTypeNameIndex;
Location location;
};
+static_assert(sizeof(Parameter) == 16, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Signal
{
- LEUInt32 nameIndex;
- LEUInt32 nParameters;
+ quint32_le nameIndex;
+ quint32_le nParameters;
Location location;
// Parameter parameters[1];
@@ -424,6 +628,7 @@ struct Signal
int parameterCount() const { return nParameters; }
// ---
};
+static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Property
{
@@ -436,14 +641,15 @@ struct Property
IsReadOnly = 0x1
};
- LEUInt32 nameIndex;
+ quint32_le nameIndex;
union {
- QJsonPrivate::qle_bitfield<0, 31> type;
- QJsonPrivate::qle_bitfield<31, 1> flags; // readonly
+ quint32_le_bitfield<0, 31> type;
+ quint32_le_bitfield<31, 1> flags; // readonly
};
- LEUInt32 customTypeNameIndex; // If type >= Custom
+ quint32_le customTypeNameIndex; // If type >= Custom
Location location;
};
+static_assert(sizeof(Property) == 16, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Alias {
enum Flags : unsigned int {
@@ -452,18 +658,18 @@ struct Alias {
AliasPointsToPointerObject = 0x4
};
union {
- QJsonPrivate::qle_bitfield<0, 29> nameIndex;
- QJsonPrivate::qle_bitfield<29, 3> flags;
+ quint32_le_bitfield<0, 29> nameIndex;
+ quint32_le_bitfield<29, 3> flags;
};
union {
- LEUInt32 idIndex; // string index
- QJsonPrivate::qle_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
- QJsonPrivate::qle_bitfield<31, 1> aliasToLocalAlias;
+ quint32_le idIndex; // string index
+ quint32_le_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
+ quint32_le_bitfield<31, 1> aliasToLocalAlias;
};
union {
- LEUInt32 propertyNameIndex; // string index
- LEInt32 encodedMetaPropertyIndex;
- LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
+ quint32_le propertyNameIndex; // string index
+ qint32_le encodedMetaPropertyIndex;
+ quint32_le localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
};
Location location;
Location referenceLocation;
@@ -473,6 +679,7 @@ struct Alias {
return encodedMetaPropertyIndex == -1;
}
};
+static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Object
{
@@ -486,26 +693,28 @@ struct Object
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
- LEUInt32 inheritedTypeNameIndex;
- LEUInt32 idNameIndex;
+ quint32_le inheritedTypeNameIndex;
+ quint32_le idNameIndex;
union {
- QJsonPrivate::qle_bitfield<0, 15> flags;
- QJsonPrivate::qle_bitfield<15, 1> defaultPropertyIsAlias;
- QJsonPrivate::qle_signedbitfield<16, 16> id;
+ quint32_le_bitfield<0, 15> flags;
+ quint32_le_bitfield<15, 1> defaultPropertyIsAlias;
+ qint32_le_bitfield<16, 16> id;
};
- LEInt32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
- LEUInt32 nFunctions;
- LEUInt32 offsetToFunctions;
- LEUInt32 nProperties;
- LEUInt32 offsetToProperties;
- LEUInt32 nAliases;
- LEUInt32 offsetToAliases;
- LEUInt32 nSignals;
- LEUInt32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
- LEUInt32 nBindings;
- LEUInt32 offsetToBindings;
- LEUInt32 nNamedObjectsInComponent;
- LEUInt32 offsetToNamedObjectsInComponent;
+ qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
+ quint16_le nFunctions;
+ quint16_le nProperties;
+ quint32_le offsetToFunctions;
+ quint32_le offsetToProperties;
+ quint32_le offsetToAliases;
+ quint16_le nAliases;
+ quint16_le nEnums;
+ quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects
+ quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
+ quint16_le nSignals;
+ quint16_le nBindings;
+ quint32_le offsetToBindings;
+ quint32_le nNamedObjectsInComponent;
+ quint32_le offsetToNamedObjectsInComponent;
Location location;
Location locationOfIdProperty;
// Function[]
@@ -513,12 +722,13 @@ struct Object
// Signal[]
// Binding[]
- static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings, int nNamedObjectsInComponent)
+ static int calculateSizeExcludingSignalsAndEnums(int nFunctions, int nProperties, int nAliases, int nEnums, int nSignals, int nBindings, int nNamedObjectsInComponent)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
+ nProperties * sizeof(Property)
+ nAliases * sizeof(Alias)
+ + nEnums * sizeof(quint32)
+ nSignals * sizeof(quint32)
+ nBindings * sizeof(Binding)
+ nNamedObjectsInComponent * sizeof(int)
@@ -526,9 +736,9 @@ struct Object
) & ~0x7;
}
- const LEUInt32 *functionOffsetTable() const
+ const quint32_le *functionOffsetTable() const
{
- return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
+ return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
}
const Property *propertyTable() const
@@ -546,21 +756,29 @@ struct Object
return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings);
}
+ const Enum *enumAt(int idx) const
+ {
+ const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToEnums);
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Enum*>(reinterpret_cast<const char*>(this) + offset);
+ }
+
const Signal *signalAt(int idx) const
{
- const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
- const LEUInt32 offset = offsetTable[idx];
+ const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
+ const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset);
}
- const LEUInt32 *namedObjectsInComponentTable() const
+ const quint32_le *namedObjectsInComponentTable() const
{
- return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
+ return reinterpret_cast<const quint32_le*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
}
// --- QQmlPropertyCacheCreator interface
int propertyCount() const { return nProperties; }
int aliasCount() const { return nAliases; }
+ int enumCount() const { return nEnums; }
int signalCount() const { return nSignals; }
int functionCount() const { return nFunctions; }
@@ -573,6 +791,10 @@ struct Object
const Alias *aliasesBegin() const { return aliasTable(); }
const Alias *aliasesEnd() const { return aliasTable() + nAliases; }
+ typedef TableIterator<Enum, Object, &Object::enumAt> EnumIterator;
+ EnumIterator enumsBegin() const { return EnumIterator(this, 0); }
+ EnumIterator enumsEnd() const { return EnumIterator(this, nEnums); }
+
typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator;
SignalIterator signalsBegin() const { return SignalIterator(this, 0); }
SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); }
@@ -580,6 +802,7 @@ struct Object
int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
// ---
};
+static_assert(sizeof(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Import
{
@@ -588,78 +811,111 @@ struct Import
ImportFile = 0x2,
ImportScript = 0x3
};
- LEUInt32 type;
+ quint32_le type;
- LEUInt32 uriIndex;
- LEUInt32 qualifierIndex;
+ quint32_le uriIndex;
+ quint32_le qualifierIndex;
- LEInt32 majorVersion;
- LEInt32 minorVersion;
+ qint32_le majorVersion;
+ qint32_le minorVersion;
Location location;
Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; }
};
+static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct QmlUnit
+{
+ quint32_le nImports;
+ quint32_le offsetToImports;
+ quint32_le nObjects;
+ quint32_le offsetToObjects;
+
+ const Import *importAt(int idx) const {
+ return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
+ }
+
+ const Object *objectAt(int idx) const {
+ const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
+ }
+};
+static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+enum { QmlCompileHashSpace = 48 };
static const char magic_str[] = "qv4cdata";
+extern const char qml_compile_hash[QmlCompileHashSpace + 1];
struct Unit
{
// DO NOT CHANGE THESE FIELDS EVER
char magic[8];
- LEUInt32 version;
- LEUInt32 qtVersion;
- LEInt64 sourceTimeStamp;
- LEUInt32 unitSize; // Size of the Unit and any depending data.
+ quint32_le version;
+ quint32_le qtVersion;
+ qint64_le sourceTimeStamp;
+ quint32_le unitSize; // Size of the Unit and any depending data.
// END DO NOT CHANGE THESE FIELDS EVER
+ char libraryVersionHash[QmlCompileHashSpace];
+
char md5Checksum[16]; // checksum of all bytes following this field.
void generateChecksum();
- LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi()
- LEUInt32 codeGeneratorIndex;
char dependencyMD5Checksum[16];
enum : unsigned int {
IsJavascript = 0x1,
- IsQml = 0x2,
- StaticData = 0x4, // Unit data persistent in memory?
- IsSingleton = 0x8,
- IsSharedLibrary = 0x10, // .pragma shared?
- ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions
- PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation
+ StaticData = 0x2, // Unit data persistent in memory?
+ IsSingleton = 0x4,
+ IsSharedLibrary = 0x8, // .pragma shared?
+ IsESModule = 0x10,
+ PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation
};
- LEUInt32 flags;
- LEUInt32 stringTableSize;
- LEUInt32 offsetToStringTable;
- LEUInt32 functionTableSize;
- LEUInt32 offsetToFunctionTable;
- LEUInt32 lookupTableSize;
- LEUInt32 offsetToLookupTable;
- LEUInt32 regexpTableSize;
- LEUInt32 offsetToRegexpTable;
- LEUInt32 constantTableSize;
- LEUInt32 offsetToConstantTable;
- LEUInt32 jsClassTableSize;
- LEUInt32 offsetToJSClassTable;
- LEInt32 indexOfRootFunction;
- LEUInt32 sourceFileIndex;
+ quint32_le flags;
+ quint32_le stringTableSize;
+ quint32_le offsetToStringTable;
+ quint32_le functionTableSize;
+ quint32_le offsetToFunctionTable;
+ quint32_le classTableSize;
+ quint32_le offsetToClassTable;
+ quint32_le templateObjectTableSize;
+ quint32_le offsetToTemplateObjectTable;
+ quint32_le blockTableSize;
+ quint32_le offsetToBlockTable;
+ quint32_le lookupTableSize;
+ quint32_le offsetToLookupTable;
+ quint32_le regexpTableSize;
+ quint32_le offsetToRegexpTable;
+ quint32_le constantTableSize;
+ quint32_le offsetToConstantTable;
+ quint32_le jsClassTableSize;
+ quint32_le offsetToJSClassTable;
+ quint32_le translationTableSize;
+ quint32_le offsetToTranslationTable;
+ quint32_le localExportEntryTableSize;
+ quint32_le offsetToLocalExportEntryTable;
+ quint32_le indirectExportEntryTableSize;
+ quint32_le offsetToIndirectExportEntryTable;
+ quint32_le starExportEntryTableSize;
+ quint32_le offsetToStarExportEntryTable;
+ quint32_le importEntryTableSize;
+ quint32_le offsetToImportEntryTable;
+ quint32_le moduleRequestTableSize;
+ quint32_le offsetToModuleRequestTable;
+ qint32_le indexOfRootFunction;
+ quint32_le sourceFileIndex;
+ quint32_le finalUrlIndex;
+
+ quint32_le offsetToQmlUnit;
+
+ bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const;
/* QML specific fields */
- LEUInt32 nImports;
- LEUInt32 offsetToImports;
- LEUInt32 nObjects;
- LEUInt32 offsetToObjects;
- LEUInt32 indexOfRootObject;
- const Import *importAt(int idx) const {
- return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
- }
-
- const Object *objectAt(int idx) const {
- const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
- const LEUInt32 offset = offsetTable[idx];
- return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
+ const QmlUnit *qmlUnit() const {
+ return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit);
}
bool isSingleton() const {
@@ -667,21 +923,22 @@ struct Unit
}
/* end QML specific fields*/
- QString stringAt(int idx) const {
- const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
- const LEUInt32 offset = offsetTable[idx];
+ QString stringAtInternal(int idx) const {
+ Q_ASSERT(idx < int(stringTableSize));
+ const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
+ const quint32_le offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
if (str->size == 0)
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (flags & StaticData) {
+ const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) };
+ return QString(holder);
+ }
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
- // Too risky to do this while we unmap disk backed compilation but keep pointers to string
- // data in the identifier tables.
- // if (flags & StaticData)
- // return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
- const LEUInt16 *characters = reinterpret_cast<const LEUInt16 *>(str + 1);
+ const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
QString qstr(str->size, Qt::Uninitialized);
QChar *ch = qstr.data();
for (int i = 0; i < str->size; ++i)
@@ -690,35 +947,65 @@ struct Unit
#endif
}
- const LEUInt32 *functionOffsetTable() const { return reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); }
+ const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); }
+ const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
- const LEUInt32 *offsetTable = functionOffsetTable();
- const LEUInt32 offset = offsetTable[idx];
+ const quint32_le *offsetTable = functionOffsetTable();
+ const quint32_le offset = offsetTable[idx];
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
+ const Class *classAt(int idx) const {
+ const quint32_le *offsetTable = classOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
+ const TemplateObject *templateObjectAt(int idx) const {
+ const quint32_le *offsetTable = templateObjectOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
+ const Block *blockAt(int idx) const {
+ const quint32_le *offsetTable = blockOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
}
- const LEUInt64 *constants() const {
- return reinterpret_cast<const LEUInt64*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
+ const quint64_le *constants() const {
+ return reinterpret_cast<const quint64_le*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
}
const JSClassMember *jsClassAt(int idx, int *nMembers) const {
- const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
- const LEUInt32 offset = offsetTable[idx];
+ const quint32_le *offsetTable = reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
+ const quint32_le offset = offsetTable[idx];
const char *ptr = reinterpret_cast<const char *>(this) + offset;
const JSClass *klass = reinterpret_cast<const JSClass *>(ptr);
*nMembers = klass->nMembers;
return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass));
}
+
+ const TranslationData *translations() const {
+ return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
+ }
+
+ const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
+ const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
+ const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
+ const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); }
+
+ const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
};
-#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
-#pragma pack(pop)
-#endif
+static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TypeReference
{
@@ -755,9 +1042,7 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
if (prop->type >= QV4::CompiledData::Property::Custom) {
- // ### FIXME: We could report the more accurate location here by using prop->location, but the old
- // compiler can't and the tests expect it to be the object location right now.
- TypeReference &r = this->add(prop->customTypeNameIndex, obj->location);
+ TypeReference &r = this->add(prop->customTypeNameIndex, prop->location);
r.errorWhenNotFound = true;
}
}
@@ -800,44 +1085,86 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData;
struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
{
- QV4::Heap::String **runtimeStrings = 0; // Array
+ // pointers either to data->constants() or little-endian memory copy.
+ QV4::Heap::String **runtimeStrings = nullptr; // Array
+ const Value* constants = nullptr;
+ QV4::Value *runtimeRegularExpressions = nullptr;
+ QV4::Heap::InternalClass **runtimeClasses = nullptr;
+ const Value** imports = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *));
-struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QQmlRefCount
+struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase
{
+ const Unit *data = nullptr;
+ const QmlUnit *qmlData = nullptr;
+public:
+ CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString());
#ifdef V4_BOOTSTRAP
- CompilationUnit()
- : data(0)
- {}
- virtual ~CompilationUnit() {}
+ ~CompilationUnit() {}
#else
- CompilationUnit();
- virtual ~CompilationUnit();
+ ~CompilationUnit();
#endif
- const Unit *data;
+ void addref()
+ {
+ Q_ASSERT(refCount.load() > 0);
+ refCount.ref();
+ }
+
+ void release()
+ {
+ Q_ASSERT(refCount.load() > 0);
+ if (!refCount.deref())
+ destroy();
+ }
+ int count() const
+ {
+ return refCount.load();
+ }
- // Called only when building QML, when we build the header for JS first and append QML data
- virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
+ const Unit *unitData() const { return data; }
+ void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
+ const QString &fileName = QString(), const QString &finalUrlString = QString());
#ifndef V4_BOOTSTRAP
- ExecutionEngine *engine;
-
- QString fileName() const { return data->stringAt(data->sourceFileIndex); }
+ QIntrusiveListNode nextCompilationUnit;
+ ExecutionEngine *engine = nullptr;
+ QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case.
+
+ // url() and fileName() shall be used to load the actual QML/JS code or to show errors or
+ // warnings about that code. They include any potential URL interceptions and thus represent the
+ // "physical" location of the code.
+ //
+ // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
+ // They are _not_ intercepted and thus represent the "logical" name for the code.
+
+ QString fileName() const { return m_fileName; }
+ QString finalUrlString() const { return m_finalUrlString; }
QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
+ QUrl finalUrl() const
+ {
+ if (m_finalUrl.isNull)
+ m_finalUrl = QUrl(finalUrlString());
+ return m_finalUrl;
+ }
- QV4::Lookup *runtimeLookups;
- QV4::Value *runtimeRegularExpressions;
- QV4::InternalClass **runtimeClasses;
+ QV4::Lookup *runtimeLookups = nullptr;
QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
+ mutable QVector<QV4::Heap::Object *> templateObjects;
mutable QQmlNullableValue<QUrl> m_url;
+ mutable QQmlNullableValue<QUrl> m_finalUrl;
// QML specific fields
QQmlPropertyCacheVector propertyCaches;
- QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); }
+ QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
@@ -848,35 +1175,42 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public
// mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
// this is initialized on-demand by QQmlContextData
- QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache;
- IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex);
+ QHash<int, IdentifierHash> namedObjectsPerComponentCache;
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
- // pointers either to data->constants() or little-endian memory copy.
- const Value* constants;
-
- void finalize(QQmlEnginePrivate *engine);
+ void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
- int totalBindingsCount; // Number of bindings used in this type
- int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses
- int totalObjectCount; // Number of objects explicitly instantiated
+ int totalBindingsCount = 0; // Number of bindings used in this type
+ int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
+ int totalObjectCount = 0; // Number of objects explicitly instantiated
- QVector<QQmlScriptData *> dependentScripts;
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
ResolvedTypeReferenceMap resolvedTypes;
+ ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); }
bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
- int metaTypeId;
- int listMetaTypeId;
- bool isRegisteredWithEngine;
+ int metaTypeId = -1;
+ int listMetaTypeId = -1;
+ bool isRegisteredWithEngine = false;
QScopedPointer<CompilationUnitMapper> backingFile;
+ QStringList dynamicStrings;
// --- interface for QQmlPropertyCacheCreator
typedef Object CompiledObject;
- int objectCount() const { return data->nObjects; }
- int rootObjectIndex() const { return data->indexOfRootObject; }
- const Object *objectAt(int index) const { return data->objectAt(index); }
- QString stringAt(int index) const { return data->stringAt(index); }
+ int objectCount() const { return qmlData->nObjects; }
+ const Object *objectAt(int index) const { return qmlData->objectAt(index); }
+ int importCount() const { return qmlData->nImports; }
+ const Import *importAt(int index) const { return qmlData->importAt(index); }
+ QString stringAt(int index) const
+ {
+ if (uint(index) >= data->stringTableSize)
+ return dynamicStrings.at(index - data->stringTableSize);
+ return data->stringAtInternal(index);
+ }
+
+ Heap::Object *templateObjectAt(int index) const;
struct FunctionIterator
{
@@ -894,43 +1228,75 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public
FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); }
// ---
+ bool isESModule() const { return data->flags & Unit::IsESModule; }
+ bool isSharedLibrary() const { return data->flags & Unit::IsSharedLibrary; }
+ QStringList moduleRequests() const;
+ Heap::Module *instantiate(ExecutionEngine *engine);
+ const Value *resolveExport(QV4::String *exportName);
+ QStringList exportedNames() const;
+ void evaluate();
+ void evaluateModuleRequests();
+
QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
void unlink();
void markObjects(MarkStack *markStack);
- void destroy() Q_DECL_OVERRIDE;
+ bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
- bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString);
+ static QString localCacheFilePath(const QUrl &url);
protected:
- virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
- virtual bool memoryMapCode(QString *errorString);
+ quint32 totalStringCount() const
+ { return data->stringTableSize; }
+
+#else // V4_BOOTSTRAP
+ QString stringAt(int index) const { return data->stringAtInternal(index); }
#endif // V4_BOOTSTRAP
+private:
+ void destroy();
+
+ struct ResolveSetEntry
+ {
+ ResolveSetEntry() {}
+ ResolveSetEntry(CompilationUnit *module, QV4::String *exportName)
+ : module(module), exportName(exportName) {}
+ CompilationUnit *module = nullptr;
+ QV4::String *exportName = nullptr;
+ };
+
+ const Value *resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet);
+ const ExportEntry *lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const;
+ void getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit *> *exportNameSet, bool includeDefaultExport = true) const;
+
+ QString m_fileName; // initialized from data->sourceFileIndex
+ QString m_finalUrlString; // initialized from data->finalUrlIndex
+
+ QAtomicInt refCount = 1;
+
+ Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
+
+ Heap::Module *m_module = nullptr;
+
public:
#if defined(V4_BOOTSTRAP)
bool saveToDisk(const QString &outputFileName, QString *errorString);
#else
bool saveToDisk(const QUrl &unitUrl, QString *errorString);
#endif
-
-protected:
- virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit);
- virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString);
};
#ifndef V4_BOOTSTRAP
struct ResolvedTypeReference
{
ResolvedTypeReference()
- : type(0)
- , majorVersion(0)
+ : majorVersion(0)
, minorVersion(0)
, isFullyDynamicType(false)
{}
- QQmlType *type;
+ QQmlType type;
QQmlRefPointer<QQmlPropertyCache> typePropertyCache;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
@@ -940,17 +1306,24 @@ struct ResolvedTypeReference
// therefore cannot have a property cache installed when instantiated.
bool isFullyDynamicType;
- QQmlPropertyCache *propertyCache() const;
- QQmlPropertyCache *createPropertyCache(QQmlEngine *);
+ QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
+ QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
void doDynamicTypeCheck();
};
-#endif
+IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ return createNamedObjectsPerComponent(componentObjectIndex);
+ return *it;
}
+#endif // V4_BOOTSTRAP
-}
+} // CompiledData namespace
+} // QV4 namespace
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index f7e63437e1..0833f552e6 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -39,10 +39,12 @@
#include <qv4compiler_p.h>
#include <qv4compileddata_p.h>
-#include <qv4isel_p.h>
+#include <qv4codegen_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsast_p.h>
#include <wtf/MathExtras.h>
#include <QCryptographicHash>
@@ -53,6 +55,7 @@ QV4::Compiler::StringTableGenerator::StringTableGenerator()
int QV4::Compiler::StringTableGenerator::registerString(const QString &str)
{
+ Q_ASSERT(!frozen);
QHash<QString, int>::ConstIterator it = stringToId.constFind(str);
if (it != stringToId.cend())
return *it;
@@ -73,96 +76,114 @@ void QV4::Compiler::StringTableGenerator::clear()
strings.clear();
stringToId.clear();
stringDataSize = 0;
+ frozen = false;
+}
+
+void QV4::Compiler::StringTableGenerator::initializeFromBackingUnit(const QV4::CompiledData::Unit *unit)
+{
+ clear();
+ for (uint i = 0; i < unit->stringTableSize; ++i)
+ registerString(unit->stringAtInternal(i));
+ backingUnitTableSize = unit->stringTableSize;
+ stringDataSize = 0;
}
void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
{
char *dataStart = reinterpret_cast<char *>(unit);
- CompiledData::LEUInt32 *stringTable = reinterpret_cast<CompiledData::LEUInt32 *>(dataStart + unit->offsetToStringTable);
- char *stringData = dataStart + unit->offsetToStringTable + unit->stringTableSize * sizeof(uint);
- for (int i = 0; i < strings.size(); ++i) {
- stringTable[i] = stringData - dataStart;
+ quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable);
+ char *stringData = reinterpret_cast<char *>(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
+ for (int i = backingUnitTableSize ; i < strings.size(); ++i) {
+ const int index = i - backingUnitTableSize;
+ stringTable[index] = stringData - dataStart;
const QString &qstr = strings.at(i);
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);
+
+ ushort *uc = reinterpret_cast<ushort *>(reinterpret_cast<char *>(s) + sizeof(*s));
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort));
+ memcpy(uc, qstr.constData(), s->size * sizeof(ushort));
#else
- ushort *uc = reinterpret_cast<ushort *>(s + 1);
- for (int i = 0; i < qstr.length(); ++i)
+ for (int i = 0; i < s->size; ++i)
uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode());
#endif
+ uc[s->size] = 0;
stringData += QV4::CompiledData::String::calculateSize(qstr);
}
}
-QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module)
- : irModule(module)
+QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::Compiler::Module *module)
+ : module(module)
{
// Make sure the empty string always gets index 0
registerString(QString());
}
-uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup()
+int QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_IndexedGetter;
- l.nameIndex = 0;
- lookups << l;
- return lookups.size() - 1;
+ return registerGetterLookup(registerString(name));
}
-uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup()
+int QV4::Compiler::JSUnitGenerator::registerGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter;
- l.nameIndex = 0;
+ l.type_and_flags = CompiledData::Lookup::Type_Getter;
+ l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
-uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
{
- CompiledData::Lookup l;
- l.type_and_flags = CompiledData::Lookup::Type_Getter;
- l.nameIndex = registerString(name);
- lookups << l;
- return lookups.size() - 1;
+ return registerSetterLookup(registerString(name));
}
-
-uint 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 = registerString(name);
+ l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
-uint QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
+int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
+{
+ return registerGlobalGetterLookup(registerString(name));
+}
+
+int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter;
- l.nameIndex = registerString(name);
+ l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerRegExp(QV4::IR::RegExp *regexp)
+int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
{
CompiledData::RegExp re;
- re.stringIndex = registerString(*regexp->value);
+ re.stringIndex = registerString(regexp->pattern.toString());
re.flags = 0;
- if (regexp->flags & QV4::IR::RegExp::RegExp_Global)
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Global)
re.flags |= CompiledData::RegExp::RegExp_Global;
- if (regexp->flags & QV4::IR::RegExp::RegExp_IgnoreCase)
+ if (regexp->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
re.flags |= CompiledData::RegExp::RegExp_IgnoreCase;
- if (regexp->flags & QV4::IR::RegExp::RegExp_Multiline)
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline)
re.flags |= CompiledData::RegExp::RegExp_Multiline;
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode)
+ re.flags |= CompiledData::RegExp::RegExp_Unicode;
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky)
+ re.flags |= CompiledData::RegExp::RegExp_Sticky;
regexps.append(re);
return regexps.size() - 1;
@@ -177,70 +198,119 @@ int QV4::Compiler::JSUnitGenerator::registerConstant(QV4::ReturnedValue v)
return constants.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *args)
+QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx)
+{
+ return constants.at(idx);
+}
+
+int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
{
// ### re-use existing class definitions.
- const int size = CompiledData::JSClass::calculateSize(count);
+ const int size = CompiledData::JSClass::calculateSize(members.size());
jsClassOffsets.append(jsClassData.size());
const int oldSize = jsClassData.size();
jsClassData.resize(jsClassData.size() + size);
memset(jsClassData.data() + oldSize, 0, size);
CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
- jsClass->nMembers = count;
+ jsClass->nMembers = members.size();
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- IR::ExprList *it = args;
- for (int i = 0; i < count; ++i, it = it->next, ++member) {
- QV4::IR::Name *name = it->expr->asName();
- it = it->next;
-
- const bool isData = it->expr->asConst()->value;
- it = it->next;
-
- member->nameOffset = registerString(*name->id);
- member->isAccessor = !isData;
-
- if (!isData)
- it = it->next;
+ for (const auto &name : members) {
+ member->nameOffset = registerString(name);
+ member->isAccessor = false;
+ ++member;
}
return jsClassOffsets.size() - 1;
}
+int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData::TranslationData &translation)
+{
+ translations.append(translation);
+ return translations.size() - 1;
+}
+
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
{
- registerString(irModule->fileName);
- for (QV4::IR::Function *f : qAsConst(irModule->functions)) {
- registerString(*f->name);
- for (int i = 0; i < f->formals.size(); ++i)
- registerString(*f->formals.at(i));
+ registerString(module->fileName);
+ registerString(module->finalUrl);
+ for (Context *f : qAsConst(module->functions)) {
+ registerString(f->name);
+ for (int i = 0; i < f->arguments.size(); ++i)
+ registerString(f->arguments.at(i));
for (int i = 0; i < f->locals.size(); ++i)
- registerString(*f->locals.at(i));
+ registerString(f->locals.at(i));
+ }
+ for (Context *c : qAsConst(module->blocks)) {
+ for (int i = 0; i < c->locals.size(); ++i)
+ registerString(c->locals.at(i));
+ }
+ {
+ const auto registerExportEntry = [this](const Compiler::ExportEntry &entry) {
+ registerString(entry.exportName);
+ registerString(entry.moduleRequest);
+ registerString(entry.importName);
+ registerString(entry.localName);
+ };
+ std::for_each(module->localExportEntries.constBegin(), module->localExportEntries.constEnd(), registerExportEntry);
+ std::for_each(module->indirectExportEntries.constBegin(), module->indirectExportEntries.constEnd(), registerExportEntry);
+ std::for_each(module->starExportEntries.constBegin(), module->starExportEntries.constEnd(), registerExportEntry);
+ }
+ {
+ for (const auto &entry: module->importEntries) {
+ registerString(entry.moduleRequest);
+ registerString(entry.importName);
+ registerString(entry.localName);
+ }
+
+ for (const QString &request: module->moduleRequests)
+ registerString(request);
}
- Q_ALLOCA_VAR(CompiledData::LEUInt32, functionOffsets, irModule->functions.size() * sizeof(CompiledData::LEUInt32));
+ Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->templateObjects.size() + module->blocks.size()) * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
CompiledData::Unit *unit;
{
- QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset);
dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
memset(dataPtr, 0, tempHeader.unitSize);
memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32));
+ memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToTemplateObjectTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->templateObjectTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize + unit->templateObjectTableSize, unit->blockTableSize * sizeof(quint32_le));
- for (int i = 0; i < irModule->functions.size(); ++i) {
- QV4::IR::Function *function = irModule->functions.at(i);
- if (function == irModule->rootFunction)
+ for (int i = 0; i < module->functions.size(); ++i) {
+ Context *function = module->functions.at(i);
+ if (function == module->rootContext)
unit->indexOfRootFunction = i;
- writeFunction(dataPtr + functionOffsets[i], function);
+ writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function);
+ }
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+
+ writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
+ }
+
+ for (int i = 0; i < module->templateObjects.size(); ++i) {
+ const TemplateObject &t = module->templateObjects.at(i);
+
+ writeTemplateObject(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size() + module->classes.size()], t);
+ }
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *block = module->blocks.at(i);
+
+ writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->templateObjects.size() + module->functions.size()], block);
}
CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
@@ -254,7 +324,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable);
memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue));
#else
- CompiledData::LEUInt64 *constantTable = reinterpret_cast<CompiledData::LEUInt64 *>(dataPtr + unit->offsetToConstantTable);
+ quint64_le *constantTable = reinterpret_cast<quint64_le *>(dataPtr + unit->offsetToConstantTable);
for (int i = 0; i < constants.count(); ++i)
constantTable[i] = constants.at(i);
#endif
@@ -263,11 +333,50 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size());
// write js classes and js class lookup table
- CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast<CompiledData::LEUInt32 *>(dataPtr + unit->offsetToJSClassTable);
+ quint32_le *jsClassOffsetTable = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToJSClassTable);
for (int i = 0; i < jsClassOffsets.count(); ++i)
jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
}
+
+ memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.count() * sizeof(CompiledData::TranslationData));
+
+ {
+ const auto populateExportEntryTable = [this, dataPtr](const QVector<Compiler::ExportEntry> &table, quint32_le offset) {
+ CompiledData::ExportEntry *entryToWrite = reinterpret_cast<CompiledData::ExportEntry *>(dataPtr + offset);
+ for (const Compiler::ExportEntry &entry: table) {
+ entryToWrite->exportName = getStringId(entry.exportName);
+ entryToWrite->moduleRequest = getStringId(entry.moduleRequest);
+ entryToWrite->importName = getStringId(entry.importName);
+ entryToWrite->localName = getStringId(entry.localName);
+ entryToWrite->location = entry.location;
+ entryToWrite++;
+ }
+ };
+ populateExportEntryTable(module->localExportEntries, unit->offsetToLocalExportEntryTable);
+ populateExportEntryTable(module->indirectExportEntries, unit->offsetToIndirectExportEntryTable);
+ populateExportEntryTable(module->starExportEntries, unit->offsetToStarExportEntryTable);
+ }
+
+ {
+ CompiledData::ImportEntry *entryToWrite = reinterpret_cast<CompiledData::ImportEntry *>(dataPtr + unit->offsetToImportEntryTable);
+ for (const Compiler::ImportEntry &entry: module->importEntries) {
+ entryToWrite->moduleRequest = getStringId(entry.moduleRequest);
+ entryToWrite->importName = getStringId(entry.importName);
+ entryToWrite->localName = getStringId(entry.localName);
+ entryToWrite->location = entry.location;
+ entryToWrite++;
+ }
+ }
+
+ {
+ quint32_le *moduleRequestEntryToWrite = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToModuleRequestTable);
+ for (const QString &moduleRequest: module->moduleRequests) {
+ *moduleRequestEntryToWrite = getStringId(moduleRequest);
+ moduleRequestEntryToWrite++;
+ }
+ }
+
// write strings and string table
if (option == GenerateWithStringTable)
stringTable.serialize(unit);
@@ -277,36 +386,42 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
return unit;
}
-void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const
+void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Context *irFunction) const
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
- quint32 currentOffset = sizeof(QV4::CompiledData::Function);
- currentOffset = (currentOffset + 7) & ~quint32(0x7);
+ quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*function)));
- function->nameIndex = getStringId(*irFunction->name);
+ function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
- if (irFunction->hasDirectEval)
- function->flags |= CompiledData::Function::HasDirectEval;
- if (irFunction->usesArgumentsObject)
- function->flags |= CompiledData::Function::UsesArgumentsObject;
if (irFunction->isStrict)
function->flags |= CompiledData::Function::IsStrict;
- if (irFunction->isNamedExpression)
- function->flags |= CompiledData::Function::IsNamedExpression;
- if (irFunction->hasTry || irFunction->hasWith)
- function->flags |= CompiledData::Function::HasCatchOrWith;
- if (irFunction->canUseSimpleCall())
- function->flags |= CompiledData::Function::CanUseSimpleCall;
- function->nFormals = irFunction->formals.size();
+ if (irFunction->isArrowFunction)
+ 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();
+ function->length = irFunction->formals ? irFunction->formals->length() : 0;
+ function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
+ function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
+ function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
+ function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
+
function->nLocals = irFunction->locals.size();
function->localsOffset = currentOffset;
currentOffset += function->nLocals * sizeof(quint32);
- function->nInnerFunctions = irFunction->nestedFunctions.size();
+ function->nLineNumbers = irFunction->lineNumberMapping.size();
+ Q_ASSERT(function->lineNumberOffset() == currentOffset);
+ currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine);
+
+ function->nTraceInfos = irFunction->nTraceInfos;
+ function->nRegisters = irFunction->registerCountInFunction;
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -314,78 +429,211 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i
if (!irFunction->idObjectDependencies.isEmpty()) {
function->nDependingIdObjects = irFunction->idObjectDependencies.count();
- function->dependingIdObjectsOffset = currentOffset;
+ Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset);
currentOffset += function->nDependingIdObjects * sizeof(quint32);
}
if (!irFunction->contextObjectPropertyDependencies.isEmpty()) {
function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count();
- function->dependingContextPropertiesOffset = currentOffset;
+ Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset);
currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2;
}
if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) {
function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count();
- function->dependingScopePropertiesOffset = currentOffset;
+ Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset);
currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2;
}
+ if (!irFunction->labelInfo.empty()) {
+ function->nLabelInfos = quint32(irFunction->labelInfo.size());
+ Q_ASSERT(function->labelInfosOffset() == currentOffset);
+ currentOffset += function->nLabelInfos * sizeof(quint32);
+ }
+
function->location.line = irFunction->line;
function->location.column = irFunction->column;
- function->codeOffset = 0;
- function->codeSize = 0;
+ function->codeOffset = currentOffset;
+ function->codeSize = irFunction->code.size();
// write formals
- quint32 *formals = (quint32 *)(f + function->formalsOffset);
- for (int i = 0; i < irFunction->formals.size(); ++i)
- formals[i] = getStringId(*irFunction->formals.at(i));
+ quint32_le *formals = (quint32_le *)(f + function->formalsOffset);
+ for (int i = 0; i < irFunction->arguments.size(); ++i)
+ formals[i] = getStringId(irFunction->arguments.at(i));
// write locals
- quint32 *locals = (quint32 *)(f + function->localsOffset);
+ quint32_le *locals = (quint32_le *)(f + function->localsOffset);
for (int i = 0; i < irFunction->locals.size(); ++i)
- locals[i] = getStringId(*irFunction->locals.at(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 QML dependencies
- quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset);
+ quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset());
for (int id : irFunction->idObjectDependencies) {
Q_ASSERT(id >= 0);
*writtenDeps++ = static_cast<quint32>(id);
}
- writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset);
+ writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset());
for (auto property : irFunction->contextObjectPropertyDependencies) {
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
- writtenDeps = (quint32 *)(f + function->dependingScopePropertiesOffset);
+ writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset());
for (auto property : irFunction->scopeObjectPropertyDependencies) {
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
+
+ quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
+ for (unsigned u : irFunction->labelInfo) {
+ *labels++ = u;
+ }
+
+ // write byte code
+ memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
+}
+
+static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout");
+
+void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c)
+{
+ QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Class);
+
+ QVector<Class::Method> allMethods = c.staticMethods;
+ allMethods += c.methods;
+
+ cls->constructorFunction = c.constructorIndex;
+ cls->nameIndex = c.nameIndex;
+ cls->nMethods = c.methods.size();
+ cls->nStaticMethods = c.staticMethods.size();
+ cls->methodTableOffset = currentOffset;
+ CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset);
+
+ // write methods
+ for (int i = 0; i < allMethods.size(); ++i) {
+ method->name = allMethods.at(i).nameIndex;
+ method->type = allMethods.at(i).type;
+ method->function = allMethods.at(i).functionIndex;
+ ++method;
+ }
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ 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;
+ switch (cls->methodTable()[i].type) {
+ case CompiledData::Method::Getter:
+ type = "get "; break;
+ case CompiledData::Method::Setter:
+ type = "set "; break;
+ default:
+ type = "";
+
+ }
+ qDebug() << " " << i << staticString << type << stringForIndex(cls->methodTable()[i].name) << cls->methodTable()[i].function;
+ }
+ qDebug();
+ }
}
-QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset)
+void QV4::Compiler::JSUnitGenerator::writeTemplateObject(char *b, const QV4::Compiler::TemplateObject &t)
+{
+ QV4::CompiledData::TemplateObject *tmpl = reinterpret_cast<QV4::CompiledData::TemplateObject *>(b);
+ tmpl->size = t.strings.size();
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::TemplateObject);
+
+ quint32_le *strings = reinterpret_cast<quint32_le *>(b + currentOffset);
+
+ // write methods
+ for (int i = 0; i < t.strings.size(); ++i)
+ strings[i] = t.strings.at(i);
+ strings += t.strings.size();
+
+ for (int i = 0; i < t.rawStrings.size(); ++i)
+ strings[i] = t.rawStrings.at(i);
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== TemplateObject size" << tmpl->size;
+ for (uint i = 0; i < tmpl->size; ++i) {
+ qDebug() << " " << i << stringForIndex(tmpl->stringIndexAt(i));
+ qDebug() << " raw: " << stringForIndex(tmpl->rawStringIndexAt(i));
+ }
+ qDebug();
+ }
+}
+
+void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
+{
+ QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
+
+ quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*block)));
+
+ block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
+ block->nLocals = irBlock->locals.size();
+ block->localsOffset = currentOffset;
+ currentOffset += block->nLocals * sizeof(quint32);
+
+ // write locals
+ quint32_le *locals = (quint32_le *)(b + block->localsOffset);
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ locals[i] = getStringId(irBlock->locals.at(i));
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Variables for block" << irBlock->blockIndex;
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ qDebug() << " " << i << ":" << locals[i];
+ qDebug();
+ }
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
{
CompiledData::Unit unit;
memset(&unit, 0, sizeof(unit));
memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic));
unit.flags = QV4::CompiledData::Unit::IsJavascript;
- unit.flags |= irModule->unitFlags;
+ unit.flags |= module->unitFlags;
unit.version = QV4_DATA_STRUCTURE_VERSION;
unit.qtVersion = QT_VERSION;
+ qstrcpy(unit.libraryVersionHash, CompiledData::qml_compile_hash);
memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
- unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI);
- unit.codeGeneratorIndex = registerString(codeGeneratorName);
memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
quint32 nextOffset = sizeof(CompiledData::Unit);
- unit.functionTableSize = irModule->functions.size();
+ unit.functionTableSize = module->functions.size();
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
+ unit.classTableSize = module->classes.size();
+ unit.offsetToClassTable = nextOffset;
+ nextOffset += unit.classTableSize * sizeof(uint);
+
+ unit.templateObjectTableSize = module->templateObjects.size();
+ unit.offsetToTemplateObjectTable = nextOffset;
+ nextOffset += unit.templateObjectTableSize * sizeof(uint);
+
+ unit.blockTableSize = module->blocks.size();
+ unit.offsetToBlockTable = nextOffset;
+ nextOffset += unit.blockTableSize * sizeof(uint);
+
unit.lookupTableSize = lookups.count();
unit.offsetToLookupTable = nextOffset;
nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
@@ -408,17 +656,76 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
*jsClassDataOffset = nextOffset;
nextOffset += jsClassData.size();
- for (int i = 0; i < irModule->functions.size(); ++i) {
- QV4::IR::Function *f = irModule->functions.at(i);
- functionOffsets[i] = nextOffset;
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ unit.translationTableSize = translations.count();
+ unit.offsetToTranslationTable = nextOffset;
+ nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
+
+ nextOffset = static_cast<quint32>(WTF::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>(WTF::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);
+
+ unit.importEntryTableSize = module->importEntries.count();
+ unit.offsetToImportEntryTable = nextOffset;
+ nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry);
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ unit.moduleRequestTableSize = module->moduleRequests.count();
+ unit.offsetToModuleRequestTable = nextOffset;
+ nextOffset += unit.moduleRequestTableSize * sizeof(uint);
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ quint32 functionSize = 0;
+ for (int i = 0; i < module->functions.size(); ++i) {
+ Context *f = module->functions.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
- nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount);
+ quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
+ qmlIdDepsCount, qmlPropertyDepsCount, int(f->labelInfo.size()), f->code.size());
+ functionSize += size - f->code.size();
+ nextOffset += size;
+ }
+
+ blockAndFunctionOffsets += module->functions.size();
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size());
+ }
+ blockAndFunctionOffsets += module->classes.size();
+
+ for (int i = 0; i < module->templateObjects.size(); ++i) {
+ const TemplateObject &t = module->templateObjects.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::TemplateObject::calculateSize(t.strings.size());
+ }
+ blockAndFunctionOffsets += module->templateObjects.size();
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *c = module->blocks.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size());
}
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
unit.offsetToStringTable = nextOffset;
nextOffset += stringTable.sizeOfTableAndData();
} else {
@@ -426,15 +733,19 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToStringTable = 0;
}
unit.indexOfRootFunction = -1;
- unit.sourceFileIndex = getStringId(irModule->fileName);
- unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0;
- unit.nImports = 0;
- unit.offsetToImports = 0;
- unit.nObjects = 0;
- unit.offsetToObjects = 0;
- unit.indexOfRootObject = 0;
+ unit.sourceFileIndex = getStringId(module->fileName);
+ unit.finalUrlIndex = getStringId(module->finalUrl);
+ unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0;
+ unit.offsetToQmlUnit = 0;
unit.unitSize = nextOffset;
+ static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
+ 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";
+ }
+
return unit;
}
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 49b8664513..2f5889ab53 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -51,8 +51,11 @@
//
#include <QtCore/qstring.h>
-#include "qv4jsir_p.h"
-#include <private/qjson_p.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstringlist.h>
+#include <private/qv4global_p.h>
+#include <private/qqmljsastfwd_p.h>
+#include <private/qv4compileddata_p.h>
QT_BEGIN_NAMESPACE
@@ -69,44 +72,63 @@ struct JSClassMember;
namespace Compiler {
+struct Class;
+struct TemplateObject;
+
struct Q_QML_PRIVATE_EXPORT StringTableGenerator {
StringTableGenerator();
int registerString(const QString &str);
int getStringId(const QString &string) const;
QString stringForIndex(int index) const { return strings.at(index); }
- uint stringCount() const { return strings.size(); }
+ uint stringCount() const { return strings.size() - backingUnitTableSize; }
+
+ uint sizeOfTableAndData() const { return stringDataSize + ((stringCount() * sizeof(uint) + 7) & ~7); }
- uint sizeOfTableAndData() const { return stringDataSize + strings.count() * sizeof(uint); }
+ void freeze() { frozen = true; }
void clear();
+ void initializeFromBackingUnit(const CompiledData::Unit *unit);
+
void serialize(CompiledData::Unit *unit);
+ QStringList allStrings() const { return strings.mid(backingUnitTableSize); }
private:
QHash<QString, int> stringToId;
QStringList strings;
uint stringDataSize;
+ uint backingUnitTableSize = 0;
+ bool frozen = false;
};
struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
- JSUnitGenerator(IR::Module *module);
+ struct MemberInfo {
+ QString name;
+ bool isAccessor;
+ };
+
+ JSUnitGenerator(Module *module);
int registerString(const QString &str) { return stringTable.registerString(str); }
int getStringId(const QString &string) const { return stringTable.getStringId(string); }
QString stringForIndex(int index) const { return stringTable.stringForIndex(index); }
- uint registerGetterLookup(const QString &name);
- uint registerSetterLookup(const QString &name);
- uint registerGlobalGetterLookup(const QString &name);
- uint registerIndexedGetterLookup();
- uint registerIndexedSetterLookup();
+ int registerGetterLookup(const QString &name);
+ int registerGetterLookup(int nameIndex);
+ int registerSetterLookup(const QString &name);
+ int registerSetterLookup(int nameIndex);
+ int registerGlobalGetterLookup(const QString &name);
+ int registerGlobalGetterLookup(int nameIndex);
- int registerRegExp(IR::RegExp *regexp);
+ int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);
int registerConstant(ReturnedValue v);
+ ReturnedValue constant(int idx);
+
+ int registerJSClass(const QStringList &members);
- int registerJSClass(int count, IR::ExprList *args);
+ int registerTranslation(const CompiledData::TranslationData &translation);
enum GeneratorOption {
GenerateWithStringTable,
@@ -114,21 +136,24 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
};
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
- // Returns bytes written
- void writeFunction(char *f, IR::Function *irFunction) const;
+ void writeFunction(char *f, Context *irFunction) const;
+ void writeClass(char *f, const Class &c);
+ void writeTemplateObject(char *f, const TemplateObject &o);
+ void writeBlock(char *f, Context *irBlock) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
private:
- CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset);
+ CompiledData::Unit generateHeader(GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset);
- IR::Module *irModule;
+ Module *module;
QList<CompiledData::Lookup> lookups;
QVector<CompiledData::RegExp> regexps;
QVector<ReturnedValue> constants;
QByteArray jsClassData;
QVector<int> jsClassOffsets;
+ QVector<CompiledData::TranslationData> translations;
};
}
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
new file mode 100644
index 0000000000..931759fa72
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -0,0 +1,432 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qv4compilercontext_p.h"
+#include "qv4compilercontrolflow_p.h"
+#include "qv4bytecodegenerator_p.h"
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
+
+QT_BEGIN_NAMESPACE
+
+Context *Module::newContext(Node *node, Context *parent, ContextType contextType)
+{
+ Q_ASSERT(!contextMap.contains(node));
+
+ Context *c = new Context(parent, contextType);
+ if (node) {
+ SourceLocation loc = node->firstSourceLocation();
+ c->line = loc.startLine;
+ c->column = loc.startColumn;
+ }
+
+ contextMap.insert(node, c);
+
+ if (!parent)
+ rootContext = c;
+ else {
+ parent->nestedContexts.append(c);
+ c->isStrict = parent->isStrict;
+ }
+
+ return c;
+}
+
+bool Context::Member::requiresTDZCheck(const SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const
+{
+ if (!isLexicallyScoped())
+ return false;
+
+ if (accessAcrossContextBoundaries)
+ return true;
+
+ if (!accessLocation.isValid() || !endOfInitializerLocation.isValid())
+ return true;
+
+ return accessLocation.begin() < endOfInitializerLocation.end();
+}
+
+bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function,
+ const QQmlJS::AST::SourceLocation &endOfInitializer)
+{
+ // ### can this happen?
+ if (name.isEmpty())
+ return true;
+
+ if (type != FunctionDefinition) {
+ if (formals && formals->containsName(name))
+ return (scope == VariableScope::Var);
+ }
+ if (!isCatchBlock || name != caughtVariable) {
+ MemberMap::iterator it = members.find(name);
+ if (it != members.end()) {
+ if (scope != VariableScope::Var || (*it).scope != VariableScope::Var)
+ return false;
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
+ return true;
+ }
+ }
+
+ // 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);
+
+ Member m;
+ m.type = type;
+ m.function = function;
+ m.scope = scope;
+ m.endOfInitializerLocation = endOfInitializer;
+ members.insert(name, m);
+ return true;
+}
+
+Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation)
+{
+ int scope = 0;
+ Context *c = this;
+
+ ResolvedName result;
+
+ while (c) {
+ if (c->isWithBlock)
+ return result;
+
+ Context::Member m = c->findMember(name);
+ if (!c->parent && m.index < 0)
+ break;
+
+ if (m.type != Context::UndefinedMember) {
+ result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack;
+ result.scope = scope;
+ result.index = m.index;
+ result.isConst = (m.scope == VariableScope::Const);
+ result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this);
+ if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
+ result.isArgOrEval = true;
+ return result;
+ }
+ const int argIdx = c->findArgument(name);
+ if (argIdx != -1) {
+ if (c->argumentsCanEscape) {
+ result.index = argIdx + c->locals.size();
+ result.scope = scope;
+ result.type = ResolvedName::Local;
+ result.isConst = false;
+ return result;
+ } else {
+ result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1;
+ result.scope = 0;
+ result.type = ResolvedName::Stack;
+ result.isConst = false;
+ return result;
+ }
+ }
+ if (c->hasDirectEval) {
+ Q_ASSERT(!c->isStrict && c->contextType != ContextType::Block);
+ return result;
+ }
+
+ if (c->requiresExecutionContext)
+ ++scope;
+ c = c->parent;
+ }
+
+ if (c && c->contextType == ContextType::ESModule) {
+ for (int i = 0; i < c->importEntries.count(); ++i) {
+ if (c->importEntries.at(i).localName == name) {
+ result.index = i;
+ result.type = ResolvedName::Import;
+ result.isConst = true;
+ // We don't know at compile time whether the imported value is let/const or not.
+ result.requiresTDZCheck = true;
+ return result;
+ }
+ }
+ }
+
+ // ### can we relax the restrictions here?
+ if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding)
+ return result;
+
+ result.type = ResolvedName::Global;
+ return result;
+}
+
+void Context::emitBlockHeader(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ setupFunctionIndices(bytecodeGenerator);
+
+ if (requiresExecutionContext) {
+ if (blockIndex < 0) {
+ codegen->module()->blocks.append(this);
+ blockIndex = codegen->module()->blocks.count() - 1;
+ }
+
+ if (contextType == ContextType::Global) {
+ Instruction::PushScriptContext scriptContext;
+ scriptContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(scriptContext);
+ } else if (contextType == ContextType::Block || (contextType == ContextType::Eval && !isStrict)) {
+ if (isCatchBlock) {
+ Instruction::PushCatchContext catchContext;
+ catchContext.index = blockIndex;
+ catchContext.name = codegen->registerString(caughtVariable);
+ bytecodeGenerator->addInstruction(catchContext);
+ } else {
+ Instruction::PushBlockContext blockContext;
+ blockContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(blockContext);
+ }
+ } else if (contextType != ContextType::ESModule && contextType != ContextType::ScriptImportedByQML) {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
+ }
+
+ if (contextType == ContextType::Block && sizeOfRegisterTemporalDeadZone > 0) {
+ Instruction::InitializeBlockDeadTemporalZone tdzInit;
+ tdzInit.firstReg = registerOffset + nRegisters - sizeOfRegisterTemporalDeadZone;
+ tdzInit.count = sizeOfRegisterTemporalDeadZone;
+ bytecodeGenerator->addInstruction(tdzInit);
+ }
+
+ if (usesThis) {
+ Q_ASSERT(!isStrict);
+ // make sure we convert this to an object
+ Instruction::ConvertThisToObject convert;
+ bytecodeGenerator->addInstruction(convert);
+ }
+ if (innerFunctionAccessesThis) {
+ Instruction::LoadReg load;
+ load.reg = CallData::This;
+ bytecodeGenerator->addInstruction(load);
+ Codegen::Reference r = codegen->referenceForName(QStringLiteral("this"), true);
+ r.storeConsumeAccumulator();
+ }
+ if (innerFunctionAccessesNewTarget) {
+ Instruction::LoadReg load;
+ load.reg = CallData::NewTarget;
+ bytecodeGenerator->addInstruction(load);
+ Codegen::Reference r = codegen->referenceForName(QStringLiteral("new.target"), true);
+ r.storeConsumeAccumulator();
+ }
+
+ if (contextType == ContextType::Global || contextType == ContextType::ScriptImportedByQML || (contextType == ContextType::Eval && !isStrict)) {
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
+ if (it->isLexicallyScoped())
+ continue;
+ const QString &local = it.key();
+
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = (contextType == ContextType::Eval);
+ declareVar.varName = codegen->registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
+ }
+ }
+
+ if (contextType == ContextType::Function || contextType == ContextType::Binding || contextType == ContextType::ESModule) {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (it->canEscape && it->type == Context::ThisFunctionName) {
+ // move the function from the stack to the call context
+ Instruction::LoadReg load;
+ load.reg = CallData::Function;
+ bytecodeGenerator->addInstruction(load);
+ Instruction::StoreLocal store;
+ store.index = it->index;
+ bytecodeGenerator->addInstruction(store);
+ }
+ }
+ }
+
+ if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Q_ASSERT(contextType != ContextType::Block);
+ if (isStrict || (formals && !formals->isSimpleParameterList())) {
+ Instruction::CreateUnmappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ } else {
+ Instruction::CreateMappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ }
+ codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ }
+
+ for (const Context::Member &member : qAsConst(members)) {
+ if (member.function) {
+ const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
+ codegen->loadClosure(function);
+ Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
+ r.storeConsumeAccumulator();
+ }
+ }
+}
+
+void Context::emitBlockFooter(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ if (!requiresExecutionContext)
+ return;
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ if (contextType == ContextType::Global)
+ bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
+ else if (contextType != ContextType::ESModule && contextType != ContextType::ScriptImportedByQML)
+ bytecodeGenerator->addInstruction(Instruction::PopContext());
+QT_WARNING_POP
+}
+
+void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
+{
+ if (registerOffset != -1) {
+ // already computed, check for consistency
+ Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister());
+ bytecodeGenerator->newRegisterArray(nRegisters);
+ return;
+ }
+ Q_ASSERT(locals.size() == 0);
+ Q_ASSERT(nRegisters == 0);
+ registerOffset = bytecodeGenerator->currentRegister();
+
+ QVector<Context::MemberMap::Iterator> localsInTDZ;
+ const auto registerLocal = [this, &localsInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped()) {
+ localsInTDZ << member;
+ } else {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+ };
+
+ QVector<Context::MemberMap::Iterator> registersInTDZ;
+ const auto allocateRegister = [bytecodeGenerator, &registersInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped())
+ registersInTDZ << member;
+ else
+ member->index = bytecodeGenerator->newRegister();
+ };
+
+ switch (contextType) {
+ case ContextType::ESModule:
+ case ContextType::Block:
+ case ContextType::Function:
+ case ContextType::Binding: {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (it->canEscape) {
+ registerLocal(it);
+ } else {
+ if (it->type == Context::ThisFunctionName)
+ it->index = CallData::Function;
+ else
+ allocateRegister(it);
+ }
+ }
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::ScriptImportedByQML:
+ case ContextType::Eval:
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (!it->isLexicallyScoped() && (contextType == ContextType::Global || contextType == ContextType::ScriptImportedByQML || !isStrict))
+ continue;
+ if (it->canEscape)
+ registerLocal(it);
+ else
+ allocateRegister(it);
+ }
+ break;
+ }
+
+ sizeOfLocalTemporalDeadZone = localsInTDZ.count();
+ for (auto &member: qAsConst(localsInTDZ)) {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+
+ if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) {
+ if (!members.contains(localNameForDefaultExport)) {
+ // allocate a local slot for the default export, to be used in
+ // CodeGen::visit(ExportDeclaration*).
+ locals.append(localNameForDefaultExport);
+ ++sizeOfLocalTemporalDeadZone;
+ }
+ }
+
+ sizeOfRegisterTemporalDeadZone = registersInTDZ.count();
+ firstTemporalDeadZoneRegister = bytecodeGenerator->currentRegister();
+ for (auto &member: qAsConst(registersInTDZ))
+ member->index = bytecodeGenerator->newRegister();
+
+ nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
+}
+
+bool Context::canUseTracingJit() const
+{
+#if QT_CONFIG(qml_tracing)
+ static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING");
+ if (forceTracing) //### we can probably remove this when tracing is turned on by default
+ return true; // to be used by unittests
+
+ static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING");
+ if (disableTracing)
+ return false;
+
+ static QStringList onlyTrace =
+ qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts);
+ if (!onlyTrace.isEmpty())
+ return onlyTrace.contains(name);
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
new file mode 100644
index 0000000000..5b91b93346
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -0,0 +1,380 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#ifndef QV4COMPILERCONTEXT_P_H
+#define QV4COMPILERCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qv4global_p.h"
+#include <private/qqmljsast_p.h>
+#include <private/qv4compileddata_p.h>
+#include <QtCore/QStringList>
+#include <QtCore/QDateTime>
+#include <QtCore/QStack>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Compiler {
+
+struct ControlFlow;
+
+enum class ContextType {
+ Global,
+ Function,
+ Eval,
+ Binding, // This is almost the same as Eval, except:
+ // * function declarations are moved to the return address when encountered
+ // * return statements are allowed everywhere (like in FunctionCode)
+ // * variable declarations are treated as true locals (like in FunctionCode)
+ Block,
+ ESModule,
+ ScriptImportedByQML,
+};
+
+struct Context;
+
+struct Class {
+ struct Method {
+ enum Type {
+ Regular,
+ Getter,
+ Setter
+ };
+ uint nameIndex;
+ Type type;
+ uint functionIndex;
+ };
+
+ uint nameIndex;
+ uint constructorIndex = UINT_MAX;
+ QVector<Method> staticMethods;
+ QVector<Method> methods;
+};
+
+struct TemplateObject {
+ QVector<uint> strings;
+ QVector<uint> rawStrings;
+ bool operator==(const TemplateObject &other) {
+ return strings == other.strings && rawStrings == other.rawStrings;
+ }
+};
+
+struct ExportEntry
+{
+ QString exportName;
+ QString moduleRequest;
+ QString importName;
+ QString localName;
+ CompiledData::Location location;
+
+ static bool lessThan(const ExportEntry &lhs, const ExportEntry &rhs)
+ { return lhs.exportName < rhs.exportName; }
+};
+
+struct ImportEntry
+{
+ QString moduleRequest;
+ QString importName;
+ QString localName;
+ CompiledData::Location location;
+};
+
+struct Module {
+ Module(bool debugMode)
+ : debugMode(debugMode)
+ {}
+ ~Module() {
+ qDeleteAll(contextMap);
+ }
+
+ Context *newContext(QQmlJS::AST::Node *node, Context *parent, ContextType compilationMode);
+
+ QHash<QQmlJS::AST::Node *, Context *> contextMap;
+ QList<Context *> functions;
+ QList<Context *> blocks;
+ QVector<Class> classes;
+ QVector<TemplateObject> templateObjects;
+ Context *rootContext;
+ QString fileName;
+ QString finalUrl;
+ QDateTime sourceTimeStamp;
+ uint unitFlags = 0; // flags merged into CompiledData::Unit::flags
+ bool debugMode = false;
+ QVector<ExportEntry> localExportEntries;
+ QVector<ExportEntry> indirectExportEntries;
+ QVector<ExportEntry> starExportEntries;
+ QVector<ImportEntry> importEntries;
+ QStringList moduleRequests;
+};
+
+
+struct Context {
+ Context *parent;
+ QString name;
+ int line = 0;
+ int column = 0;
+ int registerCountInFunction = 0;
+ uint nTraceInfos = 0;
+ int functionIndex = -1;
+ int blockIndex = -1;
+
+ enum MemberType {
+ UndefinedMember,
+ ThisFunctionName,
+ VariableDefinition,
+ VariableDeclaration,
+ FunctionDefinition
+ };
+
+ struct Member {
+ MemberType type = UndefinedMember;
+ int index = -1;
+ QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var;
+ mutable bool canEscape = false;
+ QQmlJS::AST::FunctionExpression *function = nullptr;
+ QQmlJS::AST::SourceLocation endOfInitializerLocation;
+
+ bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; }
+ bool requiresTDZCheck(const QQmlJS::AST::SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const;
+ };
+ typedef QMap<QString, Member> MemberMap;
+
+ MemberMap members;
+ QSet<QString> usedVariables;
+ QQmlJS::AST::FormalParameterList *formals = nullptr;
+ QStringList arguments;
+ QStringList locals;
+ QStringList moduleRequests;
+ QVector<ImportEntry> importEntries;
+ QVector<ExportEntry> exportEntries;
+ QString localNameForDefaultExport;
+ QVector<Context *> nestedContexts;
+
+ ControlFlow *controlFlow = nullptr;
+ QByteArray code;
+ QVector<CompiledData::CodeOffsetToLine> lineNumberMapping;
+ std::vector<unsigned> labelInfo;
+
+ int nRegisters = 0;
+ int registerOffset = -1;
+ int sizeOfLocalTemporalDeadZone = 0;
+ int firstTemporalDeadZoneRegister = 0;
+ int sizeOfRegisterTemporalDeadZone = 0;
+ bool hasDirectEval = false;
+ bool allVarsEscape = false;
+ bool hasNestedFunctions = false;
+ bool isStrict = false;
+ bool isArrowFunction = false;
+ bool isGenerator = false;
+ bool usesThis = false;
+ bool innerFunctionAccessesThis = false;
+ bool innerFunctionAccessesNewTarget = false;
+ bool hasTry = false;
+ bool returnsClosure = false;
+ mutable bool argumentsCanEscape = false;
+ bool requiresExecutionContext = false;
+ bool isWithBlock = false;
+ bool isCatchBlock = false;
+ QString caughtVariable;
+ QQmlJS::AST::SourceLocation lastBlockInitializerLocation;
+
+ enum UsesArgumentsObject {
+ ArgumentsObjectUnknown,
+ ArgumentsObjectNotUsed,
+ ArgumentsObjectUsed
+ };
+
+ UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
+
+ ContextType contextType;
+
+ template <typename T>
+ class SmallSet: public QVarLengthArray<T, 8>
+ {
+ public:
+ void insert(int value)
+ {
+ for (auto it : *this) {
+ if (it == value)
+ return;
+ }
+ this->append(value);
+ }
+ };
+
+ // Map from meta property index (existence implies dependency) to notify signal index
+ struct KeyValuePair
+ {
+ quint32 _key = 0;
+ quint32 _value = 0;
+
+ KeyValuePair() {}
+ KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
+
+ quint32 key() const { return _key; }
+ quint32 value() const { return _value; }
+ };
+
+ class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
+ {
+ public:
+ void insert(quint32 key, quint32 value)
+ {
+ for (auto it = begin(), eit = end(); it != eit; ++it) {
+ if (it->_key == key) {
+ it->_value = value;
+ return;
+ }
+ }
+ append(KeyValuePair(key, value));
+ }
+ };
+
+ // Qml extension:
+ SmallSet<int> idObjectDependencies;
+ PropertyDependencyMap contextObjectPropertyDependencies;
+ PropertyDependencyMap scopeObjectPropertyDependencies;
+
+ Context(Context *parent, ContextType type)
+ : parent(parent)
+ , contextType(type)
+ {
+ if (parent && parent->isStrict)
+ isStrict = true;
+ }
+
+ int findArgument(const QString &name) const
+ {
+ // search backwards to handle duplicate argument names correctly
+ for (int i = arguments.size() - 1; i >= 0; --i) {
+ if (arguments.at(i) == name)
+ return i;
+ }
+ return -1;
+ }
+
+ Member findMember(const QString &name) const
+ {
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end())
+ return Member();
+ Q_ASSERT(it->index != -1 || !parent);
+ return (*it);
+ }
+
+ bool memberInfo(const QString &name, const Member **m) const
+ {
+ Q_ASSERT(m);
+ MemberMap::const_iterator it = members.find(name);
+ if (it == members.end()) {
+ *m = nullptr;
+ return false;
+ }
+ *m = &(*it);
+ return true;
+ }
+
+ bool requiresImplicitReturnValue() const {
+ return contextType == ContextType::Binding ||
+ contextType == ContextType::Eval ||
+ contextType == ContextType::Global || contextType == ContextType::ScriptImportedByQML;
+ }
+
+ void addUsedVariable(const QString &name) {
+ 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());
+
+ struct ResolvedName {
+ enum Type {
+ Unresolved,
+ Global,
+ Local,
+ Stack,
+ Import
+ };
+ Type type = Unresolved;
+ bool isArgOrEval = false;
+ bool isConst = false;
+ bool requiresTDZCheck = false;
+ int scope = -1;
+ int index = -1;
+ QQmlJS::AST::SourceLocation endOfDeclarationLocation;
+ bool isValid() const { return type != Unresolved; }
+ };
+ ResolvedName resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation);
+ void emitBlockHeader(Compiler::Codegen *codegen);
+ void emitBlockFooter(Compiler::Codegen *codegen);
+
+ void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator);
+
+ bool canHaveTailCalls() const
+ {
+ if (!isStrict)
+ return false;
+ if (contextType == ContextType::Function)
+ return !isGenerator;
+ if (contextType == ContextType::Block && parent)
+ return parent->canHaveTailCalls();
+ return false;
+ }
+
+ bool canUseTracingJit() const;
+};
+
+
+} } // namespace QV4::Compiler
+
+QT_END_NAMESPACE
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
new file mode 100644
index 0000000000..5b622e81d8
--- /dev/null
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -0,0 +1,436 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERCONTROLFLOW_P_H
+#define QV4COMPILERCONTROLFLOW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4codegen_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qv4bytecodegenerator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Compiler {
+
+struct ControlFlow {
+ using Reference = Codegen::Reference;
+ using BytecodeGenerator = Moth::BytecodeGenerator;
+ using Instruction = Moth::Instruction;
+
+ enum Type {
+ Loop,
+ With,
+ Block,
+ Finally,
+ Catch
+ };
+
+ enum UnwindType {
+ Break,
+ Continue,
+ Return
+ };
+
+ struct UnwindTarget {
+ BytecodeGenerator::Label linkLabel;
+ int unwindLevel;
+ };
+
+ Codegen *cg;
+ ControlFlow *parent;
+ Type type;
+
+ ControlFlow(Codegen *cg, Type type)
+ : cg(cg), parent(cg->controlFlow), type(type)
+ {
+ cg->controlFlow = this;
+ }
+
+ virtual ~ControlFlow() {
+ cg->controlFlow = parent;
+ }
+
+ UnwindTarget unwindTarget(UnwindType type, const QString &label = QString())
+ {
+ Q_ASSERT(type == Break || type == Continue || type == Return);
+ ControlFlow *flow = this;
+ int level = 0;
+ while (flow) {
+ BytecodeGenerator::Label l = flow->getUnwindTarget(type, label);
+ if (l.isValid())
+ return UnwindTarget{l, level};
+ if (flow->requiresUnwind())
+ ++level;
+ flow = flow->parent;
+ }
+ if (type == Return)
+ return UnwindTarget{ cg->returnLabel(), level };
+ return UnwindTarget();
+ }
+
+ virtual QString label() const { return QString(); }
+
+ bool hasLoop() const {
+ const ControlFlow *flow = this;
+ while (flow) {
+ if (flow->type == Loop)
+ return true;
+ flow = flow->parent;
+ }
+ return false;
+ }
+
+protected:
+ virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
+ return BytecodeGenerator::Label();
+ }
+ virtual bool requiresUnwind() {
+ return false;
+ }
+
+public:
+ BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
+ return parent ? parent->unwindHandler() : nullptr;
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
+ return parentUnwindHandler();
+ }
+
+
+protected:
+ QString loopLabel() const {
+ QString label;
+ if (cg->_labelledStatement) {
+ label = cg->_labelledStatement->label.toString();
+ cg->_labelledStatement = nullptr;
+ }
+ return label;
+ }
+ BytecodeGenerator *generator() const {
+ return cg->bytecodeGenerator;
+ }
+};
+
+struct ControlFlowUnwind : public ControlFlow
+{
+ BytecodeGenerator::ExceptionHandler unwindLabel;
+
+ ControlFlowUnwind(Codegen *cg, Type type)
+ : ControlFlow(cg, type)
+ {
+ }
+
+ void setupUnwindHandler()
+ {
+ unwindLabel = generator()->newExceptionHandler();
+ }
+
+ void emitUnwindHandler()
+ {
+ Q_ASSERT(requiresUnwind());
+
+ Instruction::UnwindDispatch dispatch;
+ generator()->addInstruction(dispatch);
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
+ }
+};
+
+struct ControlFlowUnwindCleanup : public ControlFlowUnwind
+{
+ std::function<void()> cleanup = nullptr;
+
+ ControlFlowUnwindCleanup(Codegen *cg, std::function<void()> cleanup, Type type = Block)
+ : ControlFlowUnwind(cg, type), cleanup(cleanup)
+ {
+ if (cleanup) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+ }
+
+ ~ControlFlowUnwindCleanup() {
+ if (cleanup) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ cleanup();
+ emitUnwindHandler();
+ }
+ }
+
+ bool requiresUnwind() override {
+ return cleanup != nullptr;
+ }
+};
+
+struct ControlFlowLoop : public ControlFlowUnwindCleanup
+{
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = nullptr;
+ BytecodeGenerator::Label *continueLabel = nullptr;
+
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, std::function<void()> cleanup = nullptr)
+ : ControlFlowUnwindCleanup(cg, cleanup, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ {
+ }
+
+ BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return *breakLabel;
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return *continueLabel;
+ break;
+ default:
+ break;
+ }
+ return BytecodeGenerator::Label();
+ }
+
+ QString label() const override { return loopLabel; }
+};
+
+
+struct ControlFlowWith : public ControlFlowUnwind
+{
+ ControlFlowWith(Codegen *cg)
+ : ControlFlowUnwind(cg, With)
+ {
+ setupUnwindHandler();
+
+ // assumes the with object is in the accumulator
+ Instruction::PushWithContext pushScope;
+ generator()->addInstruction(pushScope);
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+
+ ~ControlFlowWith() {
+ // emit code for unwinding
+ unwindLabel.link();
+
+ generator()->setUnwindHandler(parentUnwindHandler());
+ Instruction::PopContext pop;
+ generator()->addInstruction(pop);
+
+ emitUnwindHandler();
+ }
+
+ bool requiresUnwind() override {
+ return true;
+ }
+
+
+};
+
+struct ControlFlowBlock : public ControlFlowUnwind
+{
+ ControlFlowBlock(Codegen *cg, AST::Node *ast)
+ : ControlFlowUnwind(cg, Block)
+ {
+ block = cg->enterBlock(ast);
+ block->emitBlockHeader(cg);
+
+ if (block->requiresExecutionContext) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+ }
+
+ virtual ~ControlFlowBlock() {
+ // emit code for unwinding
+ if (block->requiresExecutionContext) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ }
+
+ block->emitBlockFooter(cg);
+
+ if (block->requiresExecutionContext )
+ emitUnwindHandler();
+ cg->leaveBlock();
+ }
+
+ virtual bool requiresUnwind() override {
+ return block->requiresExecutionContext;
+ }
+
+ Context *block;
+};
+
+struct ControlFlowCatch : public ControlFlowUnwind
+{
+ AST::Catch *catchExpression;
+ bool insideCatch = false;
+ BytecodeGenerator::ExceptionHandler exceptionLabel;
+
+ ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
+ : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
+ exceptionLabel(generator()->newExceptionHandler())
+ {
+ generator()->setUnwindHandler(&exceptionLabel);
+ }
+
+ virtual bool requiresUnwind() override {
+ return true;
+ }
+
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideCatch ? &unwindLabel : &exceptionLabel;
+ }
+
+ ~ControlFlowCatch() {
+ // emit code for unwinding
+ insideCatch = true;
+ setupUnwindHandler();
+
+ Codegen::RegisterScope scope(cg);
+
+ // exceptions inside the try block go here
+ exceptionLabel.link();
+ BytecodeGenerator::Jump noException = generator()->jumpNoException();
+
+ Context *block = cg->enterBlock(catchExpression);
+
+ block->emitBlockHeader(cg);
+
+ generator()->setUnwindHandler(&unwindLabel);
+
+ if (catchExpression->patternElement->bindingIdentifier.isEmpty())
+ // destructuring pattern
+ cg->initializeAndDestructureBindingElement(catchExpression->patternElement, Reference::fromName(cg, QStringLiteral("@caught")));
+ // skip the additional block
+ cg->statementList(catchExpression->statement->statements);
+
+ // exceptions inside catch and break/return statements go here
+ unwindLabel.link();
+ block->emitBlockFooter(cg);
+
+ cg->leaveBlock();
+
+ noException.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+
+ emitUnwindHandler();
+ insideCatch = false;
+ }
+};
+
+struct ControlFlowFinally : public ControlFlowUnwind
+{
+ AST::Finally *finally;
+ bool insideFinally = false;
+
+ ControlFlowFinally(Codegen *cg, AST::Finally *finally)
+ : ControlFlowUnwind(cg, Finally), finally(finally)
+ {
+ Q_ASSERT(finally != nullptr);
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+
+ virtual bool requiresUnwind() override {
+ return !insideFinally;
+ }
+
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
+ }
+
+ ~ControlFlowFinally() {
+ // emit code for unwinding
+ unwindLabel.link();
+
+ Codegen::RegisterScope scope(cg);
+
+ insideFinally = true;
+ int returnValueTemp = -1;
+ if (cg->requiresReturnValue) {
+ returnValueTemp = generator()->newRegister();
+ Instruction::MoveReg move;
+ move.srcReg = cg->_returnAddress;
+ move.destReg = returnValueTemp;
+ generator()->addInstruction(move);
+ }
+ int exceptionTemp = generator()->newRegister();
+ Instruction::GetException instr;
+ generator()->addInstruction(instr);
+ Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
+
+ generator()->setUnwindHandler(parentUnwindHandler());
+ cg->statement(finally->statement);
+ insideFinally = false;
+
+ if (cg->requiresReturnValue) {
+ Instruction::MoveReg move;
+ move.srcReg = returnValueTemp;
+ move.destReg = cg->_returnAddress;
+ generator()->addInstruction(move);
+ }
+ Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
+ Instruction::SetException setException;
+ generator()->addInstruction(setException);
+
+ emitUnwindHandler();
+ }
+};
+
+} } // QV4::Compiler namespace
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
new file mode 100644
index 0000000000..e0eaa8867b
--- /dev/null
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -0,0 +1,895 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4compilerscanfunctions_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtCore/QSet>
+#include <QtCore/QBuffer>
+#include <QtCore/QBitArray>
+#include <QtCore/QLinkedList>
+#include <QtCore/QStack>
+#include <private/qqmljsast_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4codegen_p.h>
+#include <private/qv4string_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::Compiler;
+using namespace QQmlJS::AST;
+
+ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
+ : _cg(cg)
+ , _sourceCode(sourceCode)
+ , _context(nullptr)
+ , _allowFuncDecls(true)
+ , defaultProgramType(defaultProgramType)
+{
+}
+
+void ScanFunctions::operator()(Node *node)
+{
+ if (node)
+ node->accept(this);
+
+ calcEscapingVariables();
+}
+
+void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode)
+{
+ enterEnvironment(astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode"));
+}
+
+void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name)
+{
+ Context *c = _cg->_module->contextMap.value(node);
+ if (!c)
+ c = _cg->_module->newContext(node, _context, compilationMode);
+ if (!c->isStrict)
+ c->isStrict = _cg->_strictMode;
+ c->name = name;
+ _contextStack.append(c);
+ _context = c;
+}
+
+void ScanFunctions::leaveEnvironment()
+{
+ _contextStack.pop();
+ _context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
+}
+
+bool ScanFunctions::preVisit(Node *ast)
+{
+ if (_cg->hasError)
+ return false;
+ ++_recursionDepth;
+
+ if (_recursionDepth > 1000) {
+ _cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded"));
+ return false;
+ }
+
+ return true;
+}
+
+void ScanFunctions::postVisit(Node *)
+{
+ --_recursionDepth;
+}
+
+void ScanFunctions::checkDirectivePrologue(StatementList *ast)
+{
+ for (StatementList *it = ast; it; it = it->next) {
+ if (ExpressionStatement *expr = cast<ExpressionStatement *>(it->statement)) {
+ if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
+ // Use the source code, because the StringLiteral's
+ // value might have escape sequences in it, which is not
+ // allowed.
+ if (strLit->literalToken.length < 2)
+ continue;
+ QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
+ if (str == QLatin1String("use strict")) {
+ _context->isStrict = true;
+ } else {
+ // TODO: give a warning.
+ }
+ continue;
+ }
+ }
+
+ break;
+ }
+}
+
+void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
+{
+ if (_context->isStrict) {
+ if (name == QLatin1String("implements")
+ || name == QLatin1String("interface")
+ || name == QLatin1String("let")
+ || name == QLatin1String("package")
+ || name == QLatin1String("private")
+ || name == QLatin1String("protected")
+ || name == QLatin1String("public")
+ || name == QLatin1String("static")
+ || name == QLatin1String("yield")) {
+ _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word"));
+ }
+ }
+}
+
+bool ScanFunctions::visit(Program *ast)
+{
+ enterEnvironment(ast, defaultProgramType, QStringLiteral("%ProgramCode"));
+ checkDirectivePrologue(ast->statements);
+ return true;
+}
+
+void ScanFunctions::endVisit(Program *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ESModule *ast)
+{
+ enterEnvironment(ast, defaultProgramType, QStringLiteral("%ModuleCode"));
+ _context->isStrict = true;
+ return true;
+}
+
+void ScanFunctions::endVisit(ESModule *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ExportDeclaration *declaration)
+{
+ QString module;
+ if (declaration->fromClause) {
+ module = declaration->fromClause->moduleSpecifier.toString();
+ if (!module.isEmpty())
+ _context->moduleRequests << module;
+ }
+
+ QString localNameForDefaultExport = QStringLiteral("*default*");
+
+ if (declaration->exportAll) {
+ Compiler::ExportEntry entry;
+ entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString();
+ entry.importName = QStringLiteral("*");
+ entry.location = declaration->firstSourceLocation();
+ _context->exportEntries << entry;
+ } else if (declaration->exportClause) {
+ for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) {
+ ExportSpecifier *spec = it->exportSpecifier;
+ Compiler::ExportEntry entry;
+ if (module.isEmpty())
+ entry.localName = spec->identifier.toString();
+ else
+ entry.importName = spec->identifier.toString();
+
+ entry.moduleRequest = module;
+ entry.exportName = spec->exportedIdentifier.toString();
+ entry.location = it->firstSourceLocation();
+
+ _context->exportEntries << entry;
+ }
+ } else if (auto *vstmt = AST::cast<AST::VariableStatement*>(declaration->variableStatementOrDeclaration)) {
+ QStringList boundNames;
+ for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) {
+ if (!it->declaration)
+ continue;
+ it->declaration->boundNames(&boundNames);
+ }
+ for (const QString &name: boundNames) {
+ Compiler::ExportEntry entry;
+ entry.localName = name;
+ entry.exportName = name;
+ entry.location = vstmt->firstSourceLocation();
+ _context->exportEntries << entry;
+ }
+ } else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(declaration->variableStatementOrDeclaration)) {
+ QString name = classDecl->name.toString();
+ if (!name.isEmpty()) {
+ Compiler::ExportEntry entry;
+ entry.localName = name;
+ entry.exportName = name;
+ entry.location = classDecl->firstSourceLocation();
+ _context->exportEntries << entry;
+ if (declaration->exportDefault)
+ localNameForDefaultExport = entry.localName;
+ }
+ } else if (auto *fdef = declaration->variableStatementOrDeclaration->asFunctionDefinition()) {
+ QString functionName;
+
+ // Only function definitions for which we enter their name into the local environment
+ // can result in exports. Nested expressions such as (function foo() {}) are not accessible
+ // as locals and can only be exported as default exports (further down).
+ auto ast = declaration->variableStatementOrDeclaration;
+ if (AST::cast<AST::ExpressionStatement*>(ast) || AST::cast<AST::FunctionDeclaration*>(ast))
+ functionName = fdef->name.toString();
+
+ if (!functionName.isEmpty()) {
+ Compiler::ExportEntry entry;
+ entry.localName = functionName;
+ entry.exportName = functionName;
+ entry.location = fdef->firstSourceLocation();
+ _context->exportEntries << entry;
+ if (declaration->exportDefault)
+ localNameForDefaultExport = entry.localName;
+ }
+ }
+
+ if (declaration->exportDefault) {
+ Compiler::ExportEntry entry;
+ entry.localName = localNameForDefaultExport;
+ _context->localNameForDefaultExport = localNameForDefaultExport;
+ entry.exportName = QStringLiteral("default");
+ entry.location = declaration->firstSourceLocation();
+ _context->exportEntries << entry;
+ }
+
+ return true; // scan through potential assignment expression code, etc.
+}
+
+bool ScanFunctions::visit(ImportDeclaration *declaration)
+{
+ QString module;
+ if (declaration->fromClause) {
+ module = declaration->fromClause->moduleSpecifier.toString();
+ if (!module.isEmpty())
+ _context->moduleRequests << module;
+ }
+
+ if (!declaration->moduleSpecifier.isEmpty())
+ _context->moduleRequests << declaration->moduleSpecifier.toString();
+
+ if (ImportClause *import = declaration->importClause) {
+ if (!import->importedDefaultBinding.isEmpty()) {
+ Compiler::ImportEntry entry;
+ entry.moduleRequest = module;
+ entry.importName = QStringLiteral("default");
+ entry.localName = import->importedDefaultBinding.toString();
+ entry.location = declaration->firstSourceLocation();
+ _context->importEntries << entry;
+ }
+
+ if (import->nameSpaceImport) {
+ Compiler::ImportEntry entry;
+ entry.moduleRequest = module;
+ entry.importName = QStringLiteral("*");
+ entry.localName = import->nameSpaceImport->importedBinding.toString();
+ entry.location = declaration->firstSourceLocation();
+ _context->importEntries << entry;
+ }
+
+ if (import->namedImports) {
+ for (ImportsList *it = import->namedImports->importsList; it; it = it->next) {
+ Compiler::ImportEntry entry;
+ entry.moduleRequest = module;
+ entry.localName = it->importSpecifier->importedBinding.toString();
+ if (!it->importSpecifier->identifier.isEmpty())
+ entry.importName = it->importSpecifier->identifier.toString();
+ else
+ entry.importName = entry.localName;
+ entry.location = declaration->firstSourceLocation();
+ _context->importEntries << entry;
+ }
+ }
+ }
+ return false;
+}
+
+bool ScanFunctions::visit(CallExpression *ast)
+{
+ if (!_context->hasDirectEval) {
+ if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("eval")) {
+ if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ _context->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ _context->hasDirectEval = true;
+ }
+ }
+ }
+ return true;
+}
+
+bool ScanFunctions::visit(PatternElement *ast)
+{
+ if (!ast->isVariableDeclaration())
+ return true;
+
+ QStringList names;
+ ast->boundNames(&names);
+
+ QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation();
+ if (_context->lastBlockInitializerLocation.isValid())
+ lastInitializerLocation = _context->lastBlockInitializerLocation;
+
+ for (const QString &name : qAsConst(names)) {
+ if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ checkName(QStringRef(&name), ast->identifierToken);
+ if (name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
+ return false;
+ }
+ if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope,
+ /*function*/nullptr, lastInitializerLocation)) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ScanFunctions::visit(IdentifierExpression *ast)
+{
+ checkName(ast->name, ast->identifierToken);
+ if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ _context->addUsedVariable(ast->name.toString());
+ return true;
+}
+
+bool ScanFunctions::visit(ExpressionStatement *ast)
+{
+ if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) {
+ if (!_allowFuncDecls)
+ _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
+
+ if (!enterFunction(expr, /*enterName*/ true))
+ 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")) {
+ _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token"));
+ }
+ }
+ return true;
+}
+
+bool ScanFunctions::visit(FunctionExpression *ast)
+{
+ return enterFunction(ast, /*enterName*/ false);
+}
+
+bool ScanFunctions::visit(ClassExpression *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
+ return true;
+}
+
+void ScanFunctions::endVisit(ClassExpression *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ClassDeclaration *ast)
+{
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let);
+
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
+ return true;
+}
+
+void ScanFunctions::endVisit(ClassDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(TemplateLiteral *ast)
+{
+ while (ast) {
+ if (ast->expression)
+ Node::accept(ast->expression, this);
+ ast = ast->next;
+ }
+ return true;
+
+}
+
+bool ScanFunctions::visit(SuperLiteral *)
+{
+ Context *c = _context;
+ bool needContext = false;
+ while (c && (c->contextType == ContextType::Block || c->isArrowFunction)) {
+ needContext |= c->isArrowFunction;
+ c = c->parent;
+ }
+
+ c->requiresExecutionContext |= needContext;
+
+ return false;
+}
+
+bool ScanFunctions::visit(FieldMemberExpression *ast)
+{
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ if (ast->name != QLatin1String("target")) {
+ _cg->throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'."));
+ return false;
+ }
+ Context *c = _context;
+ bool needContext = false;
+ while (c->contextType == ContextType::Block || c->isArrowFunction) {
+ needContext |= c->isArrowFunction;
+ c = c->parent;
+ }
+ c->requiresExecutionContext |= needContext;
+ c->innerFunctionAccessesNewTarget |= needContext;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ScanFunctions::visit(ArrayPattern *ast)
+{
+ for (PatternElementList *it = ast->elements; it; it = it->next)
+ Node::accept(it->element, this);
+
+ return false;
+}
+
+bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
+{
+ 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);
+}
+
+void ScanFunctions::endVisit(FunctionExpression *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ObjectPattern *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ Node::accept(ast->properties, this);
+ return false;
+}
+
+bool ScanFunctions::visit(PatternProperty *ast)
+{
+ Q_UNUSED(ast);
+ // ### Shouldn't be required anymore
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) {
+// TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+// return enterFunction(ast, QString(), ast->formals, ast->functionBody, /*enterName */ false);
+// }
+ return true;
+}
+
+void ScanFunctions::endVisit(PatternProperty *)
+{
+ // ###
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter)
+// leaveEnvironment();
+}
+
+bool ScanFunctions::visit(FunctionDeclaration *ast)
+{
+ return enterFunction(ast, /*enterName*/ true);
+}
+
+void ScanFunctions::endVisit(FunctionDeclaration *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(DoWhileStatement *ast) {
+ {
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+ }
+ Node::accept(ast->expression, this);
+ return false;
+}
+
+bool ScanFunctions::visit(ForStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%For"));
+ Node::accept(ast->initialiser, this);
+ Node::accept(ast->declarations, this);
+ Node::accept(ast->condition, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+void ScanFunctions::endVisit(ForStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ForEachStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach"));
+ if (ast->expression)
+ _context->lastBlockInitializerLocation = ast->expression->lastSourceLocation();
+ Node::accept(ast->lhs, this);
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+void ScanFunctions::endVisit(ForEachStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ThisExpression *)
+{
+ _context->usesThis = true;
+ return false;
+}
+
+bool ScanFunctions::visit(Block *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Block"));
+ Node::accept(ast->statements, this);
+ return false;
+}
+
+void ScanFunctions::endVisit(Block *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(CaseBlock *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CaseBlock"));
+ return true;
+}
+
+void ScanFunctions::endVisit(CaseBlock *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(Catch *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CatchBlock"));
+ _context->isCatchBlock = true;
+ QString caughtVar = ast->patternElement->bindingIdentifier.toString();
+ if (caughtVar.isEmpty())
+ caughtVar = QStringLiteral("@caught");
+ _context->addLocalVar(caughtVar, Context::MemberType::VariableDefinition, VariableScope::Let);
+
+ _context->caughtVariable = caughtVar;
+ if (_context->isStrict &&
+ (caughtVar == QLatin1String("eval") || caughtVar == QLatin1String("arguments"))) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
+ return false;
+ }
+ Node::accept(ast->patternElement, this);
+ // skip the block statement
+ Node::accept(ast->statement->statements, this);
+ return false;
+}
+
+void ScanFunctions::endVisit(Catch *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(WithStatement *ast)
+{
+ Node::accept(ast->expression, this);
+
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%WithBlock"));
+ _context->isWithBlock = true;
+
+ if (_context->isStrict) {
+ _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
+ return false;
+ }
+ Node::accept(ast->statement, this);
+
+ return false;
+}
+
+void ScanFunctions::endVisit(WithStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName)
+{
+ Context *outerContext = _context;
+ enterEnvironment(ast, ContextType::Function, name);
+
+ FunctionExpression *expr = AST::cast<FunctionExpression *>(ast);
+ if (!expr)
+ expr = AST::cast<FunctionDeclaration *>(ast);
+ if (outerContext) {
+ outerContext->hasNestedFunctions = true;
+ // The identifier of a function expression cannot be referenced from the enclosing environment.
+ if (enterName) {
+ if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) {
+ _cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
+ return false;
+ }
+ outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr);
+ }
+ if (name == QLatin1String("arguments"))
+ outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
+
+ _context->name = name;
+ if (formals && formals->containsName(QStringLiteral("arguments")))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (expr) {
+ if (expr->isArrowFunction)
+ _context->isArrowFunction = true;
+ else if (expr->isGenerator)
+ _context->isGenerator = true;
+ }
+
+
+ if (!enterName && (!name.isEmpty() && (!formals || !formals->containsName(name))))
+ _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
+ _context->formals = formals;
+
+ if (body && !_context->isStrict)
+ checkDirectivePrologue(body);
+
+ bool isSimpleParameterList = formals && formals->isSimpleParameterList();
+
+ _context->arguments = formals ? formals->formals() : QStringList();
+
+ const QStringList boundNames = formals ? formals->boundNames() : QStringList();
+ for (int i = 0; i < boundNames.size(); ++i) {
+ const QString &arg = boundNames.at(i);
+ if (_context->isStrict || !isSimpleParameterList) {
+ bool duplicate = (boundNames.indexOf(arg, i + 1) != -1);
+ if (duplicate) {
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg));
+ 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));
+ return false;
+ }
+ }
+ if (!_context->arguments.contains(arg))
+ _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var);
+ }
+ return true;
+}
+
+void ScanFunctions::calcEscapingVariables()
+{
+ Module *m = _cg->_module;
+
+ for (Context *inner : qAsConst(m->contextMap)) {
+ if (inner->usesArgumentsObject != Context::ArgumentsObjectUsed)
+ continue;
+ if (inner->contextType != ContextType::Block && !inner->isArrowFunction)
+ continue;
+ Context *c = inner->parent;
+ while (c && (c->contextType == ContextType::Block || c->isArrowFunction))
+ c = c->parent;
+ if (c)
+ c->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
+ for (Context *inner : qAsConst(m->contextMap)) {
+ if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ QString arguments = QStringLiteral("arguments");
+ inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var);
+ if (!inner->isStrict) {
+ inner->argumentsCanEscape = true;
+ inner->requiresExecutionContext = true;
+ }
+ }
+ }
+
+ for (Context *c : qAsConst(m->contextMap)) {
+ if (c->contextType != ContextType::ESModule)
+ continue;
+ for (const auto &entry: c->exportEntries) {
+ auto m = c->members.find(entry.localName);
+ if (m != c->members.end())
+ m->canEscape = true;
+ }
+ break;
+ }
+
+ for (Context *inner : qAsConst(m->contextMap)) {
+ for (const QString &var : qAsConst(inner->usedVariables)) {
+ Context *c = inner;
+ while (c) {
+ Context *current = c;
+ c = c->parent;
+ if (current->isWithBlock || current->contextType != ContextType::Block)
+ break;
+ }
+ Q_ASSERT(c != inner);
+ while (c) {
+ Context::MemberMap::const_iterator it = c->members.find(var);
+ if (it != c->members.end()) {
+ if (c->parent || it->isLexicallyScoped()) {
+ it->canEscape = true;
+ c->requiresExecutionContext = true;
+ } else if (c->contextType == ContextType::ESModule) {
+ // Module instantiation provides a context, but vars used from inner
+ // scopes need to be stored in its locals[].
+ it->canEscape = true;
+ }
+ break;
+ }
+ if (c->findArgument(var) != -1) {
+ c->argumentsCanEscape = true;
+ c->requiresExecutionContext = true;
+ break;
+ }
+ c = c->parent;
+ }
+ }
+ if (inner->hasDirectEval) {
+ inner->hasDirectEval = false;
+ inner->innerFunctionAccessesNewTarget = true;
+ if (!inner->isStrict) {
+ Context *c = inner;
+ while (c->contextType == ContextType::Block) {
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ c->hasDirectEval = true;
+ c->innerFunctionAccessesThis = true;
+ }
+ Context *c = inner;
+ while (c) {
+ c->allVarsEscape = true;
+ c = c->parent;
+ }
+ }
+ if (inner->usesThis) {
+ inner->usesThis = false;
+ bool innerFunctionAccessesThis = false;
+ Context *c = inner;
+ while (c->contextType == ContextType::Block || c->isArrowFunction) {
+ innerFunctionAccessesThis |= c->isArrowFunction;
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ if (!inner->isStrict)
+ c->usesThis = true;
+ c->innerFunctionAccessesThis |= innerFunctionAccessesThis;
+ }
+ }
+ for (Context *c : qAsConst(m->contextMap)) {
+ if (c->innerFunctionAccessesThis) {
+ // add an escaping 'this' variable
+ c->addLocalVar(QStringLiteral("this"), Context::VariableDefinition, VariableScope::Let);
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(QStringLiteral("this"));
+ m->canEscape = true;
+ }
+ if (c->innerFunctionAccessesNewTarget) {
+ // add an escaping 'new.target' variable
+ c->addLocalVar(QStringLiteral("new.target"), Context::VariableDefinition, VariableScope::Let);
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(QStringLiteral("new.target"));
+ m->canEscape = true;
+ }
+ if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
+ c->allVarsEscape = false;
+ if (c->contextType == ContextType::Global || c->contextType == ContextType::ScriptImportedByQML || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
+ c->allVarsEscape = true;
+ if (c->allVarsEscape) {
+ if (c->parent) {
+ c->requiresExecutionContext = true;
+ c->argumentsCanEscape = true;
+ } else {
+ for (const auto &m : qAsConst(c->members)) {
+ if (m.isLexicallyScoped()) {
+ c->requiresExecutionContext = true;
+ break;
+ }
+ }
+ }
+ }
+ if (c->contextType == ContextType::Block && c->isCatchBlock) {
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(c->caughtVariable);
+ m->canEscape = true;
+ }
+ const QLatin1String exprForOn("expression for on");
+ if (c->contextType == ContextType::Binding && c->name.length() > 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 (auto &m : c->members)
+ m.canEscape = true;
+ }
+ }
+
+ static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS");
+ if (showEscapingVars) {
+ qDebug() << "==== escaping variables ====";
+ for (Context *c : qAsConst(m->contextMap)) {
+ qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict;
+ qDebug() << " isArrowFunction" << c->isArrowFunction << "innerFunctionAccessesThis" << c->innerFunctionAccessesThis;
+ qDebug() << " parent:" << c->parent;
+ if (c->argumentsCanEscape)
+ qDebug() << " Arguments escape";
+ for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
+ qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
+ }
+ }
+ }
+}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
new file mode 100644
index 0000000000..28ad846bcd
--- /dev/null
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4COMPILERSCANFUNCTIONS_P_H
+#define QV4COMPILERSCANFUNCTIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qv4global_p.h"
+#include <private/qqmljsastvisitor_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4util_p.h>
+#include <QtCore/QStringList>
+#include <QStack>
+#include <QScopedValueRollback>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQmlJS;
+
+namespace QV4 {
+
+namespace Moth {
+struct Instruction;
+}
+
+namespace CompiledData {
+struct CompilationUnit;
+}
+
+namespace Compiler {
+
+class Codegen;
+
+class ScanFunctions: protected QQmlJS::AST::Visitor
+{
+ typedef QScopedValueRollback<bool> TemporaryBoolAssignment;
+public:
+ ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
+ void operator()(AST::Node *node);
+
+ void enterGlobalEnvironment(ContextType compilationMode);
+ void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name);
+ void leaveEnvironment();
+
+ void enterQmlFunction(AST::FunctionDeclaration *ast)
+ { enterFunction(ast, false); }
+
+protected:
+ using Visitor::visit;
+ using Visitor::endVisit;
+
+ bool preVisit(AST::Node *ast) override;
+ void postVisit(AST::Node *) override;
+
+ void checkDirectivePrologue(AST::StatementList *ast);
+
+ void checkName(const QStringRef &name, const AST::SourceLocation &loc);
+
+ bool visit(AST::Program *ast) override;
+ void endVisit(AST::Program *) override;
+
+ bool visit(AST::ESModule *ast) override;
+ void endVisit(AST::ESModule *) override;
+
+ bool visit(AST::ExportDeclaration *declaration) override;
+ bool visit(AST::ImportDeclaration *declaration) override;
+
+ bool visit(AST::CallExpression *ast) override;
+ bool visit(AST::PatternElement *ast) override;
+ bool visit(AST::IdentifierExpression *ast) override;
+ bool visit(AST::ExpressionStatement *ast) override;
+ bool visit(AST::FunctionExpression *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
+ bool visit(AST::SuperLiteral *) override;
+ bool visit(AST::FieldMemberExpression *) override;
+ bool visit(AST::ArrayPattern *) override;
+
+ bool enterFunction(AST::FunctionExpression *ast, bool enterName);
+
+ void endVisit(AST::FunctionExpression *) override;
+
+ bool visit(AST::ObjectPattern *ast) override;
+
+ bool visit(AST::PatternProperty *ast) override;
+ void endVisit(AST::PatternProperty *) override;
+
+ bool visit(AST::FunctionDeclaration *ast) override;
+ void endVisit(AST::FunctionDeclaration *) override;
+
+ bool visit(AST::ClassExpression *ast) override;
+ void endVisit(AST::ClassExpression *) override;
+
+ bool visit(AST::ClassDeclaration *ast) override;
+ void endVisit(AST::ClassDeclaration *) override;
+
+ bool visit(AST::DoWhileStatement *ast) override;
+ bool visit(AST::ForStatement *ast) override;
+ void endVisit(AST::ForStatement *) override;
+ bool visit(AST::ForEachStatement *ast) override;
+ void endVisit(AST::ForEachStatement *) override;
+
+ bool visit(AST::ThisExpression *ast) override;
+
+ bool visit(AST::Block *ast) override;
+ void endVisit(AST::Block *ast) override;
+
+ bool visit(AST::CaseBlock *ast) override;
+ void endVisit(AST::CaseBlock *ast) override;
+
+ bool visit(AST::Catch *ast) override;
+ void endVisit(AST::Catch *ast) override;
+
+ bool visit(AST::WithStatement *ast) override;
+ void endVisit(AST::WithStatement *ast) override;
+
+protected:
+ bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
+
+ void calcEscapingVariables();
+// fields:
+ Codegen *_cg;
+ const QString _sourceCode;
+ Context *_context;
+ QStack<Context *> _contextStack;
+
+ bool _allowFuncDecls;
+ ContextType defaultProgramType;
+
+ unsigned _recursionDepth = 0;
+
+private:
+ static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
+};
+
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4CODEGEN_P_H
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index cf8cf623bc..c0b1be1492 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -38,17 +38,711 @@
****************************************************************************/
#include "qv4instr_moth_p.h"
+#include <private/qv4compileddata_p.h>
+#include <private/qv4stackframe_p.h>
using namespace QV4;
using namespace QV4::Moth;
-int Instr::size(Type type)
+int InstrInfo::size(Instr::Type type)
{
-#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size;
+#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: case Instr::Type::I##_Wide: return InstrMeta<int(Instr::Type::I)>::Size;
switch (type) {
- FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE)
- default: return 0;
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_RETURN_INSTR_SIZE)
}
#undef MOTH_RETURN_INSTR_SIZE
+ Q_UNREACHABLE();
}
+static QByteArray alignedNumber(int n) {
+ QByteArray number = QByteArray::number(n);
+ while (number.size() < 8)
+ number.prepend(' ');
+ return number;
+}
+
+static QByteArray alignedLineNumber(int line) {
+ if (line > 0)
+ return alignedNumber(static_cast<int>(line));
+ return QByteArray(" ");
+}
+
+static QByteArray rawBytes(const char *data, int n)
+{
+ QByteArray ba;
+ while (n) {
+ uint num = *reinterpret_cast<const uchar *>(data);
+ if (num < 16)
+ ba += '0';
+ ba += QByteArray::number(num, 16) + " ";
+ ++data;
+ --n;
+ }
+ while (ba.size() < 25)
+ ba += ' ';
+ return ba;
+}
+
+static QString toString(QV4::ReturnedValue v)
+{
+#ifdef V4_BOOTSTRAP
+ return QStringLiteral("string-const(%1)").arg(v);
+#else // !V4_BOOTSTRAP
+ Value val = Value::fromReturnedValue(v);
+ QString result;
+ if (val.isInt32())
+ result = QLatin1String("int ");
+ else if (val.isDouble())
+ result = QLatin1String("double ");
+ if (val.isEmpty())
+ result += QLatin1String("empty");
+ else
+ result += val.toQStringNoThrow();
+ return result;
+#endif // V4_BOOTSTRAP
+}
+
+#define ABSOLUTE_OFFSET() \
+ (code - 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() << ": " \
+ << rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
+
+#define MOTH_END_INSTR(instr) \
+ continue; \
+ }
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace Moth {
+
+const int InstrInfo::argumentCount[] = {
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS)
+};
+
+
+void dumpConstantTable(const Value *constants, uint count)
+{
+ QDebug d = qDebug();
+ d.nospace();
+ for (uint i = 0; i < count; ++i)
+ d << alignedNumber(int(i)).constData() << ": "
+ << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n";
+}
+
+QString dumpRegister(int reg, int nFormals)
+{
+ Q_STATIC_ASSERT(offsetof(CallData, function) == 0);
+ Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value));
+ Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value));
+ Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value));
+ if (reg == CallData::Function)
+ return QStringLiteral("(function)");
+ else if (reg == CallData::Context)
+ return QStringLiteral("(context)");
+ else if (reg == CallData::Accumulator)
+ return QStringLiteral("(accumulator)");
+ else if (reg == CallData::NewTarget)
+ return QStringLiteral("(new.target)");
+ else if (reg == CallData::This)
+ return QStringLiteral("(this)");
+ else if (reg == CallData::Argc)
+ return QStringLiteral("(argc)");
+ reg -= CallData::HeaderSize();
+ if (reg < nFormals)
+ return QStringLiteral("a%1").arg(reg);
+ reg -= nFormals;
+ return QStringLiteral("r%1").arg(reg);
+
+}
+
+QString dumpArguments(int argc, int argv, int nFormals)
+{
+ if (!argc)
+ return QStringLiteral("()");
+ return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")");
+}
+
+#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot)
+
+void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping)
+{
+ MOTH_JUMP_TABLE;
+
+ auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
+ return entry.codeOffset < offset;
+ };
+
+ int lastLine = -1;
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ const CompiledData::CodeOffsetToLine *codeToLine = std::lower_bound(lineNumberMapping.constBegin(), lineNumberMapping.constEnd(), static_cast<uint>(code - start) + 1, findLine) - 1;
+ int line = int(codeToLine->line);
+ if (line != lastLine)
+ lastLine = line;
+ else
+ line = -1;
+
+ int codeOffset = int(code - start);
+
+ MOTH_DISPATCH()
+
+ MOTH_BEGIN_INSTR(LoadReg)
+ d << dumpRegister(reg, nFormals);
+ MOTH_END_INSTR(LoadReg)
+
+ MOTH_BEGIN_INSTR(StoreReg)
+ d << dumpRegister(reg, nFormals);
+ MOTH_END_INSTR(StoreReg)
+
+ MOTH_BEGIN_INSTR(MoveReg)
+ d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals);
+ MOTH_END_INSTR(MoveReg)
+
+ MOTH_BEGIN_INSTR(LoadImport)
+ d << "i" << index;
+ MOTH_END_INSTR(LoadImport)
+
+ MOTH_BEGIN_INSTR(LoadConst)
+ d << "C" << index;
+ MOTH_END_INSTR(LoadConst)
+
+ MOTH_BEGIN_INSTR(LoadNull)
+ MOTH_END_INSTR(LoadNull)
+
+ MOTH_BEGIN_INSTR(LoadZero)
+ MOTH_END_INSTR(LoadZero)
+
+ MOTH_BEGIN_INSTR(LoadTrue)
+ MOTH_END_INSTR(LoadTrue)
+
+ MOTH_BEGIN_INSTR(LoadFalse)
+ MOTH_END_INSTR(LoadFalse)
+
+ MOTH_BEGIN_INSTR(LoadUndefined)
+ MOTH_END_INSTR(LoadUndefined)
+
+ MOTH_BEGIN_INSTR(LoadInt)
+ d << value;
+ MOTH_END_INSTR(LoadInt)
+
+ MOTH_BEGIN_INSTR(MoveConst)
+ d << dumpRegister(destTemp, nFormals) << ", C" << constIndex;
+ MOTH_END_INSTR(MoveConst)
+
+ MOTH_BEGIN_INSTR(LoadLocal)
+ if (index < nLocals)
+ d << "l" << index << TRACE_SLOT;
+ else
+ d << "a" << (index - nLocals) << TRACE_SLOT;
+ MOTH_END_INSTR(LoadLocal)
+
+ MOTH_BEGIN_INSTR(StoreLocal)
+ if (index < nLocals)
+ d << "l" << index;
+ else
+ d << "a" << (index - nLocals);
+ MOTH_END_INSTR(StoreLocal)
+
+ MOTH_BEGIN_INSTR(LoadScopedLocal)
+ if (index < nLocals)
+ d << "l" << index << "@" << scope << TRACE_SLOT;
+ else
+ d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT;
+ MOTH_END_INSTR(LoadScopedLocal)
+
+ MOTH_BEGIN_INSTR(StoreScopedLocal)
+ if (index < nLocals)
+ d << ", " << "l" << index << "@" << scope;
+ else
+ d << ", " << "a" << (index - nLocals) << "@" << scope;
+ MOTH_END_INSTR(StoreScopedLocal)
+
+ MOTH_BEGIN_INSTR(LoadRuntimeString)
+ d << stringId;
+ MOTH_END_INSTR(LoadRuntimeString)
+
+ MOTH_BEGIN_INSTR(MoveRegExp)
+ d << dumpRegister(destReg, nFormals) << ", " <<regExpId;
+ MOTH_END_INSTR(MoveRegExp)
+
+ MOTH_BEGIN_INSTR(LoadClosure)
+ d << value;
+ MOTH_END_INSTR(LoadClosure)
+
+ MOTH_BEGIN_INSTR(LoadName)
+ d << name << TRACE_SLOT;
+ MOTH_END_INSTR(LoadName)
+
+ MOTH_BEGIN_INSTR(LoadGlobalLookup)
+ d << index << TRACE_SLOT;
+ MOTH_END_INSTR(LoadGlobalLookup)
+
+ MOTH_BEGIN_INSTR(StoreNameSloppy)
+ d << name;
+ MOTH_END_INSTR(StoreNameSloppy)
+
+ MOTH_BEGIN_INSTR(StoreNameStrict)
+ d << name;
+ MOTH_END_INSTR(StoreNameStrict)
+
+ MOTH_BEGIN_INSTR(LoadElement)
+ d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT;
+ MOTH_END_INSTR(LoadElement)
+
+ MOTH_BEGIN_INSTR(StoreElement)
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"
+ << TRACE_SLOT;
+ MOTH_END_INSTR(StoreElement)
+
+ MOTH_BEGIN_INSTR(LoadProperty)
+ d << "acc[" << name << "]" << TRACE_SLOT;
+ MOTH_END_INSTR(LoadProperty)
+
+ MOTH_BEGIN_INSTR(GetLookup)
+ d << "acc(" << index << ")" << TRACE_SLOT;
+ MOTH_END_INSTR(GetLookup)
+
+ MOTH_BEGIN_INSTR(StoreProperty)
+ d << dumpRegister(base, nFormals) << "[" << name<< "]";
+ MOTH_END_INSTR(StoreProperty)
+
+ MOTH_BEGIN_INSTR(SetLookup)
+ d << dumpRegister(base, nFormals) << "(" << index << ")";
+ MOTH_END_INSTR(SetLookup)
+
+ MOTH_BEGIN_INSTR(LoadSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(LoadSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(StoreSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
+ MOTH_END_INSTR(StoreScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
+ MOTH_END_INSTR(LoadScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(StoreContextObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
+ MOTH_END_INSTR(StoreContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadContextObjectProperty)
+ d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
+ MOTH_END_INSTR(LoadContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(LoadIdObject)
+ d << dumpRegister(base, nFormals) << "[" << index << "]";
+ MOTH_END_INSTR(LoadIdObject)
+
+ MOTH_BEGIN_INSTR(Yield)
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(YieldStar)
+ MOTH_END_INSTR(YieldStar)
+
+ MOTH_BEGIN_INSTR(Resume)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(Resume)
+
+ MOTH_BEGIN_INSTR(CallValue)
+ d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallValue)
+
+ MOTH_BEGIN_INSTR(CallWithReceiver)
+ d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals)
+ << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallWithReceiver)
+
+ MOTH_BEGIN_INSTR(CallProperty)
+ d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
+ << TRACE_SLOT;
+ MOTH_END_INSTR(CallProperty)
+
+ MOTH_BEGIN_INSTR(CallPropertyLookup)
+ d << dumpRegister(base, nFormals) << "." << lookupIndex
+ << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallPropertyLookup)
+
+ MOTH_BEGIN_INSTR(CallElement)
+ d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"
+ << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallElement)
+
+ MOTH_BEGIN_INSTR(CallName)
+ d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallName)
+
+ MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
+ d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallPossiblyDirectEval)
+
+ MOTH_BEGIN_INSTR(CallGlobalLookup)
+ d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
+ MOTH_END_INSTR(CallGlobalLookup)
+
+ MOTH_BEGIN_INSTR(CallScopeObjectProperty)
+ d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
+ << TRACE_SLOT;
+ MOTH_END_INSTR(CallScopeObjectProperty)
+
+ MOTH_BEGIN_INSTR(CallContextObjectProperty)
+ d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
+ << TRACE_SLOT;
+ MOTH_END_INSTR(CallContextObjectProperty)
+
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
+ << dumpArguments(argc, argv, nFormals)
+ << TRACE_SLOT;
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(Construct)
+ d << "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);
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
+ if (offset)
+ d << ABSOLUTE_OFFSET();
+ else
+ d << "<null>";
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(UnwindToLabel)
+
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ d << name;
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
+
+ MOTH_BEGIN_INSTR(ThrowException)
+ MOTH_END_INSTR(ThrowException)
+
+ MOTH_BEGIN_INSTR(GetException)
+ MOTH_END_INSTR(HasException)
+
+ MOTH_BEGIN_INSTR(SetException)
+ MOTH_END_INSTR(SetExceptionFlag)
+
+ MOTH_BEGIN_INSTR(CreateCallContext)
+ MOTH_END_INSTR(CreateCallContext)
+
+ MOTH_BEGIN_INSTR(PushCatchContext)
+ d << index << ", " << name;
+ MOTH_END_INSTR(PushCatchContext)
+
+ MOTH_BEGIN_INSTR(PushWithContext)
+ MOTH_END_INSTR(PushWithContext)
+
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ d << index;
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ d << index;
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ MOTH_END_INSTR(PopScriptContext)
+
+ MOTH_BEGIN_INSTR(PopContext)
+ MOTH_END_INSTR(PopContext)
+
+ MOTH_BEGIN_INSTR(GetIterator)
+ d << iterator;
+ MOTH_END_INSTR(GetIterator)
+
+ MOTH_BEGIN_INSTR(IteratorNext)
+ d << dumpRegister(value, nFormals) << ", " << dumpRegister(done, nFormals);
+ MOTH_END_INSTR(IteratorNext)
+
+ MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
+ d << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals);
+ 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) << "]";
+ MOTH_END_INSTR(DeleteProperty)
+
+ MOTH_BEGIN_INSTR(DeleteName)
+ d << name;
+ MOTH_END_INSTR(DeleteName)
+
+ MOTH_BEGIN_INSTR(TypeofName)
+ d << name;
+ MOTH_END_INSTR(TypeofName)
+
+ MOTH_BEGIN_INSTR(TypeofValue)
+ MOTH_END_INSTR(TypeofValue)
+
+ MOTH_BEGIN_INSTR(DeclareVar)
+ d << isDeletable << ", " << varName;
+ MOTH_END_INSTR(DeclareVar)
+
+ MOTH_BEGIN_INSTR(DefineArray)
+ d << dumpRegister(args, nFormals) << ", " << argc;
+ MOTH_END_INSTR(DefineArray)
+
+ MOTH_BEGIN_INSTR(DefineObjectLiteral)
+ d << internalClassId
+ << ", " << argc
+ << ", " << dumpRegister(args, nFormals);
+ MOTH_END_INSTR(DefineObjectLiteral)
+
+ MOTH_BEGIN_INSTR(CreateClass)
+ d << classIndex
+ << ", " << dumpRegister(heritage, nFormals)
+ << ", " << dumpRegister(computedNames, nFormals);
+ MOTH_END_INSTR(CreateClass)
+
+ MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
+ MOTH_END_INSTR(CreateMappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ d << argIndex;
+ MOTH_END_INSTR(CreateRestParameter)
+
+ MOTH_BEGIN_INSTR(ConvertThisToObject)
+ MOTH_END_INSTR(ConvertThisToObject)
+
+ MOTH_BEGIN_INSTR(LoadSuperConstructor)
+ MOTH_END_INSTR(LoadSuperConstructor)
+
+ MOTH_BEGIN_INSTR(ToObject)
+ MOTH_END_INSTR(ToObject)
+
+ MOTH_BEGIN_INSTR(Jump)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(Jump)
+
+ MOTH_BEGIN_INSTR(JumpTrue)
+ d << ABSOLUTE_OFFSET() << TRACE_SLOT;
+ MOTH_END_INSTR(JumpTrue)
+
+ MOTH_BEGIN_INSTR(JumpFalse)
+ d << ABSOLUTE_OFFSET() << TRACE_SLOT;
+ MOTH_END_INSTR(JumpFalse)
+
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNotUndefined)
+
+ MOTH_BEGIN_INSTR(JumpNoException)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNoException)
+
+ MOTH_BEGIN_INSTR(CmpEqNull)
+ MOTH_END_INSTR(CmpEqNull)
+
+ MOTH_BEGIN_INSTR(CmpNeNull)
+ MOTH_END_INSTR(CmpNeNull)
+
+ MOTH_BEGIN_INSTR(CmpEqInt)
+ d << lhs;
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNeInt)
+ d << lhs;
+ MOTH_END_INSTR(CmpNeInt)
+
+ MOTH_BEGIN_INSTR(CmpEq)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpEq)
+
+ MOTH_BEGIN_INSTR(CmpNe)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpNe)
+
+ MOTH_BEGIN_INSTR(CmpGt)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpGt)
+
+ MOTH_BEGIN_INSTR(CmpGe)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpGe)
+
+ MOTH_BEGIN_INSTR(CmpLt)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpLt)
+
+ MOTH_BEGIN_INSTR(CmpLe)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpLe)
+
+ MOTH_BEGIN_INSTR(CmpStrictEqual)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpStrictEqual)
+
+ MOTH_BEGIN_INSTR(CmpStrictNotEqual)
+ d << dumpRegister(lhs, nFormals);
+ MOTH_END_INSTR(CmpStrictNotEqual)
+
+ MOTH_BEGIN_INSTR(UNot)
+ MOTH_END_INSTR(UNot)
+
+ MOTH_BEGIN_INSTR(UPlus)
+ MOTH_END_INSTR(UPlus)
+
+ MOTH_BEGIN_INSTR(UMinus)
+ d << TRACE_SLOT;
+ MOTH_END_INSTR(UMinus)
+
+ MOTH_BEGIN_INSTR(UCompl)
+ MOTH_END_INSTR(UCompl)
+
+ MOTH_BEGIN_INSTR(Increment)
+ d << TRACE_SLOT;
+ MOTH_END_INSTR(Increment)
+
+ MOTH_BEGIN_INSTR(Decrement)
+ d << TRACE_SLOT;
+ MOTH_END_INSTR(Decrement)
+
+ MOTH_BEGIN_INSTR(Add)
+ d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ MOTH_END_INSTR(Add)
+
+ MOTH_BEGIN_INSTR(BitAnd)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(BitAnd)
+
+ MOTH_BEGIN_INSTR(BitOr)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(BitOr)
+
+ MOTH_BEGIN_INSTR(BitXor)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(BitXor)
+
+ MOTH_BEGIN_INSTR(UShr)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(UShr)
+
+ MOTH_BEGIN_INSTR(Shr)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Shr)
+
+ MOTH_BEGIN_INSTR(Shl)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Shl)
+
+ MOTH_BEGIN_INSTR(BitAndConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(BitAndConst)
+
+ MOTH_BEGIN_INSTR(BitOrConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(BitOr)
+
+ MOTH_BEGIN_INSTR(BitXorConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(BitXor)
+
+ MOTH_BEGIN_INSTR(UShrConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(UShrConst)
+
+ MOTH_BEGIN_INSTR(ShrConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(ShrConst)
+
+ MOTH_BEGIN_INSTR(ShlConst)
+ d << "acc, " << rhs;
+ MOTH_END_INSTR(ShlConst)
+
+ MOTH_BEGIN_INSTR(Exp)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Exp)
+
+ MOTH_BEGIN_INSTR(Mul)
+ d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ MOTH_END_INSTR(Mul)
+
+ MOTH_BEGIN_INSTR(Div)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Div)
+
+ MOTH_BEGIN_INSTR(Mod)
+ d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ MOTH_END_INSTR(Mod)
+
+ MOTH_BEGIN_INSTR(Sub)
+ d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT;
+ MOTH_END_INSTR(Sub)
+
+ MOTH_BEGIN_INSTR(CmpIn)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(CmpIn)
+
+ MOTH_BEGIN_INSTR(CmpInstanceOf)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(CmpInstanceOf)
+
+ MOTH_BEGIN_INSTR(Ret)
+ MOTH_END_INSTR(Ret)
+
+ MOTH_BEGIN_INSTR(Debug)
+ MOTH_END_INSTR(Debug)
+
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ d << dumpRegister(firstReg, nFormals) << ", " << count;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
+ MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
+ MOTH_END_INSTR(ThrowOnNullOrUndefined)
+
+ MOTH_BEGIN_INSTR(GetTemplateObject)
+ d << index;
+ MOTH_END_INSTR(GetTemplateObject)
+
+ MOTH_BEGIN_INSTR(LoadQmlContext)
+ d << dumpRegister(result, nFormals);
+ MOTH_END_INSTR(LoadQmlContext)
+
+ MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
+ d << dumpRegister(result, nFormals);
+ MOTH_END_INSTR(LoadQmlImportedScripts)
+
+ MOTH_BEGIN_INSTR(TailCall)
+ d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(TailCall)
+ }
+}
+
+}
+}
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index dabda7bae8..3996143843 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -53,869 +53,575 @@
#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4runtime_p.h>
-
-#if !defined(V4_BOOTSTRAP)
-QT_REQUIRE_CONFIG(qml_interpreter);
-#endif
+#include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper
+#include <qendian.h>
QT_BEGIN_NAMESPACE
-#ifdef QT_NO_QML_DEBUGGER
-#define MOTH_DEBUG_INSTR(F)
-#else
-#define MOTH_DEBUG_INSTR(F) \
- F(Line, line) \
- F(Debug, debug)
-#endif
+#define INSTRUCTION(op, name, nargs, ...) \
+ op##_INSTRUCTION(name, nargs, __VA_ARGS__)
+
+/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */
+#define INSTR_Nop(op) INSTRUCTION(op, Nop, 0)
+#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0)
+#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0)
+#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index)
+#define INSTR_LoadZero(op) INSTRUCTION(op, LoadZero, 0)
+#define INSTR_LoadTrue(op) INSTRUCTION(op, LoadTrue, 0)
+#define INSTR_LoadFalse(op) INSTRUCTION(op, LoadFalse, 0)
+#define INSTR_LoadNull(op) INSTRUCTION(op, LoadNull, 0)
+#define INSTR_LoadUndefined(op) INSTRUCTION(op, LoadUndefined, 0)
+#define INSTR_LoadInt(op) INSTRUCTION(op, LoadInt, 1, value)
+#define INSTR_MoveConst(op) INSTRUCTION(op, MoveConst, 2, constIndex, destTemp)
+#define INSTR_LoadReg(op) INSTRUCTION(op, LoadReg, 1, reg)
+#define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg)
+#define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg)
+#define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index)
+#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot)
+#define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index)
+#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot)
+#define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index)
+#define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId)
+#define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg)
+#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value)
+#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot)
+#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot)
+#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, 2, name, traceSlot)
+#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot)
+#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired)
+#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired)
+#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_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)
+#define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property)
+#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex)
+#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex)
+#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot)
+#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot)
+#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot)
+#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot)
+#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot)
+#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot)
+#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot)
+#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot)
+#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot)
+#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot)
+#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 5, name, base, argc, argv, traceSlot)
+#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 5, name, base, argc, argv, traceSlot)
+#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot)
+#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
+#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
+#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
+#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
+#define INSTR_DeadTemporalZoneCheck(op) INSTRUCTION(op, DeadTemporalZoneCheck, 1, name)
+#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
+#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
+#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
+#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0)
+#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, index, name)
+#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 0)
+#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 1, index)
+#define INSTR_CloneBlockContext(op) INSTRUCTION(op, CloneBlockContext, 0)
+#define INSTR_PushScriptContext(op) INSTRUCTION(op, PushScriptContext, 1, index)
+#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_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)
+#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name)
+#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0)
+#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable)
+#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args)
+#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 3, internalClassId, argc, args)
+#define INSTR_CreateClass(op) INSTRUCTION(op, CreateClass, 3, classIndex, heritage, computedNames)
+#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0)
+#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0)
+#define INSTR_CreateRestParameter(op) INSTRUCTION(op, CreateRestParameter, 1, argIndex)
+#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0)
+#define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0)
+#define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0)
+#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset)
+#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset)
+#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset)
+#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
+#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
+#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
+#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
+#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs)
+#define INSTR_CmpNeInt(op) INSTRUCTION(op, CmpNeInt, 1, lhs)
+#define INSTR_CmpEq(op) INSTRUCTION(op, CmpEq, 1, lhs)
+#define INSTR_CmpNe(op) INSTRUCTION(op, CmpNe, 1, lhs)
+#define INSTR_CmpGt(op) INSTRUCTION(op, CmpGt, 1, lhs)
+#define INSTR_CmpGe(op) INSTRUCTION(op, CmpGe, 1, lhs)
+#define INSTR_CmpLt(op) INSTRUCTION(op, CmpLt, 1, lhs)
+#define INSTR_CmpLe(op) INSTRUCTION(op, CmpLe, 1, lhs)
+#define INSTR_CmpStrictEqual(op) INSTRUCTION(op, CmpStrictEqual, 1, lhs)
+#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
+#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
+#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
+#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
+#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
+#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot)
+#define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0)
+#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot)
+#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot)
+#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot)
+#define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs)
+#define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs)
+#define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs)
+#define INSTR_UShr(op) INSTRUCTION(op, UShr, 1, lhs)
+#define INSTR_Shr(op) INSTRUCTION(op, Shr, 1, lhs)
+#define INSTR_Shl(op) INSTRUCTION(op, Shl, 1, lhs)
+#define INSTR_BitAndConst(op) INSTRUCTION(op, BitAndConst, 1, rhs)
+#define INSTR_BitOrConst(op) INSTRUCTION(op, BitOrConst, 1, rhs)
+#define INSTR_BitXorConst(op) INSTRUCTION(op, BitXorConst, 1, rhs)
+#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs)
+#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs)
+#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs)
+#define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs)
+#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot)
+#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
+#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot)
+#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot)
+#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
+#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)
+#define INSTR_GetTemplateObject(op) INSTRUCTION(op, GetTemplateObject, 1, index)
+#define INSTR_TailCall(op) INSTRUCTION(op, TailCall, 4, func, thisObject, argc, argv)
+
+#define FOR_EACH_MOTH_INSTR_ALL(F) \
+ F(Nop) \
+ FOR_EACH_MOTH_INSTR(F)
#define FOR_EACH_MOTH_INSTR(F) \
- F(Ret, ret) \
- MOTH_DEBUG_INSTR(F) \
- F(LoadRuntimeString, loadRuntimeString) \
- F(LoadRegExp, loadRegExp) \
- F(LoadClosure, loadClosure) \
- F(Move, move) \
- F(MoveConst, moveConst) \
- F(SwapTemps, swapTemps) \
- F(LoadName, loadName) \
- F(GetGlobalLookup, getGlobalLookup) \
- F(StoreName, storeName) \
- F(LoadElement, loadElement) \
- F(LoadElementLookup, loadElementLookup) \
- F(StoreElement, storeElement) \
- F(StoreElementLookup, storeElementLookup) \
- F(LoadProperty, loadProperty) \
- F(GetLookup, getLookup) \
- F(StoreProperty, storeProperty) \
- F(SetLookup, setLookup) \
- F(StoreQObjectProperty, storeQObjectProperty) \
- F(LoadQObjectProperty, loadQObjectProperty) \
- F(StoreScopeObjectProperty, storeScopeObjectProperty) \
- F(StoreContextObjectProperty, storeContextObjectProperty) \
- F(LoadScopeObjectProperty, loadScopeObjectProperty) \
- F(LoadContextObjectProperty, loadContextObjectProperty) \
- F(LoadIdObject, loadIdObject) \
- F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \
- F(LoadSingletonQObjectProperty, loadQObjectProperty) \
- F(Push, push) \
- F(CallValue, callValue) \
- F(CallProperty, callProperty) \
- F(CallPropertyLookup, callPropertyLookup) \
- F(CallScopeObjectProperty, callScopeObjectProperty) \
- F(CallContextObjectProperty, callContextObjectProperty) \
- F(CallElement, callElement) \
- F(CallActivationProperty, callActivationProperty) \
- F(CallGlobalLookup, callGlobalLookup) \
- F(SetExceptionHandler, setExceptionHandler) \
- F(CallBuiltinThrow, callBuiltinThrow) \
- F(CallBuiltinUnwindException, callBuiltinUnwindException) \
- F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \
- F(CallBuiltinPushScope, callBuiltinPushScope) \
- F(CallBuiltinPopScope, callBuiltinPopScope) \
- F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \
- F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \
- F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \
- F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \
- F(CallBuiltinDeleteName, callBuiltinDeleteName) \
- F(CallBuiltinTypeofScopeObjectProperty, callBuiltinTypeofScopeObjectProperty) \
- F(CallBuiltinTypeofContextObjectProperty, callBuiltinTypeofContextObjectProperty) \
- F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \
- F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \
- F(CallBuiltinTypeofName, callBuiltinTypeofName) \
- F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \
- F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \
- F(CallBuiltinDefineArray, callBuiltinDefineArray) \
- F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \
- F(CallBuiltinSetupArgumentsObject, callBuiltinSetupArgumentsObject) \
- F(CallBuiltinConvertThisToObject, callBuiltinConvertThisToObject) \
- F(CreateValue, createValue) \
- F(CreateProperty, createProperty) \
- F(ConstructPropertyLookup, constructPropertyLookup) \
- F(CreateActivationProperty, createActivationProperty) \
- F(ConstructGlobalLookup, constructGlobalLookup) \
- F(Jump, jump) \
- F(JumpEq, jumpEq) \
- F(JumpNe, jumpNe) \
- F(UNot, unot) \
- F(UNotBool, unotBool) \
- F(UPlus, uplus) \
- F(UMinus, uminus) \
- F(UCompl, ucompl) \
- F(UComplInt, ucomplInt) \
- F(Increment, increment) \
- F(Decrement, decrement) \
- F(Binop, binop) \
- F(Add, add) \
- F(BitAnd, bitAnd) \
- F(BitOr, bitOr) \
- F(BitXor, bitXor) \
- F(Shr, shr) \
- F(Shl, shl) \
- F(BitAndConst, bitAndConst) \
- F(BitOrConst, bitOrConst) \
- F(BitXorConst, bitXorConst) \
- F(ShrConst, shrConst) \
- F(ShlConst, shlConst) \
- F(Mul, mul) \
- F(Sub, sub) \
- F(BinopContext, binopContext) \
- F(LoadThis, loadThis) \
- F(LoadQmlContext, loadQmlContext) \
- F(LoadQmlImportedScripts, loadQmlImportedScripts) \
- F(LoadQmlSingleton, loadQmlSingleton)
-
-#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200)
-# define MOTH_THREADED_INTERPRETER
+ F(Ret) \
+ F(LoadConst) \
+ F(LoadZero) \
+ F(LoadTrue) \
+ F(LoadFalse) \
+ F(LoadNull) \
+ F(LoadUndefined) \
+ F(LoadInt) \
+ F(LoadRuntimeString) \
+ F(MoveConst) \
+ F(LoadReg) \
+ F(StoreReg) \
+ F(MoveReg) \
+ F(LoadImport) \
+ F(LoadLocal) \
+ F(StoreLocal) \
+ F(LoadScopedLocal) \
+ F(StoreScopedLocal) \
+ F(MoveRegExp) \
+ F(LoadClosure) \
+ F(LoadName) \
+ F(LoadGlobalLookup) \
+ F(StoreNameSloppy) \
+ F(StoreNameStrict) \
+ F(LoadElement) \
+ F(StoreElement) \
+ F(LoadProperty) \
+ F(GetLookup) \
+ F(StoreProperty) \
+ F(SetLookup) \
+ F(LoadSuperProperty) \
+ F(StoreSuperProperty) \
+ F(StoreScopeObjectProperty) \
+ F(StoreContextObjectProperty) \
+ F(LoadScopeObjectProperty) \
+ F(LoadContextObjectProperty) \
+ F(LoadIdObject) \
+ F(ConvertThisToObject) \
+ F(ToObject) \
+ F(Jump) \
+ F(JumpTrue) \
+ F(JumpFalse) \
+ F(JumpNoException) \
+ F(JumpNotUndefined) \
+ F(CmpEqNull) \
+ F(CmpNeNull) \
+ F(CmpEqInt) \
+ F(CmpNeInt) \
+ F(CmpEq) \
+ F(CmpNe) \
+ F(CmpGt) \
+ F(CmpGe) \
+ F(CmpLt) \
+ F(CmpLe) \
+ F(CmpStrictEqual) \
+ F(CmpStrictNotEqual) \
+ F(CmpIn) \
+ F(CmpInstanceOf) \
+ F(UNot) \
+ F(UPlus) \
+ F(UMinus) \
+ F(UCompl) \
+ F(Increment) \
+ F(Decrement) \
+ F(Add) \
+ F(BitAnd) \
+ F(BitOr) \
+ F(BitXor) \
+ F(UShr) \
+ F(Shr) \
+ F(Shl) \
+ F(BitAndConst) \
+ F(BitOrConst) \
+ F(BitXorConst) \
+ F(UShrConst) \
+ F(ShrConst) \
+ F(ShlConst) \
+ F(Exp) \
+ F(Mul) \
+ F(Div) \
+ F(Mod) \
+ F(Sub) \
+ F(CallValue) \
+ F(CallWithReceiver) \
+ F(CallProperty) \
+ F(CallPropertyLookup) \
+ F(CallElement) \
+ F(CallName) \
+ F(CallPossiblyDirectEval) \
+ F(CallGlobalLookup) \
+ F(CallScopeObjectProperty) \
+ F(CallContextObjectProperty) \
+ F(CallWithSpread) \
+ F(Construct) \
+ F(ConstructWithSpread) \
+ F(SetUnwindHandler) \
+ F(UnwindDispatch) \
+ F(UnwindToLabel) \
+ F(DeadTemporalZoneCheck) \
+ F(ThrowException) \
+ F(GetException) \
+ F(SetException) \
+ F(CreateCallContext) \
+ F(PushCatchContext) \
+ F(PushWithContext) \
+ F(PushBlockContext) \
+ F(CloneBlockContext) \
+ F(PopContext) \
+ F(GetIterator) \
+ F(IteratorNext) \
+ F(IteratorClose) \
+ F(DestructureRestElement) \
+ F(DeleteProperty) \
+ F(DeleteName) \
+ F(TypeofName) \
+ F(TypeofValue) \
+ F(DeclareVar) \
+ F(DefineArray) \
+ F(DefineObjectLiteral) \
+ F(CreateMappedArgumentsObject) \
+ F(CreateUnmappedArgumentsObject) \
+ F(CreateRestParameter) \
+ F(LoadQmlContext) \
+ F(LoadQmlImportedScripts) \
+ F(Yield) \
+ F(YieldStar) \
+ F(Resume) \
+ F(IteratorNextForYieldStar) \
+ F(CreateClass) \
+ F(LoadSuperConstructor) \
+ F(PushScriptContext) \
+ F(PopScriptContext) \
+ F(InitializeBlockDeadTemporalZone) \
+ F(ThrowOnNullOrUndefined) \
+ F(GetTemplateObject) \
+ F(TailCall) \
+ F(Debug) \
+
+#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)
+// 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
+# define MOTH_COMPUTED_GOTO
+#endif
#endif
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
-#define MOTH_INSTR_HEADER quint32 instructionType;
+#define MOTH_INSTR_ENUM(I) I, I##_Wide,
+#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I))
+
+#define MOTH_EXPAND_FOR_MSVC(x) x
+#define MOTH_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define MOTH_DEFINE_ARGS0()
+#define MOTH_DEFINE_ARGS1(arg) \
+ int arg;
+#define MOTH_DEFINE_ARGS2(arg1, arg2) \
+ int arg1; \
+ int arg2;
+#define MOTH_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1; \
+ int arg2; \
+ int arg3;
+#define MOTH_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1; \
+ int arg2; \
+ int arg3; \
+ int arg4;
+#define MOTH_DEFINE_ARGS5(arg1, arg2, arg3, arg4, arg5) \
+ int arg1; \
+ int arg2; \
+ int arg3; \
+ int arg4; \
+ int arg5;
+
+#define MOTH_COLLECT_ENUMS(instr) \
+ INSTR_##instr(MOTH_GET_ENUM)
+#define MOTH_GET_ENUM_INSTRUCTION(name, ...) \
+ name,
+
+#define MOTH_EMIT_STRUCTS(instr) \
+ INSTR_##instr(MOTH_EMIT_STRUCT)
+#define MOTH_EMIT_STRUCT_INSTRUCTION(name, nargs, ...) \
+ struct instr_##name { \
+ MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ };
+
+#define MOTH_EMIT_INSTR_MEMBERS(instr) \
+ INSTR_##instr(MOTH_EMIT_INSTR_MEMBER)
+#define MOTH_EMIT_INSTR_MEMBER_INSTRUCTION(name, nargs, ...) \
+ instr_##name name;
+
+#define MOTH_COLLECT_NARGS(instr) \
+ INSTR_##instr(MOTH_COLLECT_ARG_COUNT)
+#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \
+ nargs, nargs,
+
+#define MOTH_DECODE_ARG(arg, type, nargs, offset) \
+ arg = qFromLittleEndian<type>(qFromUnaligned<type>(reinterpret_cast<const type *>(code) - nargs + offset));
+#define MOTH_ADJUST_CODE(type, nargs) \
+ code += static_cast<quintptr>(nargs*sizeof(type) + 1)
+
+#define MOTH_DECODE_INSTRUCTION(name, nargs, ...) \
+ MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ op_int_##name: \
+ MOTH_ADJUST_CODE(int, nargs); \
+ MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \
+ goto op_main_##name; \
+ op_byte_##name: \
+ MOTH_ADJUST_CODE(qint8, nargs); \
+ MOTH_DECODE_ARGS(name, qint8, nargs, __VA_ARGS__) \
+ op_main_##name: \
+ ; \
+
+#define MOTH_DECODE_WITH_BASE_INSTRUCTION(name, nargs, ...) \
+ MOTH_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ const char *base_ptr; \
+ op_int_##name: \
+ base_ptr = code; \
+ MOTH_ADJUST_CODE(int, nargs); \
+ MOTH_DECODE_ARGS(name, int, nargs, __VA_ARGS__) \
+ goto op_main_##name; \
+ op_byte_##name: \
+ base_ptr = code; \
+ MOTH_ADJUST_CODE(qint8, nargs); \
+ MOTH_DECODE_ARGS(name, qint8, nargs, __VA_ARGS__) \
+ op_main_##name: \
+ ; \
+
+#define MOTH_DECODE_ARGS(name, type, nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_DECODE_ARGS##nargs(name, type, nargs, __VA_ARGS__))
+
+#define MOTH_DECODE_ARGS0(name, type, nargs, dummy)
+#define MOTH_DECODE_ARGS1(name, type, nargs, arg) \
+ MOTH_DECODE_ARG(arg, type, nargs, 0);
+#define MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2) \
+ MOTH_DECODE_ARGS1(name, type, nargs, arg1); \
+ MOTH_DECODE_ARG(arg2, type, nargs, 1);
+#define MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3) \
+ MOTH_DECODE_ARGS2(name, type, nargs, arg1, arg2); \
+ MOTH_DECODE_ARG(arg3, type, nargs, 2);
+#define MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4) \
+ MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3); \
+ MOTH_DECODE_ARG(arg4, type, nargs, 3);
+#define MOTH_DECODE_ARGS5(name, type, nargs, arg1, arg2, arg3, arg4, arg5) \
+ MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4); \
+ MOTH_DECODE_ARG(arg5, type, nargs, 4);
+
+#ifdef MOTH_COMPUTED_GOTO
+/* collect jump labels */
+#define COLLECT_LABELS(instr) \
+ INSTR_##instr(GET_LABEL) \
+ INSTR_##instr(GET_LABEL_WIDE)
+#define GET_LABEL_INSTRUCTION(name, ...) \
+ &&op_byte_##name,
+#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \
+ &&op_int_##name,
+
+#define MOTH_JUMP_TABLE \
+ static const void *jumpTable[] = { \
+ FOR_EACH_MOTH_INSTR_ALL(COLLECT_LABELS) \
+ };
+
+#define MOTH_DISPATCH_SINGLE() \
+ goto *jumpTable[*reinterpret_cast<const uchar *>(code)];
+
+#define MOTH_DISPATCH() \
+ MOTH_DISPATCH_SINGLE() \
+ op_byte_Nop: \
+ ++code; \
+ MOTH_DISPATCH_SINGLE() \
+ op_int_Nop: /* wide prefix */ \
+ ++code; \
+ goto *jumpTable[0x100 | *reinterpret_cast<const uchar *>(code)];
+#else
+#define MOTH_JUMP_TABLE
+
+#define MOTH_INSTR_CASE_AND_JUMP(instr) \
+ INSTR_##instr(GET_CASE_AND_JUMP) \
+ INSTR_##instr(GET_CASE_AND_JUMP_WIDE)
+#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
+ case Instr::Type::name: goto op_byte_##name;
+#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \
+ case Instr::Type::name##_Wide: goto op_int_##name;
+
+#define MOTH_DISPATCH() \
+ Instr::Type type = Instr::Type(static_cast<uchar>(*code)); \
+ dispatch: \
+ switch (type) { \
+ case Instr::Type::Nop: \
+ ++code; \
+ type = Instr::Type(static_cast<uchar>(*code)); \
+ goto dispatch; \
+ case Instr::Type::Nop_Wide: /* wide prefix */ \
+ ++code; \
+ type = Instr::Type(0x100 | static_cast<uchar>(*code)); \
+ goto dispatch; \
+ FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \
+ }
+#endif
-#define MOTH_INSTR_ENUM(I, FMT) I,
-#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK)
+namespace QV4 {
+namespace CompiledData {
+struct CodeOffsetToLine;
+}
-namespace QV4 {
namespace Moth {
- // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
-
-struct Param {
- // Params are looked up as follows:
- // Constant: 0
- // Temp: 1
- // Argument: 2
- // Local: 3
- // Arg(outer): 4
- // Local(outer): 5
- // ...
- unsigned scope : 12;
- unsigned index : 20;
-
- bool isConstant() const { return !scope; }
- bool isArgument() const { return scope >= 2 && !(scope &1); }
- bool isLocal() const { return scope == 3; }
- bool isTemp() const { return scope == 1; }
- bool isScopedLocal() const { return scope >= 3 && (scope & 1); }
-
- static Param createConstant(int index)
- {
- Param p;
- p.scope = 0;
- p.index = index;
- return p;
- }
+class StackSlot {
+ int index;
- static Param createArgument(unsigned idx, uint scope)
- {
- Param p;
- p.scope = 2 + 2*scope;
- p.index = idx;
- return p;
+public:
+ static StackSlot createRegister(int index) {
+ Q_ASSERT(index >= 0);
+ StackSlot t;
+ t.index = index;
+ return t;
}
- static Param createLocal(unsigned idx)
- {
- Param p;
- p.scope = 3;
- p.index = idx;
- return p;
- }
-
- static Param createTemp(unsigned idx)
- {
- Param p;
- p.scope = 1;
- p.index = idx;
- return p;
- }
+ int stackSlot() const { return index; }
+ operator int() const { return index; }
+};
- static Param createScopedLocal(unsigned idx, uint scope)
- {
- Param p;
- p.scope = 3 + 2*scope;
- p.index = idx;
- return p;
- }
+inline bool operator==(const StackSlot &l, const StackSlot &r) { return l.stackSlot() == r.stackSlot(); }
+inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackSlot() != r.stackSlot(); }
- inline bool operator==(const Param &other) const
- { return scope == other.scope && index == other.index; }
+// When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
- inline bool operator!=(const Param &other) const
- { return !(*this == other); }
-};
+void dumpConstantTable(const Value *constants, uint count);
+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);
+}
union Instr
{
- enum Type {
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
- LastInstruction
- };
+ enum class Type {
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_ENUM)
+ };
+
+ static Type wideInstructionType(Type t) { return Type(int(t) | 1); }
+ static Type narrowInstructionType(Type t) { return Type(int(t) & ~1); }
+ static bool isWide(Type t) { return int(t) & 1; }
+ static bool isNarrow(Type t) { return !(int(t) & 1); }
+ static int encodedLength(Type t) { return int(t) >= 256 ? 2 : 1; }
+
+ static Type unpack(const uchar *c) { if (c[0] == 0x1) return Type(0x100 + c[1]); return Type(c[0]); }
+ static uchar *pack(uchar *c, Type t) {
+ if (uint(t) >= 256) {
+ c[0] = 0x1;
+ c[1] = uint(t) &0xff;
+ return c + 2;
+ }
+ c[0] = uchar(uint(t));
+ return c + 1;
+ }
- struct instr_common {
- MOTH_INSTR_HEADER
- };
- struct instr_ret {
- MOTH_INSTR_HEADER
- Param result;
- };
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_STRUCTS)
-#ifndef QT_NO_QML_DEBUGGING
- struct instr_line {
- MOTH_INSTR_HEADER
- qint32 lineNumber;
- };
- struct instr_debug {
- MOTH_INSTR_HEADER
- qint32 lineNumber;
- };
-#endif // QT_NO_QML_DEBUGGING
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_INSTR_MEMBERS)
- struct instr_loadRuntimeString {
- MOTH_INSTR_HEADER
- int stringId;
- Param result;
- };
- struct instr_loadRegExp {
- MOTH_INSTR_HEADER
- int regExpId;
- Param result;
- };
- struct instr_move {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_moveConst {
- MOTH_INSTR_HEADER
- QV4::ReturnedValue source;
- Param result;
- };
- struct instr_swapTemps {
- MOTH_INSTR_HEADER
- Param left;
- Param right;
- };
- struct instr_loadClosure {
- MOTH_INSTR_HEADER
- int value;
- Param result;
- };
- struct instr_loadName {
- MOTH_INSTR_HEADER
- int name;
- Param result;
- };
- struct instr_getGlobalLookup {
- MOTH_INSTR_HEADER
- int index;
- Param result;
- };
- struct instr_storeName {
- MOTH_INSTR_HEADER
- int name;
- Param source;
- };
- struct instr_loadProperty {
- MOTH_INSTR_HEADER
- int name;
- Param base;
- Param result;
- };
- struct instr_getLookup {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_loadScopeObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param base;
- Param result;
- bool captureRequired;
- };
- struct instr_loadContextObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param base;
- Param result;
- bool captureRequired;
- };
- struct instr_loadIdObject {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_loadQObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param base;
- Param result;
- bool captureRequired;
- };
- struct instr_loadAttachedQObjectProperty {
- MOTH_INSTR_HEADER
- int propertyIndex;
- Param result;
- int attachedPropertiesId;
- };
- struct instr_storeProperty {
- MOTH_INSTR_HEADER
- int name;
- Param base;
- Param source;
- };
- struct instr_setLookup {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param source;
- };
- struct instr_storeScopeObjectProperty {
- MOTH_INSTR_HEADER
- Param base;
- int propertyIndex;
- Param source;
- };
- struct instr_storeContextObjectProperty {
- MOTH_INSTR_HEADER
- Param base;
- int propertyIndex;
- Param source;
- };
- struct instr_storeQObjectProperty {
- MOTH_INSTR_HEADER
- Param base;
- int propertyIndex;
- Param source;
- };
- struct instr_loadElement {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param result;
- };
- struct instr_loadElementLookup {
- MOTH_INSTR_HEADER
- uint lookup;
- Param base;
- Param index;
- Param result;
- };
- struct instr_storeElement {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param source;
- };
- struct instr_storeElementLookup {
- MOTH_INSTR_HEADER
- uint lookup;
- Param base;
- Param index;
- Param source;
- };
- struct instr_push {
- MOTH_INSTR_HEADER
- quint32 value;
- };
- struct instr_callValue {
- MOTH_INSTR_HEADER
- quint32 argc;
- quint32 callData;
- Param dest;
- Param result;
- };
- struct instr_callProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callPropertyLookup {
- MOTH_INSTR_HEADER
- int lookupIndex;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callScopeObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callContextObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_callElement {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_callActivationProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_callGlobalLookup {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_setExceptionHandler {
- MOTH_INSTR_HEADER
- qptrdiff offset;
- };
- struct instr_callBuiltinThrow {
- MOTH_INSTR_HEADER
- Param arg;
- };
- struct instr_callBuiltinUnwindException {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_callBuiltinPushCatchScope {
- MOTH_INSTR_HEADER
- int name;
- };
- struct instr_callBuiltinPushScope {
- MOTH_INSTR_HEADER
- Param arg;
- };
- struct instr_callBuiltinPopScope {
- MOTH_INSTR_HEADER
- };
- struct instr_callBuiltinForeachIteratorObject {
- MOTH_INSTR_HEADER
- Param arg;
- Param result;
- };
- struct instr_callBuiltinForeachNextPropertyName {
- MOTH_INSTR_HEADER
- Param arg;
- Param result;
- };
- struct instr_callBuiltinDeleteMember {
- MOTH_INSTR_HEADER
- int member;
- Param base;
- Param result;
- };
- struct instr_callBuiltinDeleteSubscript {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param result;
- };
- struct instr_callBuiltinDeleteName {
- MOTH_INSTR_HEADER
- int name;
- Param result;
- };
- struct instr_callBuiltinTypeofScopeObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_callBuiltinTypeofContextObjectProperty {
- MOTH_INSTR_HEADER
- int index;
- Param base;
- Param result;
- };
- struct instr_callBuiltinTypeofMember {
- MOTH_INSTR_HEADER
- int member;
- Param base;
- Param result;
- };
- struct instr_callBuiltinTypeofSubscript {
- MOTH_INSTR_HEADER
- Param base;
- Param index;
- Param result;
- };
- struct instr_callBuiltinTypeofName {
- MOTH_INSTR_HEADER
- int name;
- Param result;
- };
- struct instr_callBuiltinTypeofValue {
- MOTH_INSTR_HEADER
- Param value;
- Param result;
- };
- struct instr_callBuiltinDeclareVar {
- MOTH_INSTR_HEADER
- int varName;
- bool isDeletable;
- };
- struct instr_callBuiltinDefineArray {
- MOTH_INSTR_HEADER
- quint32 argc;
- quint32 args;
- Param result;
- };
- struct instr_callBuiltinDefineObjectLiteral {
- MOTH_INSTR_HEADER
- int internalClassId;
- uint arrayValueCount;
- uint arrayGetterSetterCountAndFlags; // 30 bits for count, 1 bit for needsSparseArray boolean
- quint32 args;
- Param result;
- };
- struct instr_callBuiltinSetupArgumentsObject {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_callBuiltinConvertThisToObject {
- MOTH_INSTR_HEADER
- };
- struct instr_createValue {
- MOTH_INSTR_HEADER
- quint32 argc;
- quint32 callData;
- Param func;
- Param result;
- };
- struct instr_createProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_constructPropertyLookup {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param base;
- Param result;
- };
- struct instr_createActivationProperty {
- MOTH_INSTR_HEADER
- int name;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_constructGlobalLookup {
- MOTH_INSTR_HEADER
- int index;
- quint32 argc;
- quint32 callData;
- Param result;
- };
- struct instr_jump {
- MOTH_INSTR_HEADER
- ptrdiff_t offset;
- };
- struct instr_jumpEq {
- MOTH_INSTR_HEADER
- ptrdiff_t offset;
- Param condition;
- };
- struct instr_jumpNe {
- MOTH_INSTR_HEADER
- ptrdiff_t offset;
- Param condition;
- };
- struct instr_unot {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_unotBool {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_uplus {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_uminus {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_ucompl {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_ucomplInt {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_increment {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_decrement {
- MOTH_INSTR_HEADER
- Param source;
- Param result;
- };
- struct instr_binop {
- MOTH_INSTR_HEADER
- int alu; // QV4::Runtime::RuntimeMethods enum value
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_add {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitAnd {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitOr {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitXor {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_shr {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_shl {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_bitAndConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_bitOrConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_bitXorConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_shrConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_shlConst {
- MOTH_INSTR_HEADER
- Param lhs;
- int rhs;
- Param result;
- };
- struct instr_mul {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_sub {
- MOTH_INSTR_HEADER
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_binopContext {
- MOTH_INSTR_HEADER
- uint alu; // offset inside the runtime methods
- Param lhs;
- Param rhs;
- Param result;
- };
- struct instr_loadThis {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_loadQmlContext {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_loadQmlImportedScripts {
- MOTH_INSTR_HEADER
- Param result;
- };
- struct instr_loadQmlSingleton {
- MOTH_INSTR_HEADER
- Param result;
- int name;
- };
+ int argumentsAsInts[4];
+};
- instr_common common;
- instr_ret ret;
- instr_line line;
- instr_debug debug;
- instr_loadRuntimeString loadRuntimeString;
- instr_loadRegExp loadRegExp;
- instr_move move;
- instr_moveConst moveConst;
- instr_swapTemps swapTemps;
- instr_loadClosure loadClosure;
- instr_loadName loadName;
- instr_getGlobalLookup getGlobalLookup;
- instr_storeName storeName;
- instr_loadElement loadElement;
- instr_loadElementLookup loadElementLookup;
- instr_storeElement storeElement;
- instr_storeElementLookup storeElementLookup;
- instr_loadProperty loadProperty;
- instr_getLookup getLookup;
- instr_loadScopeObjectProperty loadScopeObjectProperty;
- instr_loadContextObjectProperty loadContextObjectProperty;
- instr_loadIdObject loadIdObject;
- instr_loadQObjectProperty loadQObjectProperty;
- instr_loadAttachedQObjectProperty loadAttachedQObjectProperty;
- instr_storeProperty storeProperty;
- instr_setLookup setLookup;
- instr_storeScopeObjectProperty storeScopeObjectProperty;
- instr_storeContextObjectProperty storeContextObjectProperty;
- instr_storeQObjectProperty storeQObjectProperty;
- instr_push push;
- instr_callValue callValue;
- instr_callProperty callProperty;
- instr_callPropertyLookup callPropertyLookup;
- instr_callScopeObjectProperty callScopeObjectProperty;
- instr_callContextObjectProperty callContextObjectProperty;
- instr_callElement callElement;
- instr_callActivationProperty callActivationProperty;
- instr_callGlobalLookup callGlobalLookup;
- instr_callBuiltinThrow callBuiltinThrow;
- instr_setExceptionHandler setExceptionHandler;
- instr_callBuiltinUnwindException callBuiltinUnwindException;
- instr_callBuiltinPushCatchScope callBuiltinPushCatchScope;
- instr_callBuiltinPushScope callBuiltinPushScope;
- instr_callBuiltinPopScope callBuiltinPopScope;
- instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject;
- instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName;
- instr_callBuiltinDeleteMember callBuiltinDeleteMember;
- instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript;
- instr_callBuiltinDeleteName callBuiltinDeleteName;
- instr_callBuiltinTypeofScopeObjectProperty callBuiltinTypeofScopeObjectProperty;
- instr_callBuiltinTypeofContextObjectProperty callBuiltinTypeofContextObjectProperty;
- instr_callBuiltinTypeofMember callBuiltinTypeofMember;
- instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript;
- instr_callBuiltinTypeofName callBuiltinTypeofName;
- instr_callBuiltinTypeofValue callBuiltinTypeofValue;
- instr_callBuiltinDeclareVar callBuiltinDeclareVar;
- instr_callBuiltinDefineArray callBuiltinDefineArray;
- instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral;
- instr_callBuiltinSetupArgumentsObject callBuiltinSetupArgumentsObject;
- instr_callBuiltinConvertThisToObject callBuiltinConvertThisToObject;
- instr_createValue createValue;
- instr_createProperty createProperty;
- instr_constructPropertyLookup constructPropertyLookup;
- instr_createActivationProperty createActivationProperty;
- instr_constructGlobalLookup constructGlobalLookup;
- instr_jump jump;
- instr_jumpEq jumpEq;
- instr_jumpNe jumpNe;
- instr_unot unot;
- instr_unotBool unotBool;
- instr_uplus uplus;
- instr_uminus uminus;
- instr_ucompl ucompl;
- instr_ucomplInt ucomplInt;
- instr_increment increment;
- instr_decrement decrement;
- instr_binop binop;
- instr_add add;
- instr_bitAnd bitAnd;
- instr_bitOr bitOr;
- instr_bitXor bitXor;
- instr_shr shr;
- instr_shl shl;
- instr_bitAndConst bitAndConst;
- instr_bitOrConst bitOrConst;
- instr_bitXorConst bitXorConst;
- instr_shrConst shrConst;
- instr_shlConst shlConst;
- instr_mul mul;
- instr_sub sub;
- instr_binopContext binopContext;
- instr_loadThis loadThis;
- instr_loadQmlContext loadQmlContext;
- instr_loadQmlImportedScripts loadQmlImportedScripts;
- instr_loadQmlSingleton loadQmlSingleton;
-
- static int size(Type type);
+struct InstrInfo
+{
+ static const int argumentCount[];
+ static int size(Instr::Type type);
};
template<int N>
struct InstrMeta {
};
-#define MOTH_INSTR_META_TEMPLATE(I, FMT) \
- template<> struct InstrMeta<(int)Instr::I> { \
- enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \
- typedef Instr::instr_##FMT DataType; \
- static const DataType &data(const Instr &instr) { return instr.FMT; } \
- static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \
- static void setDataNoCommon(Instr &instr, const DataType &v) \
- { memcpy(reinterpret_cast<char *>(&instr.FMT) + sizeof(Instr::instr_common), \
- reinterpret_cast<const char *>(&v) + sizeof(Instr::instr_common), \
- Size - sizeof(Instr::instr_common)); } \
- };
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wuninitialized")
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
+#define MOTH_INSTR_META_TEMPLATE(I) \
+ template<> struct InstrMeta<int(Instr::Type::I)> { \
+ enum { Size = MOTH_INSTR_SIZE(I) }; \
+ typedef Instr::instr_##I DataType; \
+ static const DataType &data(const Instr &instr) { return instr.I; } \
+ static void setData(Instr &instr, const DataType &v) \
+ { memcpy(reinterpret_cast<char *>(&instr.I), \
+ reinterpret_cast<const char *>(&v), \
+ Size); } \
+ };
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
+QT_WARNING_POP
template<int InstrType>
class InstrData : public InstrMeta<InstrType>::DataType
{
};
+struct Instruction {
+#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I;
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_DATA_TYPEDEF)
+#undef MOTH_INSTR_DATA_TYPEDEF
+private:
+ Instruction();
+};
+
} // namespace Moth
} // namespace QV4
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
deleted file mode 100644
index fb805dce02..0000000000
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ /dev/null
@@ -1,1528 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4isel_util_p.h"
-#include "qv4isel_moth_p.h"
-#include "qv4ssa_p.h"
-#include <private/qv4compileddata_p.h>
-#include <wtf/MathExtras.h>
-
-#if !defined(V4_BOOTSTRAP)
-#include "qv4vme_moth_p.h"
-#include <private/qv4function_p.h>
-#endif
-
-#undef USE_TYPE_INFO
-
-using namespace QV4;
-using namespace QV4::Moth;
-
-namespace {
-
-inline QV4::Runtime::RuntimeMethods aluOpFunction(IR::AluOp op)
-{
- switch (op) {
- case IR::OpInvalid:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpIfTrue:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpNot:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpUMinus:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpUPlus:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpCompl:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpBitAnd:
- return QV4::Runtime::bitAnd;
- case IR::OpBitOr:
- return QV4::Runtime::bitOr;
- case IR::OpBitXor:
- return QV4::Runtime::bitXor;
- case IR::OpAdd:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpSub:
- return QV4::Runtime::sub;
- case IR::OpMul:
- return QV4::Runtime::mul;
- case IR::OpDiv:
- return QV4::Runtime::div;
- case IR::OpMod:
- return QV4::Runtime::mod;
- case IR::OpLShift:
- return QV4::Runtime::shl;
- case IR::OpRShift:
- return QV4::Runtime::shr;
- case IR::OpURShift:
- return QV4::Runtime::ushr;
- case IR::OpGt:
- return QV4::Runtime::greaterThan;
- case IR::OpLt:
- return QV4::Runtime::lessThan;
- case IR::OpGe:
- return QV4::Runtime::greaterEqual;
- case IR::OpLe:
- return QV4::Runtime::lessEqual;
- case IR::OpEqual:
- return QV4::Runtime::equal;
- case IR::OpNotEqual:
- return QV4::Runtime::notEqual;
- case IR::OpStrictEqual:
- return QV4::Runtime::strictEqual;
- case IR::OpStrictNotEqual:
- return QV4::Runtime::strictNotEqual;
- case IR::OpInstanceof:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpIn:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpAnd:
- return QV4::Runtime::InvalidRuntimeMethod;
- case IR::OpOr:
- return QV4::Runtime::InvalidRuntimeMethod;
- default:
- Q_ASSERT(!"Unknown AluOp");
- return QV4::Runtime::InvalidRuntimeMethod;
- }
-};
-
-inline bool isNumberType(IR::Expr *e)
-{
- switch (e->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- case IR::DoubleType:
- return true;
- default:
- return false;
- }
-}
-
-inline bool isIntegerType(IR::Expr *e)
-{
- switch (e->type) {
- case IR::SInt32Type:
- case IR::UInt32Type:
- return true;
- default:
- return false;
- }
-}
-
-inline bool isBoolType(IR::Expr *e)
-{
- return (e->type == IR::BoolType);
-}
-
-} // anonymous namespace
-
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
- , qmlEngine(qmlEngine)
- , _block(0)
- , _codeStart(0)
- , _codeNext(0)
- , _codeEnd(0)
- , _currentStatement(0)
- , compilationUnit(new CompilationUnit)
-{
- setUseTypeInference(false);
-}
-
-InstructionSelection::~InstructionSelection()
-{
-}
-
-void InstructionSelection::run(int functionIndex)
-{
- IR::Function *function = irModule->functions[functionIndex];
- IR::BasicBlock *block = 0, *nextBlock = 0;
-
- QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches;
- QHash<IR::BasicBlock *, ptrdiff_t> addrs;
-
- int codeSize = 4096;
- uchar *codeStart = new uchar[codeSize];
- memset(codeStart, 0, codeSize);
- uchar *codeNext = codeStart;
- uchar *codeEnd = codeStart + codeSize;
-
- qSwap(_function, function);
- qSwap(block, _block);
- qSwap(nextBlock, _nextBlock);
- qSwap(patches, _patches);
- qSwap(addrs, _addrs);
- qSwap(codeStart, _codeStart);
- qSwap(codeNext, _codeNext);
- qSwap(codeEnd, _codeEnd);
-
- IR::Optimizer opt(_function);
- opt.run(qmlEngine, useTypeInference, /*peelLoops =*/ false);
- if (opt.isInSSA()) {
- static const bool doStackSlotAllocation =
- qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION");
-
- if (doStackSlotAllocation) {
- IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function);
- } else {
- opt.convertOutOfSSA();
- ConvertTemps().toStackSlots(_function);
- }
- opt.showMeTheCode(_function, "After stack slot allocation");
- } else {
- ConvertTemps().toStackSlots(_function);
- }
-
- BitVector removableJumps = opt.calculateOptionalJumps();
- qSwap(_removableJumps, removableJumps);
-
- IR::Stmt *cs = 0;
- qSwap(_currentStatement, cs);
-
- int locals = frameSize();
- Q_ASSERT(locals >= 0);
-
- IR::BasicBlock *exceptionHandler = 0;
-
- Instruction::Push push;
- push.value = quint32(locals);
- addInstruction(push);
-
- currentLine = 0;
- const QVector<IR::BasicBlock *> &basicBlocks = _function->basicBlocks();
- for (int i = 0, ei = basicBlocks.size(); i != ei; ++i) {
- blockNeedsDebugInstruction = irModule->debugMode;
- _block = basicBlocks[i];
- _nextBlock = (i < ei - 1) ? basicBlocks[i + 1] : 0;
- _addrs.insert(_block, _codeNext - _codeStart);
-
- if (_block->catchBlock != exceptionHandler) {
- Instruction::SetExceptionHandler set;
- set.offset = 0;
- if (_block->catchBlock) {
- ptrdiff_t loc = addInstruction(set) + (((const char *)&set.offset) - ((const char *)&set));
- _patches[_block->catchBlock].append(loc);
- } else {
- addInstruction(set);
- }
- exceptionHandler = _block->catchBlock;
- } else if (_block->catchBlock == nullptr && _block->index() != 0 && _block->in.isEmpty()) {
- exceptionHandler = nullptr;
- Instruction::SetExceptionHandler set;
- set.offset = 0;
- addInstruction(set);
- }
-
- for (IR::Stmt *s : _block->statements()) {
- _currentStatement = s;
-
- if (s->location.isValid()) {
- if (s->location.startLine != currentLine) {
- blockNeedsDebugInstruction = false;
- currentLine = s->location.startLine;
-#ifndef QT_NO_QML_DEBUGGER
- if (irModule->debugMode) {
- Instruction::Debug debug;
- debug.lineNumber = currentLine;
- addInstruction(debug);
- } else {
- Instruction::Line line;
- line.lineNumber = currentLine;
- addInstruction(line);
- }
-#endif
- }
- }
-
- visit(s);
- }
- }
-
- // TODO: patch stack size (the push instruction)
- patchJumpAddresses();
-
- codeRefs.insert(_function, squeezeCode());
-
- qSwap(_currentStatement, cs);
- qSwap(_removableJumps, removableJumps);
- qSwap(_function, function);
- qSwap(block, _block);
- qSwap(nextBlock, _nextBlock);
- qSwap(patches, _patches);
- qSwap(addrs, _addrs);
- qSwap(codeStart, _codeStart);
- qSwap(codeNext, _codeNext);
- qSwap(codeEnd, _codeEnd);
-
- delete[] codeStart;
-}
-
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep()
-{
- compilationUnit->codeRefs.resize(irModule->functions.size());
- int i = 0;
- for (IR::Function *irFunction : qAsConst(irModule->functions))
- compilationUnit->codeRefs[i++] = codeRefs[irFunction];
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> result;
- result.adopt(compilationUnit.take());
- return result;
-}
-
-void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result)
-{
- Instruction::CallValue call;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.dest = getParam(value);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::CallScopeObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::CallContextObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
- IR::Expr *result)
-{
- if (useFastLookups) {
- Instruction::CallPropertyLookup call;
- call.base = getParam(base);
- call.lookupIndex = registerGetterLookup(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- // call the property on the loaded base
- Instruction::CallProperty call;
- call.base = getParam(base);
- call.name = registerString(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- }
-}
-
-void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args,
- IR::Expr *result)
-{
- // call the property on the loaded base
- Instruction::CallElement call;
- call.base = getParam(base);
- call.index = getParam(index);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target)
-{
- // FIXME: do something more useful with this info
- if (target->type & IR::NumberType && !(source->type & IR::NumberType))
- unop(IR::OpUPlus, source, target);
- else
- copyValue(source, target);
-}
-
-void InstructionSelection::constructActivationProperty(IR::Name *func,
- IR::ExprList *args,
- IR::Expr *target)
-{
- if (useFastLookups && func->global) {
- Instruction::ConstructGlobalLookup call;
- call.index = registerGlobalGetterLookup(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(target);
- addInstruction(call);
- return;
- }
- Instruction::CreateActivationProperty create;
- create.name = registerString(*func->id);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *target)
-{
- if (useFastLookups) {
- Instruction::ConstructPropertyLookup call;
- call.base = getParam(base);
- call.index = registerGetterLookup(name);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(target);
- addInstruction(call);
- return;
- }
- Instruction::CreateProperty create;
- create.base = getParam(base);
- create.name = registerString(name);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *target)
-{
- Instruction::CreateValue create;
- create.func = getParam(value);
- prepareCallArgs(args, create.argc);
- create.callData = callDataStart();
- create.result = getResultParam(target);
- addInstruction(create);
-}
-
-void InstructionSelection::loadThisObject(IR::Expr *e)
-{
- Instruction::LoadThis load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlContext(IR::Expr *e)
-{
- Instruction::LoadQmlContext load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlImportedScripts(IR::Expr *e)
-{
- Instruction::LoadQmlImportedScripts load;
- load.result = getResultParam(e);
- addInstruction(load);
-}
-
-void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e)
-{
- Instruction::LoadQmlSingleton load;
- load.result = getResultParam(e);
- load.name = registerString(name);
- addInstruction(load);
-}
-
-void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *e)
-{
- Q_ASSERT(sourceConst);
-
- Instruction::MoveConst move;
- move.source = convertToValue(sourceConst).asReturnedValue();
- move.result = getResultParam(e);
- addInstruction(move);
-}
-
-void InstructionSelection::loadString(const QString &str, IR::Expr *target)
-{
- Instruction::LoadRuntimeString load;
- load.stringId = registerString(str);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target)
-{
- Instruction::LoadRegExp load;
- load.regExpId = registerRegExp(sourceRegexp);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target)
-{
- if (useFastLookups && name->global) {
- Instruction::GetGlobalLookup load;
- load.index = registerGlobalGetterLookup(*name->id);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadName load;
- load.name = registerString(*name->id);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
-{
- Instruction::StoreName store;
- store.source = getParam(source);
- store.name = registerString(targetName);
- addInstruction(store);
-}
-
-void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target)
-{
- int id = closure->value;
- Instruction::LoadClosure load;
- load.value = id;
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target)
-{
- if (useFastLookups) {
- Instruction::GetLookup load;
- load.base = getParam(base);
- load.index = registerGetterLookup(name);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadProperty load;
- load.base = getParam(base);
- load.name = registerString(name);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase,
- const QString &targetName)
-{
- if (useFastLookups) {
- Instruction::SetLookup store;
- store.base = getParam(targetBase);
- store.index = registerSetterLookup(targetName);
- store.source = getParam(source);
- addInstruction(store);
- return;
- }
- Instruction::StoreProperty store;
- store.base = getParam(targetBase);
- store.name = registerString(targetName);
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::StoreScopeObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::StoreContextObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex)
-{
- Instruction::StoreQObjectProperty store;
- store.base = getParam(targetBase);
- store.propertyIndex = propertyIndex;
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::LoadScopeObjectProperty load;
- load.base = getParam(source);
- load.propertyIndex = index;
- load.captureRequired = captureRequired;
- load.result = getResultParam(target);
- addInstruction(load);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::LoadContextObjectProperty load;
- load.base = getParam(source);
- load.propertyIndex = index;
- load.captureRequired = captureRequired;
- load.result = getResultParam(target);
- addInstruction(load);
- } else if (kind == IR::Member::MemberOfIdObjectsArray) {
- Instruction::LoadIdObject load;
- load.base = getParam(source);
- load.index = index;
- load.result = getResultParam(target);
- addInstruction(load);
- } else {
- Q_ASSERT(false);
- }
-}
-
-void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target)
-{
- if (attachedPropertiesId != 0) {
- Instruction::LoadAttachedQObjectProperty load;
- load.propertyIndex = propertyIndex;
- load.result = getResultParam(target);
- load.attachedPropertiesId = attachedPropertiesId;
- addInstruction(load);
- } else if (isSingletonProperty) {
- Instruction::LoadSingletonQObjectProperty load;
- load.base = getParam(base);
- load.propertyIndex = propertyIndex;
- load.result = getResultParam(target);
- load.captureRequired = captureRequired;
- addInstruction(load);
- } else {
- Instruction::LoadQObjectProperty load;
- load.base = getParam(base);
- load.propertyIndex = propertyIndex;
- load.result = getResultParam(target);
- load.captureRequired = captureRequired;
- addInstruction(load);
- }
-}
-
-void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
-{
- if (0 && useFastLookups) {
- Instruction::LoadElementLookup load;
- load.lookup = registerIndexedGetterLookup();
- load.base = getParam(base);
- load.index = getParam(index);
- load.result = getResultParam(target);
- addInstruction(load);
- return;
- }
- Instruction::LoadElement load;
- load.base = getParam(base);
- load.index = getParam(index);
- load.result = getResultParam(target);
- addInstruction(load);
-}
-
-void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase,
- IR::Expr *targetIndex)
-{
- if (0 && useFastLookups) {
- Instruction::StoreElementLookup store;
- store.lookup = registerIndexedSetterLookup();
- store.base = getParam(targetBase);
- store.index = getParam(targetIndex);
- store.source = getParam(source);
- addInstruction(store);
- return;
- }
- Instruction::StoreElement store;
- store.base = getParam(targetBase);
- store.index = getParam(targetIndex);
- store.source = getParam(source);
- addInstruction(store);
-}
-
-void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target)
-{
- Instruction::Move move;
- move.source = getParam(source);
- move.result = getResultParam(target);
- if (move.source != move.result)
- addInstruction(move);
-}
-
-void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target)
-{
- Instruction::SwapTemps swap;
- swap.left = getParam(source);
- swap.right = getParam(target);
- addInstruction(swap);
-}
-
-void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
-{
- switch (oper) {
- case IR::OpIfTrue:
- Q_ASSERT(!"unreachable"); break;
- case IR::OpNot: {
- // ### enabling this fails in some cases, where apparently the value is not a bool at runtime
- if (0 && isBoolType(source)) {
- Instruction::UNotBool unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- Instruction::UNot unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- case IR::OpUMinus: {
- Instruction::UMinus uminus;
- uminus.source = getParam(source);
- uminus.result = getResultParam(target);
- addInstruction(uminus);
- return;
- }
- case IR::OpUPlus: {
- if (isNumberType(source)) {
- // use a move
- Instruction::Move move;
- move.source = getParam(source);
- move.result = getResultParam(target);
- if (move.source != move.result)
- addInstruction(move);
- return;
- }
- Instruction::UPlus uplus;
- uplus.source = getParam(source);
- uplus.result = getResultParam(target);
- addInstruction(uplus);
- return;
- }
- case IR::OpCompl: {
- // ### enabling this fails in some cases, where apparently the value is not a int at runtime
- if (0 && isIntegerType(source)) {
- Instruction::UComplInt unot;
- unot.source = getParam(source);
- unot.result = getResultParam(target);
- addInstruction(unot);
- return;
- }
- Instruction::UCompl ucompl;
- ucompl.source = getParam(source);
- ucompl.result = getResultParam(target);
- addInstruction(ucompl);
- return;
- }
- case IR::OpIncrement: {
- Instruction::Increment inc;
- inc.source = getParam(source);
- inc.result = getResultParam(target);
- addInstruction(inc);
- return;
- }
- case IR::OpDecrement: {
- Instruction::Decrement dec;
- dec.source = getParam(source);
- dec.result = getResultParam(target);
- addInstruction(dec);
- return;
- }
- default: break;
- } // switch
-
- Q_ASSERT(!"unreachable");
-}
-
-void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- binopHelper(oper, leftSource, rightSource, target);
-}
-
-Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
-{
- if (oper == IR::OpAdd) {
- Instruction::Add add;
- add.lhs = getParam(leftSource);
- add.rhs = getParam(rightSource);
- add.result = getResultParam(target);
- addInstruction(add);
- return add.result;
- }
- if (oper == IR::OpSub) {
- Instruction::Sub sub;
- sub.lhs = getParam(leftSource);
- sub.rhs = getParam(rightSource);
- sub.result = getResultParam(target);
- addInstruction(sub);
- return sub.result;
- }
- if (oper == IR::OpMul) {
- Instruction::Mul mul;
- mul.lhs = getParam(leftSource);
- mul.rhs = getParam(rightSource);
- mul.result = getResultParam(target);
- addInstruction(mul);
- return mul.result;
- }
- if (oper == IR::OpBitAnd) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitAndConst bitAnd;
- bitAnd.lhs = getParam(leftSource);
- bitAnd.rhs = convertToValue(c).Value::toInt32();
- bitAnd.result = getResultParam(target);
- addInstruction(bitAnd);
- return bitAnd.result;
- }
- Instruction::BitAnd bitAnd;
- bitAnd.lhs = getParam(leftSource);
- bitAnd.rhs = getParam(rightSource);
- bitAnd.result = getResultParam(target);
- addInstruction(bitAnd);
- return bitAnd.result;
- }
- if (oper == IR::OpBitOr) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitOrConst bitOr;
- bitOr.lhs = getParam(leftSource);
- bitOr.rhs = convertToValue(c).Value::toInt32();
- bitOr.result = getResultParam(target);
- addInstruction(bitOr);
- return bitOr.result;
- }
- Instruction::BitOr bitOr;
- bitOr.lhs = getParam(leftSource);
- bitOr.rhs = getParam(rightSource);
- bitOr.result = getResultParam(target);
- addInstruction(bitOr);
- return bitOr.result;
- }
- if (oper == IR::OpBitXor) {
- if (leftSource->asConst())
- qSwap(leftSource, rightSource);
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::BitXorConst bitXor;
- bitXor.lhs = getParam(leftSource);
- bitXor.rhs = convertToValue(c).Value::toInt32();
- bitXor.result = getResultParam(target);
- addInstruction(bitXor);
- return bitXor.result;
- }
- Instruction::BitXor bitXor;
- bitXor.lhs = getParam(leftSource);
- bitXor.rhs = getParam(rightSource);
- bitXor.result = getResultParam(target);
- addInstruction(bitXor);
- return bitXor.result;
- }
- if (oper == IR::OpRShift) {
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::ShrConst shr;
- shr.lhs = getParam(leftSource);
- shr.rhs = convertToValue(c).Value::toInt32() & 0x1f;
- shr.result = getResultParam(target);
- addInstruction(shr);
- return shr.result;
- }
- Instruction::Shr shr;
- shr.lhs = getParam(leftSource);
- shr.rhs = getParam(rightSource);
- shr.result = getResultParam(target);
- addInstruction(shr);
- return shr.result;
- }
- if (oper == IR::OpLShift) {
- if (IR::Const *c = rightSource->asConst()) {
- Instruction::ShlConst shl;
- shl.lhs = getParam(leftSource);
- shl.rhs = convertToValue(c).Value::toInt32() & 0x1f;
- shl.result = getResultParam(target);
- addInstruction(shl);
- return shl.result;
- }
- Instruction::Shl shl;
- shl.lhs = getParam(leftSource);
- shl.rhs = getParam(rightSource);
- shl.result = getResultParam(target);
- addInstruction(shl);
- return shl.result;
- }
-
- if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
- Instruction::BinopContext binop;
- if (oper == IR::OpInstanceof)
- binop.alu = QV4::Runtime::instanceof;
- else if (oper == IR::OpIn)
- binop.alu = QV4::Runtime::in;
- else
- binop.alu = QV4::Runtime::add;
- binop.lhs = getParam(leftSource);
- binop.rhs = getParam(rightSource);
- binop.result = getResultParam(target);
- Q_ASSERT(binop.alu != QV4::Runtime::InvalidRuntimeMethod);
- addInstruction(binop);
- return binop.result;
- } else {
- auto binopFunc = aluOpFunction(oper);
- Q_ASSERT(binopFunc != QV4::Runtime::InvalidRuntimeMethod);
- Instruction::Binop binop;
- binop.alu = binopFunc;
- binop.lhs = getParam(leftSource);
- binop.rhs = getParam(rightSource);
- binop.result = getResultParam(target);
- addInstruction(binop);
- return binop.result;
- }
-}
-
-void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 *args)
-{
- int argLocation = outgoingArgumentTempStart();
- argc = 0;
- if (args)
- *args = argLocation;
- if (e) {
- // We need to move all the temps into the function arg array
- Q_ASSERT(argLocation >= 0);
- while (e) {
- if (IR::Const *c = e->expr->asConst()) {
- Instruction::MoveConst move;
- move.source = convertToValue(c).asReturnedValue();
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- } else {
- Instruction::Move move;
- move.source = getParam(e->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- }
- ++argLocation;
- ++argc;
- e = e->next;
- }
- }
-}
-
-void InstructionSelection::addDebugInstruction()
-{
-#ifndef QT_NO_QML_DEBUGGER
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
-#endif
-}
-
-void InstructionSelection::visitJump(IR::Jump *s)
-{
- if (s->target == _nextBlock)
- return;
- if (_removableJumps.at(_block->index()))
- return;
-
- addDebugInstruction();
-
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
-
- _patches[s->target].append(loc);
-}
-
-void InstructionSelection::visitCJump(IR::CJump *s)
-{
- addDebugInstruction();
-
- Param condition;
- if (IR::Temp *t = s->cond->asTemp()) {
- condition = getResultParam(t);
- } else if (IR::Binop *b = s->cond->asBinop()) {
- condition = binopHelper(b->op, b->left, b->right, /*target*/0);
- } else {
- Q_UNIMPLEMENTED();
- }
-
- if (s->iftrue == _nextBlock) {
- Instruction::JumpNe jump;
- jump.offset = 0;
- jump.condition = condition;
- ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iffalse].append(falseLoc);
- } else {
- Instruction::JumpEq jump;
- jump.offset = 0;
- jump.condition = condition;
- ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iftrue].append(trueLoc);
-
- if (s->iffalse != _nextBlock) {
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
- _patches[s->iffalse].append(falseLoc);
- }
- }
-}
-
-void InstructionSelection::visitRet(IR::Ret *s)
-{
- // this is required so stepOut will always be guaranteed to stop in every stack frame
- addDebugInstruction();
-
- Instruction::Ret ret;
- ret.result = getParam(s->expr);
- addInstruction(ret);
-}
-
-void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result)
-{
- if (useFastLookups && func->global) {
- Instruction::CallGlobalLookup call;
- call.index = registerGlobalGetterLookup(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
- return;
- }
- Instruction::CallActivationProperty call;
- call.name = registerString(*func->id);
- prepareCallArgs(args, call.argc);
- call.callData = callDataStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result)
-{
- if (kind == IR::Member::MemberOfQmlScopeObject) {
- Instruction::CallBuiltinTypeofScopeObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- call.result = getResultParam(result);
- addInstruction(call);
- } else if (kind == IR::Member::MemberOfQmlContextObject) {
- Instruction::CallBuiltinTypeofContextObjectProperty call;
- call.base = getParam(base);
- call.index = propertyIndex;
- call.result = getResultParam(result);
- addInstruction(call);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name,
- IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofMember call;
- call.base = getParam(base);
- call.member = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofSubscript call;
- call.base = getParam(base);
- call.index = getParam(index);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofName call;
- call.name = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result)
-{
- Instruction::CallBuiltinTypeofValue call;
- call.value = getParam(value);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteMember call;
- call.base = getParam(base);
- call.member = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index,
- IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteSubscript call;
- call.base = getParam(base);
- call.index = getParam(index);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result)
-{
- Instruction::CallBuiltinDeleteName call;
- call.name = registerString(name);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result)
-{
- Instruction::MoveConst move;
- move.source = QV4::Encode(false);
- move.result = getResultParam(result);
- addInstruction(move);
-}
-
-void InstructionSelection::callBuiltinThrow(IR::Expr *arg)
-{
- Instruction::CallBuiltinThrow call;
- call.arg = getParam(arg);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinReThrow()
-{
- if (_block->catchBlock) {
- // jump to exception handler
- Instruction::Jump jump;
- jump.offset = 0;
- ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
-
- _patches[_block->catchBlock].append(loc);
- } else {
- Instruction::Ret ret;
- int idx = jsUnitGenerator()->registerConstant(QV4::Encode::undefined());
- ret.result = Param::createConstant(idx);
- addInstruction(ret);
- }
-}
-
-void InstructionSelection::callBuiltinUnwindException(IR::Expr *result)
-{
- Instruction::CallBuiltinUnwindException call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-
-void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
-{
- Instruction::CallBuiltinPushCatchScope call;
- call.name = registerString(exceptionName);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result)
-{
- Instruction::CallBuiltinForeachIteratorObject call;
- call.arg = getParam(arg);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result)
-{
- Instruction::CallBuiltinForeachNextPropertyName call;
- call.arg = getParam(arg);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg)
-{
- Instruction::CallBuiltinPushScope call;
- call.arg = getParam(arg);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinPopScope()
-{
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_GCC("-Wuninitialized")
- Instruction::CallBuiltinPopScope call;
- addInstruction(call);
- QT_WARNING_POP
-}
-
-void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
-{
- Instruction::CallBuiltinDeclareVar call;
- call.isDeletable = deletable;
- call.varName = registerString(name);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args)
-{
- Instruction::CallBuiltinDefineArray call;
- prepareCallArgs(args, call.argc, &call.args);
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray)
-{
- int argLocation = outgoingArgumentTempStart();
-
- const int classId = registerJSClass(keyValuePairCount, keyValuePairs);
-
- // Process key/value pairs first
- IR::ExprList *it = keyValuePairs;
- for (int i = 0; i < keyValuePairCount; ++i, it = it->next) {
- // Skip name
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (IR::Const *c = it->expr->asConst()) {
- Instruction::MoveConst move;
- move.source = convertToValue(c).asReturnedValue();
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- } else {
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- }
- ++argLocation;
-
- if (!isData) {
- it = it->next;
-
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- ++argLocation;
- }
- }
-
- // Process array values
- uint arrayValueCount = 0;
- it = arrayEntries;
- while (it) {
- IR::Const *index = it->expr->asConst();
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (!isData) {
- it = it->next; // getter
- it = it->next; // setter
- continue;
- }
-
- ++arrayValueCount;
-
- Instruction::MoveConst indexMove;
- indexMove.source = convertToValue(index).asReturnedValue();
- indexMove.result = Param::createTemp(argLocation);
- addInstruction(indexMove);
- ++argLocation;
-
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
- ++argLocation;
- it = it->next;
- }
-
- // Process array getter/setter pairs
- uint arrayGetterSetterCount = 0;
- it = arrayEntries;
- while (it) {
- IR::Const *index = it->expr->asConst();
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- if (isData) {
- it = it->next; // value
- continue;
- }
-
- ++arrayGetterSetterCount;
-
- Instruction::MoveConst indexMove;
- indexMove.source = convertToValue(index).asReturnedValue();
- indexMove.result = Param::createTemp(argLocation);
- addInstruction(indexMove);
- ++argLocation;
-
- // getter
- Instruction::Move moveGetter;
- moveGetter.source = getParam(it->expr);
- moveGetter.result = Param::createTemp(argLocation);
- addInstruction(moveGetter);
- ++argLocation;
- it = it->next;
-
- // setter
- Instruction::Move moveSetter;
- moveSetter.source = getParam(it->expr);
- moveSetter.result = Param::createTemp(argLocation);
- addInstruction(moveSetter);
- ++argLocation;
- it = it->next;
- }
-
- Instruction::CallBuiltinDefineObjectLiteral call;
- call.internalClassId = classId;
- call.arrayValueCount = arrayValueCount;
- call.arrayGetterSetterCountAndFlags = arrayGetterSetterCount | (needSparseArray << 30);
- call.args = outgoingArgumentTempStart();
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result)
-{
- Instruction::CallBuiltinSetupArgumentsObject call;
- call.result = getResultParam(result);
- addInstruction(call);
-}
-
-
-void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject()
-{
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_GCC("-Wuninitialized")
- Instruction::CallBuiltinConvertThisToObject call;
- addInstruction(call);
- QT_WARNING_POP
-}
-
-ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
-{
- instr.common.instructionType = type;
-
- int instructionSize = Instr::size(type);
- if (_codeEnd - _codeNext < instructionSize) {
- int currSize = _codeEnd - _codeStart;
- uchar *newCode = new uchar[currSize * 2];
- ::memset(newCode + currSize, 0, currSize);
- ::memcpy(newCode, _codeStart, currSize);
- _codeNext = _codeNext - _codeStart + newCode;
- delete[] _codeStart;
- _codeStart = newCode;
- _codeEnd = _codeStart + currSize * 2;
- }
-
- ::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize);
- ptrdiff_t ptrOffset = _codeNext - _codeStart;
- _codeNext += instructionSize;
-
- return ptrOffset;
-}
-
-void InstructionSelection::patchJumpAddresses()
-{
- typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt;
- for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) {
- Q_ASSERT(_addrs.contains(i.key()));
- ptrdiff_t target = _addrs.value(i.key());
-
- const QVector<ptrdiff_t> &patchList = i.value();
- for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) {
- ptrdiff_t patch = patchList.at(ii);
-
- *((ptrdiff_t *)(_codeStart + patch)) = target - patch;
- }
- }
-
- _patches.clear();
- _addrs.clear();
-}
-
-QByteArray InstructionSelection::squeezeCode() const
-{
- int codeSize = _codeNext - _codeStart;
- QByteArray squeezed;
- squeezed.resize(codeSize);
- ::memcpy(squeezed.data(), _codeStart, codeSize);
- return squeezed;
-}
-
-Param InstructionSelection::getParam(IR::Expr *e) {
- Q_ASSERT(e);
-
- if (IR::Const *c = e->asConst()) {
- int idx = jsUnitGenerator()->registerConstant(convertToValue(c).asReturnedValue());
- return Param::createConstant(idx);
- } else if (IR::Temp *t = e->asTemp()) {
- switch (t->kind) {
- case IR::Temp::StackSlot:
- return Param::createTemp(t->index);
- default:
- Q_UNREACHABLE();
- return Param();
- }
- } else if (IR::ArgLocal *al = e->asArgLocal()) {
- switch (al->kind) {
- case IR::ArgLocal::Formal:
- case IR::ArgLocal::ScopedFormal: return Param::createArgument(al->index, al->scope);
- case IR::ArgLocal::Local: return Param::createLocal(al->index);
- case IR::ArgLocal::ScopedLocal: return Param::createScopedLocal(al->index, al->scope);
- default:
- Q_UNREACHABLE();
- return Param();
- }
- } else {
- Q_UNIMPLEMENTED();
- return Param();
- }
-}
-
-
-CompilationUnit::~CompilationUnit()
-{
-}
-
-#if !defined(V4_BOOTSTRAP)
-
-void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
-{
- runtimeFunctions.resize(data->functionTableSize);
- runtimeFunctions.fill(0);
- for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
- const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
-
- QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, &VME::exec);
- runtimeFunction->codeData = reinterpret_cast<const uchar *>(codeRefs.at(i).constData());
- runtimeFunctions[i] = runtimeFunction;
- }
-}
-
-bool CompilationUnit::memoryMapCode(QString *errorString)
-{
- Q_UNUSED(errorString);
- codeRefs.resize(data->functionTableSize);
-
- const char *basePtr = reinterpret_cast<const char *>(data);
-
- for (uint i = 0; i < data->functionTableSize; ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
- const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset));
-#ifdef MOTH_THREADED_INTERPRETER
- // for the threaded interpreter we need to make a copy of the data because it needs to be
- // modified for the instruction handler addresses.
- QByteArray code(codePtr, compiledFunction->codeSize);
-#else
- QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize);
-#endif
- codeRefs[i] = code;
- }
-
- return true;
-}
-
-#endif // V4_BOOTSTRAP
-
-void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit)
-{
- const int codeAlignment = 16;
- quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize);
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
- for (int i = 0; i < codeRefs.size(); ++i) {
- CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i));
- compiledFunction->codeOffset = offset;
- compiledFunction->codeSize = codeRefs.at(i).size();
- offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize);
- }
-}
-
-bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString)
-{
- Q_ASSERT(device->pos() == unit->unitSize);
- Q_ASSERT(device->atEnd());
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
-
- QByteArray padding;
-
- for (int i = 0; i < codeRefs.size(); ++i) {
- const CompiledData::Function *compiledFunction = unit->functionAt(i);
-
- if (device->pos() > qint64(compiledFunction->codeOffset)) {
- *errorString = QStringLiteral("Invalid state of cache file to write.");
- return false;
- }
-
- const quint64 paddingSize = compiledFunction->codeOffset - device->pos();
- padding.fill(0, paddingSize);
- qint64 written = device->write(padding);
- if (written != padding.size()) {
- *errorString = device->errorString();
- return false;
- }
-
- QByteArray code = codeRefs.at(i);
-
- written = device->write(code.constData(), compiledFunction->codeSize);
- if (written != qint64(compiledFunction->codeSize)) {
- *errorString = device->errorString();
- return false;
- }
- }
- return true;
-}
-
-QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
-{
- QQmlRefPointer<CompiledData::CompilationUnit> result;
- result.adopt(new Moth::CompilationUnit);
- return result;
-}
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
deleted file mode 100644
index 4b84bd2831..0000000000
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_MOTH_P_H
-#define QV4ISEL_MOTH_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qv4global_p.h>
-#include <private/qv4isel_p.h>
-#include <private/qv4isel_util_p.h>
-#include <private/qv4util_p.h>
-#include <private/qv4jsir_p.h>
-#include <private/qv4value_p.h>
-#include "qv4instr_moth_p.h"
-
-#if !defined(V4_BOOTSTRAP)
-QT_REQUIRE_CONFIG(qml_interpreter);
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace Moth {
-
-struct CompilationUnit : public QV4::CompiledData::CompilationUnit
-{
- virtual ~CompilationUnit();
-#if !defined(V4_BOOTSTRAP)
- void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE;
- bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE;
-#endif
- void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE;
- bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE;
-
- QVector<QByteArray> codeRefs;
-
-};
-
-class Q_QML_EXPORT InstructionSelection:
- public IR::IRDecoder,
- public EvalInstructionSelection
-{
-public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- ~InstructionSelection();
-
- void run(int functionIndex) override;
-
-protected:
- QQmlRefPointer<CompiledData::CompilationUnit> backendCompileStep() override;
-
- void visitJump(IR::Jump *) override;
- void visitCJump(IR::CJump *) override;
- void visitRet(IR::Ret *) override;
-
- void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) override;
- void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinTypeofName(const QString &name, IR::Expr *result) override;
- void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) override;
- void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) override;
- void callBuiltinDeleteName(const QString &name, IR::Expr *result) override;
- void callBuiltinDeleteValue(IR::Expr *result) override;
- void callBuiltinThrow(IR::Expr *arg) override;
- void callBuiltinReThrow() override;
- void callBuiltinUnwindException(IR::Expr *) override;
- void callBuiltinPushCatchScope(const QString &exceptionName) override;
- void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) override;
- void callBuiltinPushWithScope(IR::Expr *arg) override;
- void callBuiltinPopScope() override;
- void callBuiltinDeclareVar(bool deletable, const QString &name) override;
- void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) override;
- void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) override;
- void callBuiltinSetupArgumentObject(IR::Expr *result) override;
- void callBuiltinConvertThisToObject() override;
- void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) override;
- void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) override;
- void convertType(IR::Expr *source, IR::Expr *target) override;
- void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) override;
- void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) override;
- void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) override;
- void loadThisObject(IR::Expr *e) override;
- void loadQmlContext(IR::Expr *e) override;
- void loadQmlImportedScripts(IR::Expr *e) override;
- void loadQmlSingleton(const QString &name, IR::Expr *e) override;
- void loadConst(IR::Const *sourceConst, IR::Expr *e) override;
- void loadString(const QString &str, IR::Expr *target) override;
- void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) override;
- void getActivationProperty(const IR::Name *name, IR::Expr *target) override;
- void setActivationProperty(IR::Expr *source, const QString &targetName) override;
- void initClosure(IR::Closure *closure, IR::Expr *target) override;
- void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) override;
- void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) override;
- void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) override;
- void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) override;
- void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) override;
- void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) override;
- void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) override;
- void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) override;
- void copyValue(IR::Expr *source, IR::Expr *target) override;
- void swapValues(IR::Expr *source, IR::Expr *target) override;
- void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) override;
- void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override;
-
-private:
- Param binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target);
-
- struct Instruction {
-#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I;
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF)
-#undef MOTH_INSTR_DATA_TYPEDEF
- private:
- Instruction();
- };
-
- Param getParam(IR::Expr *e);
-
- Param getResultParam(IR::Expr *result)
- {
- if (result)
- return getParam(result);
- else
- return Param::createTemp(scratchTempIndex());
- }
-
- void simpleMove(IR::Move *);
- void prepareCallArgs(IR::ExprList *, quint32 &, quint32 * = 0);
-
- int scratchTempIndex() const { return _function->tempCount; }
- int callDataStart() const { return scratchTempIndex() + 1; }
- int outgoingArgumentTempStart() const { return callDataStart() + offsetof(QV4::CallData, args)/sizeof(QV4::Value); }
- int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; }
-
- template <int Instr>
- inline ptrdiff_t addInstruction(const InstrData<Instr> &data);
- inline void addDebugInstruction();
-
- ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr);
- void patchJumpAddresses();
- QByteArray squeezeCode() const;
-
- QQmlEnginePrivate *qmlEngine;
-
- bool blockNeedsDebugInstruction;
- uint currentLine;
- IR::BasicBlock *_block;
- IR::BasicBlock *_nextBlock;
-
- QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches;
- QHash<IR::BasicBlock *, ptrdiff_t> _addrs;
-
- uchar *_codeStart;
- uchar *_codeNext;
- uchar *_codeEnd;
-
- BitVector _removableJumps;
- IR::Stmt *_currentStatement;
-
- QScopedPointer<CompilationUnit> compilationUnit;
- QHash<IR::Function *, QByteArray> codeRefs;
-};
-
-class Q_QML_EXPORT ISelFactory: public EvalISelFactory
-{
-public:
- ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {}
- virtual ~ISelFactory() {}
- EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
- bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL
- { return false; }
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
-
-};
-
-template<int InstrT>
-ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data)
-{
- Instr genericInstr;
- InstrMeta<InstrT>::setDataNoCommon(genericInstr, data);
- return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr);
-}
-
-} // namespace Moth
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_MOTH_P_H
diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp
deleted file mode 100644
index efcfb9bd77..0000000000
--- a/src/qml/compiler/qv4isel_p.cpp
+++ /dev/null
@@ -1,446 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QDebug>
-#include <QtCore/QBuffer>
-#include "qv4jsir_p.h"
-#include "qv4isel_p.h"
-#include "qv4isel_util_p.h"
-#include <private/qv4value_p.h>
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
-#include <QString>
-
-using namespace QV4;
-using namespace QV4::IR;
-
-EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
- : useFastLookups(true)
- , useTypeInference(true)
- , executableAllocator(execAllocator)
- , irModule(module)
-{
- if (!jsGenerator) {
- jsGenerator = new QV4::Compiler::JSUnitGenerator(module);
- ownJSGenerator.reset(jsGenerator);
- }
- this->jsGenerator = jsGenerator;
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(execAllocator);
-#endif
- Q_ASSERT(module);
- jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName;
-}
-
-EvalInstructionSelection::~EvalInstructionSelection()
-{}
-
-EvalISelFactory::~EvalISelFactory()
-{}
-
-QQmlRefPointer<CompiledData::CompilationUnit> EvalInstructionSelection::compile(bool generateUnitData)
-{
- for (int i = 0; i < irModule->functions.size(); ++i)
- run(i);
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = backendCompileStep();
- if (generateUnitData)
- unit->data = jsGenerator->generateUnit();
- return unit;
-}
-
-void IRDecoder::visitMove(IR::Move *s)
-{
- if (IR::Name *n = s->target->asName()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- setActivationProperty(s->source, *n->id);
- return;
- }
- } else if (s->target->asTemp() || s->target->asArgLocal()) {
- if (IR::Name *n = s->source->asName()) {
- if (n->id && *n->id == QLatin1String("this")) // TODO: `this' should be a builtin.
- loadThisObject(s->target);
- else if (n->builtin == IR::Name::builtin_qml_context)
- loadQmlContext(s->target);
- else if (n->builtin == IR::Name::builtin_qml_imported_scripts_object)
- loadQmlImportedScripts(s->target);
- else if (n->qmlSingleton)
- loadQmlSingleton(*n->id, s->target);
- else
- getActivationProperty(n, s->target);
- return;
- } else if (IR::Const *c = s->source->asConst()) {
- loadConst(c, s->target);
- return;
- } else if (s->source->asTemp() || s->source->asArgLocal()) {
- if (s->swap)
- swapValues(s->source, s->target);
- else
- copyValue(s->source, s->target);
- return;
- } else if (IR::String *str = s->source->asString()) {
- loadString(*str->value, s->target);
- return;
- } else if (IR::RegExp *re = s->source->asRegExp()) {
- loadRegexp(re, s->target);
- return;
- } else if (IR::Closure *clos = s->source->asClosure()) {
- initClosure(clos, s->target);
- return;
- } else if (IR::New *ctor = s->source->asNew()) {
- if (Name *func = ctor->base->asName()) {
- constructActivationProperty(func, ctor->args, s->target);
- return;
- } else if (IR::Member *member = ctor->base->asMember()) {
- constructProperty(member->base, *member->name, ctor->args, s->target);
- return;
- } else if (ctor->base->asTemp() || ctor->base->asArgLocal()) {
- constructValue(ctor->base, ctor->args, s->target);
- return;
- }
- } else if (IR::Member *m = s->source->asMember()) {
- if (m->property) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
- bool captureRequired = true;
-
- Q_ASSERT(m->kind != IR::Member::MemberOfEnum && m->kind != IR::Member::MemberOfIdObjectsArray);
- const int attachedPropertiesId = m->attachedPropertiesId;
- const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject;
-
- if (_function && attachedPropertiesId == 0 && !m->property->isConstant() && _function->isQmlBinding) {
- if (m->kind == IR::Member::MemberOfQmlContextObject) {
- _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
- captureRequired = false;
- } else if (m->kind == IR::Member::MemberOfQmlScopeObject) {
- _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
- captureRequired = false;
- }
- }
- if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), captureRequired, s->target);
- return;
- }
- getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target);
-#endif // V4_BOOTSTRAP
- return;
- } else if (m->kind == IR::Member::MemberOfIdObjectsArray) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, /*captureRequired*/false, s->target);
- return;
- } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
- getProperty(m->base, *m->name, s->target);
- return;
- }
- } else if (IR::Subscript *ss = s->source->asSubscript()) {
- getElement(ss->base, ss->index, s->target);
- return;
- } else if (IR::Unop *u = s->source->asUnop()) {
- unop(u->op, u->expr, s->target);
- return;
- } else if (IR::Binop *b = s->source->asBinop()) {
- binop(b->op, b->left, b->right, s->target);
- return;
- } else if (IR::Call *c = s->source->asCall()) {
- if (c->base->asName()) {
- callBuiltin(c, s->target);
- return;
- } else if (Member *member = c->base->asMember()) {
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target);
- return;
- }
-#endif
- callProperty(member->base, *member->name, c->args, s->target);
- return;
- } else if (Subscript *ss = c->base->asSubscript()) {
- callSubscript(ss->base, ss->index, c->args, s->target);
- return;
- } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) {
- callValue(c->base, c->args, s->target);
- return;
- }
- } else if (IR::Convert *c = s->source->asConvert()) {
- Q_ASSERT(c->expr->asTemp() || c->expr->asArgLocal());
- convertType(c->expr, s->target);
- return;
- }
- } else if (IR::Member *m = s->target->asMember()) {
- if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- Q_ASSERT(m->kind != IR::Member::MemberOfEnum);
- Q_ASSERT(m->kind != IR::Member::MemberOfIdObjectsArray);
- const int attachedPropertiesId = m->attachedPropertiesId;
- if (m->property && attachedPropertiesId == 0) {
-#ifdef V4_BOOTSTRAP
- Q_UNIMPLEMENTED();
-#else
- if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex());
- return;
- }
- setQObjectProperty(s->source, m->base, m->property->coreIndex());
-#endif
- return;
- } else {
- setProperty(s->source, m->base, *m->name);
- return;
- }
- }
- }
- } else if (IR::Subscript *ss = s->target->asSubscript()) {
- if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) {
- setElement(s->source, ss->base, ss->index);
- return;
- }
- }
-
- // For anything else...:
- Q_UNIMPLEMENTED();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinter(&qout).print(s);
- qout << endl;
- qDebug("%s", buf.data().constData());
- Q_ASSERT(!"TODO");
-}
-
-IRDecoder::~IRDecoder()
-{
-}
-
-void IRDecoder::visitExp(IR::Exp *s)
-{
- if (IR::Call *c = s->expr->asCall()) {
- // These are calls where the result is ignored.
- if (c->base->asName()) {
- callBuiltin(c, 0);
- } else if (c->base->asTemp() || c->base->asArgLocal() || c->base->asConst()) {
- callValue(c->base, c->args, 0);
- } else if (Member *member = c->base->asMember()) {
- Q_ASSERT(member->base->asTemp() || member->base->asArgLocal());
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0);
- return;
- }
-#endif
- callProperty(member->base, *member->name, c->args, 0);
- } else if (Subscript *s = c->base->asSubscript()) {
- callSubscript(s->base, s->index, c->args, 0);
- } else {
- Q_UNREACHABLE();
- }
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRDecoder::callBuiltin(IR::Call *call, Expr *result)
-{
- IR::Name *baseName = call->base->asName();
- Q_ASSERT(baseName != 0);
-
- switch (baseName->builtin) {
- case IR::Name::builtin_invalid:
- callBuiltinInvalid(baseName, call->args, result);
- return;
-
- case IR::Name::builtin_typeof: {
- if (IR::Member *member = call->args->expr->asMember()) {
-#ifndef V4_BOOTSTRAP
- Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
- if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callBuiltinTypeofQmlContextProperty(member->base,
- IR::Member::MemberKind(member->kind),
- member->property->coreIndex(), result);
- return;
- }
-#endif
- callBuiltinTypeofMember(member->base, *member->name, result);
- return;
- } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
- callBuiltinTypeofSubscript(ss->base, ss->index, result);
- return;
- } else if (IR::Name *n = call->args->expr->asName()) {
- callBuiltinTypeofName(*n->id, result);
- return;
- } else if (call->args->expr->asTemp() ||
- call->args->expr->asConst() ||
- call->args->expr->asArgLocal()) {
- callBuiltinTypeofValue(call->args->expr, result);
- return;
- }
- } break;
-
- case IR::Name::builtin_delete: {
- if (IR::Member *m = call->args->expr->asMember()) {
- callBuiltinDeleteMember(m->base, *m->name, result);
- return;
- } else if (IR::Subscript *ss = call->args->expr->asSubscript()) {
- callBuiltinDeleteSubscript(ss->base, ss->index, result);
- return;
- } else if (IR::Name *n = call->args->expr->asName()) {
- callBuiltinDeleteName(*n->id, result);
- return;
- } else if (call->args->expr->asTemp() ||
- call->args->expr->asArgLocal()) {
- // TODO: should throw in strict mode
- callBuiltinDeleteValue(result);
- return;
- }
- } break;
-
- case IR::Name::builtin_throw: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg->asTemp() || arg->asConst() || arg->asArgLocal());
- callBuiltinThrow(arg);
- } return;
-
- case IR::Name::builtin_rethrow: {
- callBuiltinReThrow();
- } return;
-
- case IR::Name::builtin_unwind_exception: {
- callBuiltinUnwindException(result);
- } return;
-
- case IR::Name::builtin_push_catch_scope: {
- IR::String *s = call->args->expr->asString();
- Q_ASSERT(s);
- callBuiltinPushCatchScope(*s->value);
- } return;
-
- case IR::Name::builtin_foreach_iterator_object: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg != 0);
- callBuiltinForeachIteratorObject(arg, result);
- } return;
-
- case IR::Name::builtin_foreach_next_property_name: {
- IR::Expr *arg = call->args->expr;
- Q_ASSERT(arg != 0);
- callBuiltinForeachNextPropertyname(arg, result);
- } return;
- case IR::Name::builtin_push_with_scope: {
- if (call->args->expr->asTemp() || call->args->expr->asArgLocal())
- callBuiltinPushWithScope(call->args->expr);
- else
- Q_UNIMPLEMENTED();
- } return;
-
- case IR::Name::builtin_pop_scope:
- callBuiltinPopScope();
- return;
-
- case IR::Name::builtin_declare_vars: {
- if (!call->args)
- return;
- IR::Const *deletable = call->args->expr->asConst();
- Q_ASSERT(deletable->type == IR::BoolType);
- for (IR::ExprList *it = call->args->next; it; it = it->next) {
- IR::Name *arg = it->expr->asName();
- Q_ASSERT(arg != 0);
- callBuiltinDeclareVar(deletable->value != 0, *arg->id);
- }
- } return;
-
- case IR::Name::builtin_define_array:
- callBuiltinDefineArray(result, call->args);
- return;
-
- case IR::Name::builtin_define_object_literal: {
- IR::ExprList *args = call->args;
- const int keyValuePairsCount = args->expr->asConst()->value;
- args = args->next;
-
- IR::ExprList *keyValuePairs = args;
- for (int i = 0; i < keyValuePairsCount; ++i) {
- args = args->next; // name
- bool isData = args->expr->asConst()->value;
- args = args->next; // isData flag
- args = args->next; // value or getter
- if (!isData)
- args = args->next; // setter
- }
-
- IR::ExprList *arrayEntries = args;
- bool needSparseArray = false;
- for (IR::ExprList *it = arrayEntries; it; it = it->next) {
- uint index = it->expr->asConst()->value;
- if (index > 16) {
- needSparseArray = true;
- break;
- }
- it = it->next;
- bool isData = it->expr->asConst()->value;
- it = it->next;
- if (!isData)
- it = it->next;
- }
-
- callBuiltinDefineObjectLiteral(result, keyValuePairsCount, keyValuePairs, arrayEntries, needSparseArray);
- } return;
-
- case IR::Name::builtin_setup_argument_object:
- callBuiltinSetupArgumentObject(result);
- return;
-
- case IR::Name::builtin_convert_this_to_object:
- callBuiltinConvertThisToObject();
- return;
-
- default:
- break;
- }
-
- Q_UNIMPLEMENTED();
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- IRPrinter(&qout).print(call); qout << endl;
- qDebug("%s", buf.data().constData());
- Q_UNREACHABLE();
-}
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
deleted file mode 100644
index 037c02e5ea..0000000000
--- a/src/qml/compiler/qv4isel_p.h
+++ /dev/null
@@ -1,218 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_P_H
-#define QV4ISEL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include "qv4jsir_p.h"
-#include <private/qv4compileddata_p.h>
-#include <private/qv4compiler_p.h>
-
-#include <qglobal.h>
-#include <QHash>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlEnginePrivate;
-
-namespace QV4 {
-
-class EvalISelFactory;
-class ExecutableAllocator;
-struct Function;
-
-class Q_QML_PRIVATE_EXPORT EvalInstructionSelection
-{
-public:
- EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
- virtual ~EvalInstructionSelection() = 0;
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true);
-
- void setUseFastLookups(bool b) { useFastLookups = b; }
- void setUseTypeInference(bool onoff) { useTypeInference = onoff; }
-
- int registerString(const QString &str) { return jsGenerator->registerString(str); }
- uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); }
- uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); }
- uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); }
- uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); }
- uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); }
- int registerRegExp(IR::RegExp *regexp) { return jsGenerator->registerRegExp(regexp); }
- int registerJSClass(int count, IR::ExprList *args) { return jsGenerator->registerJSClass(count, args); }
- QV4::Compiler::JSUnitGenerator *jsUnitGenerator() const { return jsGenerator; }
-
-protected:
- virtual void run(int functionIndex) = 0;
- virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep() = 0;
-
- bool useFastLookups;
- bool useTypeInference;
- QV4::ExecutableAllocator *executableAllocator;
- QV4::Compiler::JSUnitGenerator *jsGenerator;
- QScopedPointer<QV4::Compiler::JSUnitGenerator> ownJSGenerator;
- IR::Module *irModule;
-};
-
-class Q_QML_PRIVATE_EXPORT EvalISelFactory
-{
-public:
- EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {}
- virtual ~EvalISelFactory() = 0;
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0;
- virtual bool jitCompileRegexps() const = 0;
- virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0;
-
- const QString codeGeneratorName;
-};
-
-namespace IR {
-class Q_QML_PRIVATE_EXPORT IRDecoder
-{
-public:
- IRDecoder() : _function(0) {}
- virtual ~IRDecoder() = 0;
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
-private: // visitor methods for StmtVisitor:
- void visitMove(IR::Move *s);
- void visitExp(IR::Exp *s);
-
-public: // to implement by subclasses:
- virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteName(const QString &name, IR::Expr *result) = 0;
- virtual void callBuiltinDeleteValue(IR::Expr *result) = 0;
- virtual void callBuiltinThrow(IR::Expr *arg) = 0;
- virtual void callBuiltinReThrow() = 0;
- virtual void callBuiltinUnwindException(IR::Expr *) = 0;
- virtual void callBuiltinPushCatchScope(const QString &exceptionName) = 0;
- virtual void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) = 0;
- virtual void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) = 0;
- virtual void callBuiltinPushWithScope(IR::Expr *arg) = 0;
- virtual void callBuiltinPopScope() = 0;
- virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0;
- virtual void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) = 0;
- virtual void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) = 0;
- virtual void callBuiltinSetupArgumentObject(IR::Expr *result) = 0;
- virtual void callBuiltinConvertThisToObject() = 0;
- virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void convertType(IR::Expr *source, IR::Expr *target) = 0;
- virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0;
- virtual void loadThisObject(IR::Expr *target) = 0;
- virtual void loadQmlContext(IR::Expr *target) = 0;
- virtual void loadQmlImportedScripts(IR::Expr *target) = 0;
- virtual void loadQmlSingleton(const QString &name, IR::Expr *target) = 0;
- virtual void loadConst(IR::Const *sourceConst, IR::Expr *target) = 0;
- virtual void loadString(const QString &str, IR::Expr *target) = 0;
- virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) = 0;
- virtual void getActivationProperty(const IR::Name *name, IR::Expr *target) = 0;
- virtual void setActivationProperty(IR::Expr *source, const QString &targetName) = 0;
- virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0;
- virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0;
- virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0;
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) = 0;
- virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0;
- virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0;
- virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0;
- virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) = 0;
- virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) = 0;
- virtual void copyValue(IR::Expr *source, IR::Expr *target) = 0;
- virtual void swapValues(IR::Expr *source, IR::Expr *target) = 0;
- virtual void unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) = 0;
- virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0;
-
-protected:
- virtual void visitJump(IR::Jump *) = 0;
- virtual void visitCJump(IR::CJump *) = 0;
- virtual void visitRet(IR::Ret *) = 0;
- virtual void visitPhi(IR::Phi *) {}
-
- virtual void callBuiltin(IR::Call *c, IR::Expr *result);
-
- IR::Function *_function; // subclass needs to set
-};
-} // namespace IR
-
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_P_H
diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h
deleted file mode 100644
index e949e6f0ad..0000000000
--- a/src/qml/compiler/qv4isel_util_p.h
+++ /dev/null
@@ -1,241 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4ISEL_UTIL_P_H
-#define QV4ISEL_UTIL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4value_p.h"
-#include "qv4jsir_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-
-struct TargetPrimitive32 {
- static TargetPrimitive32 emptyValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Empty) << 32; return p; }
- static TargetPrimitive32 nullValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Null) << 32; return p; }
- static TargetPrimitive32 undefinedValue() { TargetPrimitive32 p; p._val = quint64(Value::Managed_Type_Internal_32) << 32; return p; }
- static TargetPrimitive32 fromBoolean(bool b) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Boolean) << 32 | quint64(b); return p; }
- static TargetPrimitive32 fromInt32(int v) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Integer) << 32 | quint32(v); return p; }
- static TargetPrimitive32 fromDouble(double v) {
- TargetPrimitive32 p;
- memcpy(&p._val, &v, 8);
- return p;
- }
- static TargetPrimitive32 fromUInt32(uint v) {
- if (v < INT_MAX)
- return fromInt32(qint32(v));
- return fromDouble(double(v));
- }
-
- quint32 value() const { return _val & quint64(~quint32(0)); }
- quint32 tag() const { return _val >> 32; }
-
- quint64 rawValue() const { return _val; }
-
-private:
- quint64 _val;
-};
-
-struct TargetPrimitive64 {
- static TargetPrimitive64 emptyValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Empty) << 32; return p; }
- static TargetPrimitive64 nullValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Null) << 32; return p; }
- static TargetPrimitive64 undefinedValue() { TargetPrimitive64 p; p._val = 0; return p; }
- static TargetPrimitive64 fromBoolean(bool b) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Boolean) << 32 | quint64(b); return p; }
- static TargetPrimitive64 fromInt32(int v) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Integer) << 32 | quint32(v); return p; }
- static TargetPrimitive64 fromDouble(double v) {
- TargetPrimitive64 p;
- memcpy(&p._val, &v, 8);
- p._val ^= Value::NaNEncodeMask;
- return p;
- }
- static TargetPrimitive64 fromUInt32(uint v) {
- if (v < INT_MAX)
- return fromInt32(qint32(v));
- return fromDouble(double(v));
- }
-
- quint32 value() const { return _val & quint64(~quint32(0)); }
- quint32 tag() const { return _val >> 32; }
-
- quint64 rawValue() const { return _val; }
-
-private:
- quint64 _val;
-};
-
-inline bool canConvertToSignedInteger(double value)
-{
- int ival = (int) value;
- // +0 != -0, so we need to convert to double when negating 0
- return ival == value && !(value == 0 && isNegative(value));
-}
-
-inline bool canConvertToUnsignedInteger(double value)
-{
- unsigned uval = (unsigned) value;
- // +0 != -0, so we need to convert to double when negating 0
- return uval == value && !(value == 0 && isNegative(value));
-}
-
-template <typename PrimitiveType = Primitive>
-inline PrimitiveType convertToValue(IR::Const *c)
-{
- switch (c->type) {
- case IR::MissingType:
- return PrimitiveType::emptyValue();
- case IR::NullType:
- return PrimitiveType::nullValue();
- case IR::UndefinedType:
- return PrimitiveType::undefinedValue();
- case IR::BoolType:
- return PrimitiveType::fromBoolean(c->value != 0);
- case IR::SInt32Type:
- return PrimitiveType::fromInt32(int(c->value));
- case IR::UInt32Type:
- return PrimitiveType::fromUInt32(unsigned(c->value));
- case IR::DoubleType:
- return PrimitiveType::fromDouble(c->value);
- case IR::NumberType: {
- int ival = (int)c->value;
- if (canConvertToSignedInteger(c->value)) {
- return PrimitiveType::fromInt32(ival);
- } else {
- return PrimitiveType::fromDouble(c->value);
- }
- }
- default:
- Q_UNREACHABLE();
- }
- // unreachable, but the function must return something
- return PrimitiveType::undefinedValue();
-}
-
-class ConvertTemps
-{
- void renumber(IR::Temp *t)
- {
- if (t->kind != IR::Temp::VirtualRegister)
- return;
-
- int stackSlot = _stackSlotForTemp.value(t->index, -1);
- if (stackSlot == -1) {
- stackSlot = allocateFreeSlot();
- _stackSlotForTemp[t->index] = stackSlot;
- }
-
- t->kind = IR::Temp::StackSlot;
- t->index = stackSlot;
- }
-
-protected:
- int _nextUnusedStackSlot;
- QHash<int, int> _stackSlotForTemp;
- IR::BasicBlock *_currentBasicBlock;
- virtual int allocateFreeSlot()
- {
- return _nextUnusedStackSlot++;
- }
-
- virtual void process(IR::Stmt *s)
- {
- visit(s);
- }
-
-public:
- ConvertTemps()
- : _nextUnusedStackSlot(0)
- , _currentBasicBlock(0)
- {}
-
- void toStackSlots(IR::Function *function)
- {
- _stackSlotForTemp.reserve(function->tempCount);
-
- for (IR::BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- _currentBasicBlock = bb;
- for (IR::Stmt *s : bb->statements())
- process(s);
- }
-
- function->tempCount = _nextUnusedStackSlot;
- }
-
-protected:
- void visit(IR::Stmt *s) {
- switch (s->stmtKind) {
- case IR::Stmt::PhiStmt:
- visitPhi(s->asPhi());
- break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
- }
-
- virtual void visitPhi(IR::Phi *)
- { Q_UNREACHABLE(); }
-
-private:
- void visit(IR::Expr *e) {
- if (auto temp = e->asTemp()) {
- renumber(temp);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-} // namespace QV4
-
-QT_END_NAMESPACE
-
-#endif // QV4ISEL_UTIL_P_H
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
deleted file mode 100644
index 5a58380005..0000000000
--- a/src/qml/compiler/qv4jsir.cpp
+++ /dev/null
@@ -1,992 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jsir_p.h"
-#include <private/qqmljsast_p.h>
-
-#ifndef V4_BOOTSTRAP
-#include <private/qqmlpropertycache_p.h>
-#endif
-
-#include <QtCore/QBuffer>
-#include <QtCore/qtextstream.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qset.h>
-#include <cmath>
-
-#include <vector>
-
-#ifdef CONST
-#undef CONST
-#endif
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace IR {
-
-QString typeName(Type t)
-{
- switch (t) {
- case UnknownType: return QStringLiteral("");
- case MissingType: return QStringLiteral("missing");
- case UndefinedType: return QStringLiteral("undefined");
- case NullType: return QStringLiteral("null");
- case BoolType: return QStringLiteral("bool");
- case UInt32Type: return QStringLiteral("uint32");
- case SInt32Type: return QStringLiteral("int32");
- case DoubleType: return QStringLiteral("double");
- case NumberType: return QStringLiteral("number");
- case StringType: return QStringLiteral("string");
- case VarType: return QStringLiteral("var");
- case QObjectType: return QStringLiteral("qobject");
- default: return QStringLiteral("multiple");
- }
-}
-
-const char *opname(AluOp op)
-{
- switch (op) {
- case OpInvalid: return "?";
-
- case OpIfTrue: return "(bool)";
- case OpNot: return "not";
- case OpUMinus: return "neg";
- case OpUPlus: return "plus";
- case OpCompl: return "invert";
- case OpIncrement: return "incr";
- case OpDecrement: return "decr";
-
- case OpBitAnd: return "bitand";
- case OpBitOr: return "bitor";
- case OpBitXor: return "bitxor";
-
- case OpAdd: return "add";
- case OpSub: return "sub";
- case OpMul: return "mul";
- case OpDiv: return "div";
- case OpMod: return "mod";
-
- case OpLShift: return "shl";
- case OpRShift: return "shr";
- case OpURShift: return "asr";
-
- case OpGt: return "gt";
- case OpLt: return "lt";
- case OpGe: return "ge";
- case OpLe: return "le";
- case OpEqual: return "eq";
- case OpNotEqual: return "ne";
- case OpStrictEqual: return "se";
- case OpStrictNotEqual: return "sne";
-
- case OpInstanceof: return "instanceof";
- case OpIn: return "in";
-
- case OpAnd: return "and";
- case OpOr: return "or";
-
- default: return "?";
-
- } // switch
-}
-
-AluOp binaryOperator(int op)
-{
- switch (static_cast<QSOperator::Op>(op)) {
- case QSOperator::Add: return OpAdd;
- case QSOperator::And: return OpAnd;
- case QSOperator::BitAnd: return OpBitAnd;
- case QSOperator::BitOr: return OpBitOr;
- case QSOperator::BitXor: return OpBitXor;
- case QSOperator::Div: return OpDiv;
- case QSOperator::Equal: return OpEqual;
- case QSOperator::Ge: return OpGe;
- case QSOperator::Gt: return OpGt;
- case QSOperator::Le: return OpLe;
- case QSOperator::LShift: return OpLShift;
- case QSOperator::Lt: return OpLt;
- case QSOperator::Mod: return OpMod;
- case QSOperator::Mul: return OpMul;
- case QSOperator::NotEqual: return OpNotEqual;
- case QSOperator::Or: return OpOr;
- case QSOperator::RShift: return OpRShift;
- case QSOperator::StrictEqual: return OpStrictEqual;
- case QSOperator::StrictNotEqual: return OpStrictNotEqual;
- case QSOperator::Sub: return OpSub;
- case QSOperator::URShift: return OpURShift;
- case QSOperator::InstanceOf: return OpInstanceof;
- case QSOperator::In: return OpIn;
- default: return OpInvalid;
- }
-}
-
-class RemoveSharedExpressions
-{
- CloneExpr clone;
- std::vector<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound.
- Expr *uniqueExpr;
-
-public:
- RemoveSharedExpressions(): uniqueExpr(0) {}
-
- void operator()(IR::Function *function)
- {
- subexpressions.clear();
- subexpressions.reserve(function->basicBlockCount() * 8);
-
- for (BasicBlock *block : function->basicBlocks()) {
- if (block->isRemoved())
- continue;
- clone.setBasicBlock(block);
-
- for (Stmt *s : block->statements()) {
- visit(s);
- }
- }
- }
-
-private:
- template <typename Expr_>
- Expr_ *cleanup(Expr_ *expr)
- {
- std::vector<Expr *>::iterator it = std::lower_bound(subexpressions.begin(), subexpressions.end(), expr);
- if (it == subexpressions.end() || *it != expr) {
- subexpressions.insert(it, expr);
- IR::Expr *e = expr;
- qSwap(uniqueExpr, e);
- visit(expr);
- qSwap(uniqueExpr, e);
- return static_cast<Expr_ *>(e);
- }
-
- // the cloned expression is unique by definition
- // so we don't need to add it to `subexpressions'.
- return clone(expr);
- }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- e->expr = cleanup(e->expr);
- } else if (auto m = s->asMove()) {
- m->target = cleanup(m->target);
- m->source = cleanup(m->source);
- } else if (auto c = s->asCJump()) {
- c->cond = cleanup(c->cond);
- } else if (auto r = s->asRet()) {
- r->expr = cleanup(r->expr);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto c = e->asConvert()) {
- c->expr = cleanup(c->expr);
- } else if (auto u = e->asUnop()) {
- u->expr = cleanup(u->expr);
- } else if (auto b = e->asBinop()) {
- b->left = cleanup(b->left);
- b->right = cleanup(b->right);
- } else if (auto c = e->asCall()) {
- c->base = cleanup(c->base);
- for (IR::ExprList *it = c->args; it; it = it->next) {
- it->expr = cleanup(it->expr);
- }
- } else if (auto n = e->asNew()) {
- n->base = cleanup(n->base);
- for (IR::ExprList *it = n->args; it; it = it->next) {
- it->expr = cleanup(it->expr);
- }
- } else if (auto s = e->asSubscript()) {
- s->base = cleanup(s->base);
- s->index = cleanup(s->index);
- } else if (auto m = e->asMember()) {
- m->base = cleanup(m->base);
- }
- }
-};
-
-void Name::initGlobal(const QString *id, quint32 line, quint32 column)
-{
- this->id = id;
- this->builtin = builtin_invalid;
- this->global = true;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-void Name::init(const QString *id, quint32 line, quint32 column)
-{
- this->id = id;
- this->builtin = builtin_invalid;
- this->global = false;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-void Name::init(Builtin builtin, quint32 line, quint32 column)
-{
- this->id = 0;
- this->builtin = builtin;
- this->global = false;
- this->qmlSingleton = false;
- this->freeOfSideEffects = false;
- this->line = line;
- this->column = column;
-}
-
-const char *builtin_to_string(Name::Builtin b)
-{
- switch (b) {
- case Name::builtin_invalid:
- return "builtin_invalid";
- case Name::builtin_typeof:
- return "builtin_typeof";
- case Name::builtin_delete:
- return "builtin_delete";
- case Name::builtin_throw:
- return "builtin_throw";
- case Name::builtin_rethrow:
- return "builtin_rethrow";
- case Name::builtin_unwind_exception:
- return "builtin_unwind_exception";
- case Name::builtin_push_catch_scope:
- return "builtin_push_catch_scope";
- case IR::Name::builtin_foreach_iterator_object:
- return "builtin_foreach_iterator_object";
- case IR::Name::builtin_foreach_next_property_name:
- return "builtin_foreach_next_property_name";
- case IR::Name::builtin_push_with_scope:
- return "builtin_push_with_scope";
- case IR::Name::builtin_pop_scope:
- return "builtin_pop_scope";
- case IR::Name::builtin_declare_vars:
- return "builtin_declare_vars";
- case IR::Name::builtin_define_array:
- return "builtin_define_array";
- case IR::Name::builtin_define_object_literal:
- return "builtin_define_object_literal";
- case IR::Name::builtin_setup_argument_object:
- return "builtin_setup_argument_object";
- case IR::Name::builtin_convert_this_to_object:
- return "builtin_convert_this_to_object";
- case IR::Name::builtin_qml_context:
- return "builtin_qml_context";
- case IR::Name::builtin_qml_imported_scripts_object:
- return "builtin_qml_imported_scripts_object";
- }
- return "builtin_(###FIXME)";
-};
-
-bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{
- if (t1.kind < t2.kind) return true;
- if (t1.kind > t2.kind) return false;
- return t1.index < t2.index;
-}
-
-Function *Module::newFunction(const QString &name, Function *outer)
-{
- Function *f = new Function(this, outer, name);
- functions.append(f);
- if (!outer) {
- if (!isQmlModule) {
- Q_ASSERT(!rootFunction);
- rootFunction = f;
- }
- } else {
- outer->nestedFunctions.append(f);
- }
- return f;
-}
-
-Module::~Module()
-{
- qDeleteAll(functions);
-}
-
-void Module::setFileName(const QString &name)
-{
- fileName = name;
-}
-
-Function::Function(Module *module, Function *outer, const QString &name)
- : module(module)
- , pool(&module->pool)
- , tempCount(0)
- , maxNumberOfArguments(0)
- , outer(outer)
- , insideWithOrCatch(0)
- , hasDirectEval(false)
- , usesArgumentsObject(false)
- , isStrict(false)
- , isNamedExpression(false)
- , hasTry(false)
- , hasWith(false)
- , isQmlBinding(false)
- , unused(0)
- , line(0)
- , column(0)
- , _allBasicBlocks(0)
- , _statementCount(0)
-{
- this->name = newString(name);
- _basicBlocks.reserve(8);
-}
-
-Function::~Function()
-{
- if (_allBasicBlocks) {
- qDeleteAll(*_allBasicBlocks);
- delete _allBasicBlocks;
- } else {
- qDeleteAll(_basicBlocks);
- }
-
- pool = 0;
- module = 0;
-}
-
-
-const QString *Function::newString(const QString &text)
-{
- return &*strings.insert(text);
-}
-
-BasicBlock *Function::newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode)
-{
- BasicBlock *block = new BasicBlock(this, catchBlock);
- return mode == InsertBlock ? addBasicBlock(block) : block;
-}
-
-BasicBlock *Function::addBasicBlock(BasicBlock *block)
-{
- Q_ASSERT(block->index() < 0);
- block->setIndex(_basicBlocks.size());
- _basicBlocks.append(block);
- return block;
-}
-
-void Function::removeBasicBlock(BasicBlock *block)
-{
- block->markAsRemoved();
- block->in.clear();
- block->out.clear();
-}
-
-int Function::liveBasicBlocksCount() const
-{
- int count = 0;
- for (BasicBlock *bb : basicBlocks())
- if (!bb->isRemoved())
- ++count;
- return count;
-}
-
-void Function::removeSharedExpressions()
-{
- RemoveSharedExpressions removeSharedExpressions;
- removeSharedExpressions(this);
-}
-
-int Function::indexOfArgument(const QStringRef &string) const
-{
- for (int i = formals.size() - 1; i >= 0; --i) {
- if (*formals.at(i) == string)
- return i;
- }
- return -1;
-}
-
-void Function::setScheduledBlocks(const QVector<BasicBlock *> &scheduled)
-{
- Q_ASSERT(!_allBasicBlocks);
- _allBasicBlocks = new QVector<BasicBlock *>(basicBlocks());
- _basicBlocks = scheduled;
- for (int i = 0, ei = basicBlockCount(); i != ei; ++i)
- basicBlock(i)->changeIndex(i);
-}
-
-BasicBlock *Function::getOrCreateBasicBlock(int index)
-{
- if (_basicBlocks.size() <= index) {
- const int oldSize = _basicBlocks.size();
- _basicBlocks.resize(index + 1);
- for (int i = oldSize; i <= index; ++i) {
- BasicBlock *block = new BasicBlock(this, 0);
- block->setIndex(i);
- _basicBlocks[i] = block;
- }
- }
-
- return _basicBlocks.at(index);
-}
-
-void Function::setStatementCount(int cnt)
-{
- _statementCount = cnt;
-}
-
-void BasicBlock::setStatements(const QVector<Stmt *> &newStatements)
-{
- Q_ASSERT(!isRemoved());
- Q_ASSERT(newStatements.size() >= _statements.size());
- for (Stmt *s : qAsConst(_statements)) {
- if (Phi *p = s->asPhi()) {
- if (!newStatements.contains(p)) {
- // phi-node was not copied over, so:
- p->destroyData();
- }
- } else {
- break;
- }
- }
- _statements = newStatements;
-}
-
-CloneExpr::CloneExpr(BasicBlock *block)
- : block(block), cloned(0)
-{
-}
-
-void CloneExpr::setBasicBlock(BasicBlock *block)
-{
- this->block = block;
-}
-
-ExprList *CloneExpr::clone(ExprList *list)
-{
- if (! list)
- return 0;
-
- ExprList *clonedList = block->function->New<IR::ExprList>();
- clonedList->init(clone(list->expr), clone(list->next));
- return clonedList;
-}
-
-void CloneExpr::visit(Expr *e)
-{
- if (auto c = e->asConst()) {
- cloned = cloneConst(c, block->function);
- } else if (auto s = e->asString()) {
- cloned = block->STRING(s->value);
- } else if (auto r = e->asRegExp()) {
- cloned = block->REGEXP(r->value, r->flags);
- } else if (auto n = e->asName()) {
- cloned = cloneName(n, block->function);
- } else if (auto t = e->asTemp()) {
- cloned = cloneTemp(t, block->function);
- } else if (auto a = e->asArgLocal()) {
- cloned = cloneArgLocal(a, block->function);
- } else if (auto c = e->asClosure()) {
- cloned = block->CLOSURE(c->value);
- } else if (auto c = e->asConvert()) {
- cloned = block->CONVERT(clone(c->expr), c->type);
- } else if (auto u = e->asUnop()) {
- cloned = block->UNOP(u->op, clone(u->expr));
- } else if (auto b = e->asBinop()) {
- cloned = block->BINOP(b->op, clone(b->left), clone(b->right));
- } else if (auto c = e->asCall()) {
- cloned = block->CALL(clone(c->base), clone(c->args));
- } else if (auto n = e->asNew()) {
- cloned = block->NEW(clone(n->base), clone(n->args));
- } else if (auto s = e->asSubscript()) {
- cloned = block->SUBSCRIPT(clone(s->base), clone(s->index));
- } else if (auto m = e->asMember()) {
- cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-IRPrinter::IRPrinter(QTextStream *out)
- : out(out)
- , positionSize(Stmt::InvalidId)
- , currentBB(0)
-{
-}
-
-IRPrinter::~IRPrinter()
-{
-}
-
-void IRPrinter::print(Stmt *s)
-{
- visit(s);
-}
-
-void IRPrinter::print(const Expr &e)
-{
- visit(const_cast<Expr *>(&e));
-}
-
-void IRPrinter::print(Expr *e)
-{
- visit(e);
-}
-
-void IRPrinter::print(Function *f)
-{
- if (positionSize == Stmt::InvalidId)
- positionSize = QString::number(f->statementCount()).size();
-
- QString n = f->name ? *f->name : QString();
- if (n.isEmpty())
- n.sprintf("%p", f);
- *out << "function " << n << '(';
-
- for (int i = 0; i < f->formals.size(); ++i) {
- if (i != 0)
- *out << ", ";
- *out << *f->formals.at(i);
- }
- *out << ')' << endl
- << '{' << endl;
-
- for (const QString *local : qAsConst(f->locals))
- *out << " local var " << *local << endl;
-
- bool needsSeperator = !f->locals.isEmpty();
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- if (needsSeperator)
- *out << endl;
- else
- needsSeperator = true;
- print(bb);
- }
- *out << '}' << endl;
-}
-
-void IRPrinter::print(BasicBlock *bb)
-{
- std::swap(currentBB, bb);
- printBlockStart();
-
- for (Stmt *s : currentBB->statements()) {
- if (!s)
- continue;
-
- QByteArray str;
- QBuffer buf(&str);
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- QTextStream *prevOut = &os;
- std::swap(out, prevOut);
- addStmtNr(s);
- visit(s);
- if (s->location.startLine) {
- out->flush();
- for (int i = 58 - str.length(); i > 0; --i)
- *out << ' ';
- *out << " ; line: " << s->location.startLine << ", column: " << s->location.startColumn;
- }
-
- out->flush();
- std::swap(out, prevOut);
-
- *out << " " << str << endl;
- }
-
- std::swap(currentBB, bb);
-}
-
-void IRPrinter::visit(Stmt *s)
-{
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRPrinter::visitExp(Exp *s)
-{
- *out << "void ";
- visit(s->expr);
-}
-
-void IRPrinter::visitMove(Move *s)
-{
- if (Temp *targetTemp = s->target->asTemp())
- if (!s->swap && targetTemp->type != UnknownType)
- *out << typeName(targetTemp->type) << ' ';
-
- visit(s->target);
- *out << ' ';
- if (s->swap)
- *out << "<=> ";
- else
- *out << "= ";
- visit(s->source);
-}
-
-void IRPrinter::visitJump(Jump *s)
-{
- *out << "goto L" << s->target->index();
-}
-
-void IRPrinter::visitCJump(CJump *s)
-{
- *out << "if ";
- visit(s->cond);
- *out << " goto L" << s->iftrue->index()
- << " else goto L" << s->iffalse->index();
-}
-
-void IRPrinter::visitRet(Ret *s)
-{
- *out << "return";
- if (s->expr) {
- *out << ' ';
- visit(s->expr);
- }
-}
-
-void IRPrinter::visitPhi(Phi *s)
-{
- if (s->targetTemp->type != UnknownType)
- *out << typeName(s->targetTemp->type) << ' ';
-
- visit(s->targetTemp);
- *out << " = phi ";
- for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
- if (i > 0)
- *out << ", ";
- if (currentBB)
- *out << 'L' << currentBB->in.at(i)->index() << ": ";
- if (s->incoming[i])
- visit(s->incoming[i]);
- }
-}
-
-void IRPrinter::visit(Expr *e)
-{
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void IRPrinter::visitConst(Const *e)
-{
- switch (e->type) {
- case QV4::IR::UndefinedType:
- *out << "undefined";
- break;
- case QV4::IR::NullType:
- *out << "null";
- break;
- case QV4::IR::BoolType:
- *out << (e->value ? "true" : "false");
- break;
- case QV4::IR::MissingType:
- *out << "missing";
- break;
- default:
- if (int(e->value) == 0 && int(e->value) == e->value) {
- if (isNegative(e->value))
- *out << "-0";
- else
- *out << "0";
- } else {
- *out << QString::number(e->value, 'g', 16);
- }
- break;
- }
-}
-
-void IRPrinter::visitString(String *e)
-{
- *out << '"' << escape(*e->value) << '"';
-}
-
-void IRPrinter::visitRegExp(RegExp *e)
-{
- char f[3];
- int i = 0;
- if (e->flags & RegExp::RegExp_Global)
- f[i++] = 'g';
- if (e->flags & RegExp::RegExp_IgnoreCase)
- f[i++] = 'i';
- if (e->flags & RegExp::RegExp_Multiline)
- f[i++] = 'm';
- f[i] = 0;
-
- *out << '/' << *e->value << '/' << f;
-}
-
-void IRPrinter::visitName(Name *e)
-{
- if (e->id) {
- if (*e->id != QLatin1String("this"))
- *out << '.';
- *out << *e->id;
- } else {
- *out << builtin_to_string(e->builtin);
- }
-}
-
-void IRPrinter::visitTemp(Temp *e)
-{
- switch (e->kind) {
- case Temp::VirtualRegister: *out << '%' << e->index; break;
- case Temp::PhysicalRegister: *out << (e->type == DoubleType ? "fp" : "r")
- << e->index; break;
- case Temp::StackSlot: *out << '&' << e->index; break;
- default: *out << "INVALID";
- }
-}
-
-void IRPrinter::visitArgLocal(ArgLocal *e)
-{
- switch (e->kind) {
- case ArgLocal::Formal: *out << '#' << e->index; break;
- case ArgLocal::ScopedFormal: *out << '#' << e->index
- << '@' << e->scope; break;
- case ArgLocal::Local: *out << '$' << e->index; break;
- case ArgLocal::ScopedLocal: *out << '$' << e->index
- << '@' << e->scope; break;
- default: *out << "INVALID";
- }
-}
-
-void IRPrinter::visitClosure(Closure *e)
-{
- QString name = e->functionName ? *e->functionName : QString();
- if (name.isEmpty())
- name.sprintf("%x", e->value);
- *out << "closure " << name;
-}
-
-void IRPrinter::visitConvert(Convert *e)
-{
- *out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' ';
- visit(e->expr);
-}
-
-void IRPrinter::visitUnop(Unop *e)
-{
- *out << opname(e->op) << ' ';
- visit(e->expr);
-}
-
-void IRPrinter::visitBinop(Binop *e)
-{
- *out << opname(e->op) << ' ';
- visit(e->left);
- *out << ", ";
- visit(e->right);
-}
-
-void IRPrinter::visitCall(Call *e)
-{
- *out << "call ";
- visit(e->base);
- *out << '(';
- for (ExprList *it = e->args; it; it = it->next) {
- if (it != e->args)
- *out << ", ";
- visit(it->expr);
- }
- *out << ')';
-}
-
-void IRPrinter::visitNew(New *e)
-{
- *out << "new ";
- visit(e->base);
- *out << '(';
- for (ExprList *it = e->args; it; it = it->next) {
- if (it != e->args)
- *out << ", ";
- visit(it->expr);
- }
- *out << ')';
-}
-
-void IRPrinter::visitSubscript(Subscript *e)
-{
- visit(e->base);
- *out << '[';
- visit(e->index);
- *out << ']';
-}
-
-void IRPrinter::visitMember(Member *e)
-{
- if (e->kind != Member::MemberOfEnum && e->kind != Member::MemberOfIdObjectsArray
- && e->attachedPropertiesId != 0 && !e->base->asTemp())
- *out << "[[attached property from " << e->attachedPropertiesId << "]]";
- else
- visit(e->base);
- *out << '.' << *e->name;
-#ifndef V4_BOOTSTRAP
- if (e->property)
- *out << " (meta-property " << e->property->coreIndex()
- << " <" << QMetaType::typeName(e->property->propType())
- << ">)";
- else if (e->kind == Member::MemberOfIdObjectsArray)
- *out << "(id object " << e->idIndex << ")";
-#endif
-}
-
-QString IRPrinter::escape(const QString &s)
-{
- QString r;
- for (int i = 0; i < s.length(); ++i) {
- const QChar ch = s.at(i);
- if (ch == QLatin1Char('\n'))
- r += QLatin1String("\\n");
- else if (ch == QLatin1Char('\r'))
- r += QLatin1String("\\r");
- else if (ch == QLatin1Char('\\'))
- r += QLatin1String("\\\\");
- else if (ch == QLatin1Char('"'))
- r += QLatin1String("\\\"");
- else if (ch == QLatin1Char('\''))
- r += QLatin1String("\\'");
- else
- r += ch;
- }
- return r;
-}
-
-void IRPrinter::addStmtNr(Stmt *s)
-{
- if (s->id() >= 0)
- addJustifiedNr(s->id());
-}
-
-void IRPrinter::addJustifiedNr(int pos)
-{
- if (positionSize == Stmt::InvalidId) {
- *out << pos << ": ";
- } else {
- QString posStr;
- if (pos != Stmt::InvalidId)
- posStr = QString::number(pos);
- *out << posStr.rightJustified(positionSize);
- if (pos == Stmt::InvalidId)
- *out << " ";
- else
- *out << ": ";
- }
-}
-
-void IRPrinter::printBlockStart()
-{
- if (currentBB->isRemoved()) {
- *out << "(block has been removed)";
- return;
- }
-
- QByteArray str;
- str.append('L');
- str.append(QByteArray::number(currentBB->index()));
- str.append(':');
- if (currentBB->catchBlock) {
- str.append(" (exception handler L");
- str.append(QByteArray::number(currentBB->catchBlock->index()));
- str.append(')');
- }
- for (int i = 66 - str.length(); i; --i)
- str.append(' ');
- *out << str;
-
- *out << "; predecessors:";
- for (BasicBlock *in : qAsConst(currentBB->in))
- *out << " L" << in->index();
- if (currentBB->in.isEmpty())
- *out << " none";
- if (BasicBlock *container = currentBB->containingGroup())
- *out << ", container: L" << container->index();
- if (currentBB->isGroupStart())
- *out << ", loop_header: yes";
- *out << endl;
-}
-
-} // end of namespace IR
-} // end of namespace QV4
-
-QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
deleted file mode 100644
index b534eaa54f..0000000000
--- a/src/qml/compiler/qv4jsir_p.h
+++ /dev/null
@@ -1,1792 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4JSIR_P_H
-#define QV4JSIR_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "private/qv4global_p.h"
-#include <private/qqmljsmemorypool_p.h>
-#include <private/qqmljsastfwd_p.h>
-#include <private/qflagpointer_p.h>
-
-#include <QtCore/private/qnumeric_p.h>
-#include <QtCore/QVector>
-#include <QtCore/QString>
-#include <QtCore/QBitArray>
-#include <QtCore/qurl.h>
-#include <QtCore/QVarLengthArray>
-#include <QtCore/QDateTime>
-#include <qglobal.h>
-
-#if defined(CONST) && defined(Q_OS_WIN)
-# define QT_POP_CONST
-# pragma push_macro("CONST")
-# undef CONST // CONST conflicts with our own identifier
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QTextStream;
-class QQmlType;
-class QQmlPropertyData;
-class QQmlPropertyCache;
-class QQmlEnginePrivate;
-
-namespace QV4 {
-
-inline bool isNegative(double d)
-{
- uchar *dch = (uchar *)&d;
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- return (dch[0] & 0x80);
- else
- return (dch[7] & 0x80);
-
-}
-
-namespace IR {
-
-struct BasicBlock;
-struct Function;
-struct Module;
-
-struct Stmt;
-struct Expr;
-
-// expressions
-struct Const;
-struct String;
-struct RegExp;
-struct Name;
-struct Temp;
-struct ArgLocal;
-struct Closure;
-struct Convert;
-struct Unop;
-struct Binop;
-struct Call;
-struct New;
-struct Subscript;
-struct Member;
-
-// statements
-struct Exp;
-struct Move;
-struct Jump;
-struct CJump;
-struct Ret;
-struct Phi;
-
-template<class T, int Prealloc>
-class VarLengthArray: public QVarLengthArray<T, Prealloc>
-{
-public:
- bool removeOne(const T &element)
- {
- for (int i = 0; i < this->size(); ++i) {
- if (this->at(i) == element) {
- this->remove(i);
- return true;
- }
- }
-
- return false;
- }
-};
-
-// Flag pointer:
-// * The first flag indicates whether the meta object is final.
-// If final, then none of its properties themselves need to
-// be final when considering for lookups in QML.
-// * The second flag indicates whether enums should be included
-// in the lookup of properties or not. The default is false.
-typedef QFlagPointer<QQmlPropertyCache> IRMetaObject;
-
-enum AluOp {
- OpInvalid = 0,
-
- OpIfTrue,
- OpNot,
- OpUMinus,
- OpUPlus,
- OpCompl,
- OpIncrement,
- OpDecrement,
-
- OpBitAnd,
- OpBitOr,
- OpBitXor,
-
- OpAdd,
- OpSub,
- OpMul,
- OpDiv,
- OpMod,
-
- OpLShift,
- OpRShift,
- OpURShift,
-
- OpGt,
- OpLt,
- OpGe,
- OpLe,
- OpEqual,
- OpNotEqual,
- OpStrictEqual,
- OpStrictNotEqual,
-
- OpInstanceof,
- OpIn,
-
- OpAnd,
- OpOr,
-
- LastAluOp = OpOr
-};
-AluOp binaryOperator(int op);
-const char *opname(IR::AluOp op);
-
-enum Type : quint16 {
- UnknownType = 0,
-
- MissingType = 1 << 0,
- UndefinedType = 1 << 1,
- NullType = 1 << 2,
- BoolType = 1 << 3,
-
- SInt32Type = 1 << 4,
- UInt32Type = 1 << 5,
- DoubleType = 1 << 6,
- NumberType = SInt32Type | UInt32Type | DoubleType,
-
- StringType = 1 << 7,
- QObjectType = 1 << 8,
- VarType = 1 << 9
-};
-
-inline bool strictlyEqualTypes(Type t1, Type t2)
-{
- return t1 == t2 || ((t1 & NumberType) && (t2 & NumberType));
-}
-
-QString typeName(Type t);
-
-struct MemberExpressionResolver;
-
-struct DiscoveredType {
- int type;
- MemberExpressionResolver *memberResolver;
-
- DiscoveredType() : type(UnknownType), memberResolver(0) {}
- DiscoveredType(Type t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
- explicit DiscoveredType(int t) : type(t), memberResolver(0) { Q_ASSERT(type != QObjectType); }
- explicit DiscoveredType(MemberExpressionResolver *memberResolver)
- : type(QObjectType)
- , memberResolver(memberResolver)
- { Q_ASSERT(memberResolver); }
-
- bool test(Type t) const { return type & t; }
- bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); }
-
- bool operator!=(Type other) const { return type != other; }
- bool operator==(Type other) const { return type == other; }
- bool operator==(const DiscoveredType &other) const { return type == other.type; }
- bool operator!=(const DiscoveredType &other) const { return type != other.type; }
-};
-
-struct MemberExpressionResolver
-{
- typedef DiscoveredType (*ResolveFunction)(QQmlEnginePrivate *engine,
- const MemberExpressionResolver *resolver,
- Member *member);
-
- MemberExpressionResolver()
- : resolveMember(0), data(0), extraData(0), owner(nullptr), flags(0) {}
-
- bool isValid() const { return !!resolveMember; }
- void clear() { *this = MemberExpressionResolver(); }
-
- ResolveFunction resolveMember;
- void *data; // Could be pointer to meta object, importNameSpace, etc. - depends on resolveMember implementation
- void *extraData; // Could be QQmlTypeNameCache
- Function *owner;
- unsigned int flags;
-};
-
-struct Q_AUTOTEST_EXPORT Expr {
- enum ExprKind : quint8 {
- NameExpr,
- TempExpr,
- ArgLocalExpr,
- SubscriptExpr,
- MemberExpr,
-
- LastLValue = MemberExpr,
-
- ConstExpr,
- StringExpr,
- RegExpExpr,
- ClosureExpr,
- ConvertExpr,
- UnopExpr,
- BinopExpr,
- CallExpr,
- NewExpr
- };
-
- Type type;
- const ExprKind exprKind;
-
- Expr &operator=(const Expr &other) {
- Q_ASSERT(exprKind == other.exprKind);
- type = other.type;
- return *this;
- }
-
- template <typename To>
- inline bool isa() const {
- return To::classof(this);
- }
-
- template <typename To>
- inline To *as() {
- if (isa<To>()) {
- return static_cast<To *>(this);
- } else {
- return nullptr;
- }
- }
-
- template <typename To>
- inline const To *as() const {
- if (isa<To>()) {
- return static_cast<const To *>(this);
- } else {
- return nullptr;
- }
- }
-
- Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {}
- bool isLValue() const;
-
- Const *asConst();
- String *asString();
- RegExp *asRegExp();
- Name *asName();
- Temp *asTemp();
- ArgLocal *asArgLocal();
- Closure *asClosure();
- Convert *asConvert();
- Unop *asUnop();
- Binop *asBinop();
- Call *asCall();
- New *asNew();
- Subscript *asSubscript();
- Member *asMember();
-};
-
-#define EXPR_VISIT_ALL_KINDS(e) \
- switch (e->exprKind) { \
- case QV4::IR::Expr::ConstExpr: \
- break; \
- case QV4::IR::Expr::StringExpr: \
- break; \
- case QV4::IR::Expr::RegExpExpr: \
- break; \
- case QV4::IR::Expr::NameExpr: \
- break; \
- case QV4::IR::Expr::TempExpr: \
- break; \
- case QV4::IR::Expr::ArgLocalExpr: \
- break; \
- case QV4::IR::Expr::ClosureExpr: \
- break; \
- case QV4::IR::Expr::ConvertExpr: { \
- auto casted = e->asConvert(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Expr::UnopExpr: { \
- auto casted = e->asUnop(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Expr::BinopExpr: { \
- auto casted = e->asBinop(); \
- visit(casted->left); \
- visit(casted->right); \
- } break; \
- case QV4::IR::Expr::CallExpr: { \
- auto casted = e->asCall(); \
- visit(casted->base); \
- for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
- visit(it->expr); \
- } break; \
- case QV4::IR::Expr::NewExpr: { \
- auto casted = e->asNew(); \
- visit(casted->base); \
- for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
- visit(it->expr); \
- } break; \
- case QV4::IR::Expr::SubscriptExpr: { \
- auto casted = e->asSubscript(); \
- visit(casted->base); \
- visit(casted->index); \
- } break; \
- case QV4::IR::Expr::MemberExpr: { \
- auto casted = e->asMember(); \
- visit(casted->base); \
- } break; \
- }
-
-struct ExprList {
- Expr *expr;
- ExprList *next;
-
- ExprList(): expr(0), next(0) {}
-
- void init(Expr *expr, ExprList *next = 0)
- {
- this->expr = expr;
- this->next = next;
- }
-};
-
-struct Const: Expr {
- double value;
-
- Const(): Expr(ConstExpr) {}
-
- void init(Type type, double value)
- {
- this->type = type;
- this->value = value;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ConstExpr; }
-};
-
-struct String: Expr {
- const QString *value;
-
- String(): Expr(StringExpr) {}
-
- void init(const QString *value)
- {
- this->value = value;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == StringExpr; }
-};
-
-struct RegExp: Expr {
- // needs to be compatible with the flags in the lexer, and in RegExpObject
- enum Flags {
- RegExp_Global = 0x01,
- RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
- };
-
- const QString *value;
- int flags;
-
- RegExp(): Expr(RegExpExpr) {}
-
- void init(const QString *value, int flags)
- {
- this->value = value;
- this->flags = flags;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; }
-};
-
-struct Name: Expr {
- enum Builtin {
- builtin_invalid,
- builtin_typeof,
- builtin_delete,
- builtin_throw,
- builtin_rethrow,
- builtin_unwind_exception,
- builtin_push_catch_scope,
- builtin_foreach_iterator_object,
- builtin_foreach_next_property_name,
- builtin_push_with_scope,
- builtin_pop_scope,
- builtin_declare_vars,
- builtin_define_array,
- builtin_define_object_literal,
- builtin_setup_argument_object,
- builtin_convert_this_to_object,
- builtin_qml_context,
- builtin_qml_imported_scripts_object
- };
-
- const QString *id;
- Builtin builtin;
- bool global : 1;
- bool qmlSingleton : 1;
- bool freeOfSideEffects : 1;
- quint32 line;
- quint32 column;
-
- Name(): Expr(NameExpr) {}
-
- void initGlobal(const QString *id, quint32 line, quint32 column);
- void init(const QString *id, quint32 line, quint32 column);
- void init(Builtin builtin, quint32 line, quint32 column);
-
- static bool classof(const Expr *c) { return c->exprKind == NameExpr; }
-};
-
-struct Q_AUTOTEST_EXPORT Temp: Expr {
- enum Kind {
- Invalid = 0,
- VirtualRegister,
- PhysicalRegister,
- StackSlot
- };
-
- unsigned index : 28;
- unsigned isReadOnly : 1;
- unsigned kind : 3;
-
- // Used when temp is used as base in member expression
- MemberExpressionResolver *memberResolver;
-
- Temp()
- : Expr(TempExpr)
- , index((1 << 28) - 1)
- , isReadOnly(0)
- , kind(Invalid)
- , memberResolver(0)
- {}
-
- Temp(Type type, Kind kind, unsigned index)
- : Expr(TempExpr)
- , index(index)
- , isReadOnly(0)
- , kind(kind)
- , memberResolver(0)
- {
- this->type = type;
- }
-
- void init(unsigned kind, unsigned index)
- {
- this->index = index;
- this->isReadOnly = false;
- this->kind = kind;
- }
-
- bool isInvalid() const { return kind == Invalid; }
-
- static bool classof(const Expr *c) { return c->exprKind == TempExpr; }
-};
-
-inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{ return t1.index == t2.index && t1.kind == t2.kind && t1.type == t2.type; }
-
-inline bool operator!=(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
-{ return !(t1 == t2); }
-
-inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW
-{ return t.index ^ t.kind ^ seed; }
-
-bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW;
-
-struct Q_AUTOTEST_EXPORT ArgLocal: Expr {
- enum Kind {
- Formal = 0,
- ScopedFormal,
- Local,
- ScopedLocal
- };
-
- unsigned index;
- unsigned scope : 29; // how many scopes outside the current one?
- unsigned kind : 2;
- unsigned isArgumentsOrEval : 1;
-
- void init(unsigned kind, unsigned index, unsigned scope)
- {
- Q_ASSERT((kind == ScopedLocal && scope != 0) ||
- (kind == ScopedFormal && scope != 0) ||
- (scope == 0));
-
- this->kind = kind;
- this->index = index;
- this->scope = scope;
- this->isArgumentsOrEval = false;
- }
-
- ArgLocal(): Expr(ArgLocalExpr) {}
-
- bool operator==(const ArgLocal &other) const
- { return index == other.index && scope == other.scope && kind == other.kind; }
-
- static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; }
-};
-
-struct Closure: Expr {
- int value; // index in _module->functions
- const QString *functionName;
-
- Closure(): Expr(ClosureExpr) {}
-
- void init(int functionInModule, const QString *functionName)
- {
- this->value = functionInModule;
- this->functionName = functionName;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; }
-};
-
-struct Convert: Expr {
- Expr *expr;
-
- Convert(): Expr(ConvertExpr) {}
-
- void init(Expr *expr, Type type)
- {
- this->expr = expr;
- this->type = type;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; }
-};
-
-struct Unop: Expr {
- Expr *expr;
- AluOp op;
-
- Unop(): Expr(UnopExpr) {}
-
- void init(AluOp op, Expr *expr)
- {
- this->op = op;
- this->expr = expr;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == UnopExpr; }
-};
-
-struct Binop: Expr {
- Expr *left; // Temp or Const
- Expr *right; // Temp or Const
- AluOp op;
-
- Binop(): Expr(BinopExpr) {}
-
- void init(AluOp op, Expr *left, Expr *right)
- {
- this->op = op;
- this->left = left;
- this->right = right;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == BinopExpr; }
-};
-
-struct Call: Expr {
- Expr *base; // Name, Member, Temp
- ExprList *args; // List of Temps
-
- Call(): Expr(CallExpr) {}
-
- void init(Expr *base, ExprList *args)
- {
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == CallExpr; }
-};
-
-struct New: Expr {
- Expr *base; // Name, Member, Temp
- ExprList *args; // List of Temps
-
- New(): Expr(NewExpr) {}
-
- void init(Expr *base, ExprList *args)
- {
- this->base = base;
- this->args = args;
- }
-
- Expr *onlyArgument() const {
- if (args && ! args->next)
- return args->expr;
- return 0;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == NewExpr; }
-};
-
-struct Subscript: Expr {
- Expr *base;
- Expr *index;
-
- Subscript(): Expr(SubscriptExpr) {}
-
- void init(Expr *base, Expr *index)
- {
- this->base = base;
- this->index = index;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; }
-};
-
-struct Member: Expr {
- // Used for property dependency tracking
- enum MemberKind {
- UnspecifiedMember,
- MemberOfEnum,
- MemberOfQmlScopeObject,
- MemberOfQmlContextObject,
- MemberOfIdObjectsArray,
- MemberOfSingletonObject,
- };
-
- Expr *base;
- const QString *name;
- QQmlPropertyData *property;
- union { // depending on kind
- int attachedPropertiesId;
- int enumValue;
- int idIndex;
- };
- uchar freeOfSideEffects : 1;
-
- // This is set for example for for QObject properties. All sorts of extra behavior
- // is defined when writing to them, for example resettable properties are reset
- // when writing undefined to them, and an exception is thrown when they're missing
- // a reset function. And then there's also Qt.binding().
- uchar inhibitTypeConversionOnWrite: 1;
-
- uchar kind: 3; // MemberKind
-
- Member(): Expr(MemberExpr) {}
-
- void setEnumValue(int value) {
- kind = MemberOfEnum;
- enumValue = value;
- }
-
- void setAttachedPropertiesId(int id) {
- Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray);
- attachedPropertiesId = id;
- }
-
- void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0)
- {
- this->base = base;
- this->name = name;
- this->property = property;
- this->idIndex = index;
- this->freeOfSideEffects = false;
- this->inhibitTypeConversionOnWrite = property != 0;
- this->kind = kind;
- }
-
- static bool classof(const Expr *c) { return c->exprKind == MemberExpr; }
-};
-
-inline bool Expr::isLValue() const {
- if (auto t = as<Temp>())
- return !t->isReadOnly;
- return exprKind <= LastLValue;
-}
-
-struct Stmt {
- enum StmtKind: quint8 {
- MoveStmt,
- ExpStmt,
- JumpStmt,
- CJumpStmt,
- RetStmt,
- PhiStmt
- };
-
- template <typename To>
- inline bool isa() const {
- return To::classof(this);
- }
-
- template <typename To>
- inline To *as() {
- if (isa<To>())
- return static_cast<To *>(this);
- else
- return nullptr;
- }
-
- enum { InvalidId = -1 };
-
- QQmlJS::AST::SourceLocation location;
-
- explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {}
-
- Stmt *asTerminator();
-
- Exp *asExp();
- Move *asMove();
- Jump *asJump();
- CJump *asCJump();
- Ret *asRet();
- Phi *asPhi();
-
- int id() const { return _id; }
-
-private: // For memory management in BasicBlock
- friend struct BasicBlock;
-
-private:
- friend struct Function;
- int _id;
-
-public:
- const StmtKind stmtKind;
-};
-
-#define STMT_VISIT_ALL_KINDS(s) \
- switch (s->stmtKind) { \
- case QV4::IR::Stmt::MoveStmt: { \
- auto casted = s->asMove(); \
- visit(casted->target); \
- visit(casted->source); \
- } break; \
- case QV4::IR::Stmt::ExpStmt: { \
- auto casted = s->asExp(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Stmt::JumpStmt: \
- break; \
- case QV4::IR::Stmt::CJumpStmt: { \
- auto casted = s->asCJump(); \
- visit(casted->cond); \
- } break; \
- case QV4::IR::Stmt::RetStmt: { \
- auto casted = s->asRet(); \
- visit(casted->expr); \
- } break; \
- case QV4::IR::Stmt::PhiStmt: { \
- auto casted = s->asPhi(); \
- visit(casted->targetTemp); \
- for (auto *e : casted->incoming) { \
- visit(e); \
- } \
- } break; \
- }
-
-struct Exp: Stmt {
- Expr *expr;
-
- Exp(int id): Stmt(id, ExpStmt) {}
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; }
-};
-
-struct Move: Stmt {
- Expr *target; // LHS - Temp, Name, Member or Subscript
- Expr *source;
- bool swap;
-
- Move(int id): Stmt(id, MoveStmt) {}
-
- void init(Expr *target, Expr *source)
- {
- this->target = target;
- this->source = source;
- this->swap = false;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; }
-};
-
-struct Jump: Stmt {
- BasicBlock *target;
-
- Jump(int id): Stmt(id, JumpStmt) {}
-
- void init(BasicBlock *target)
- {
- this->target = target;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; }
-};
-
-struct CJump: Stmt {
- Expr *cond; // Temp, Binop
- BasicBlock *iftrue;
- BasicBlock *iffalse;
- BasicBlock *parent;
-
- CJump(int id): Stmt(id, CJumpStmt) {}
-
- void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent)
- {
- this->cond = cond;
- this->iftrue = iftrue;
- this->iffalse = iffalse;
- this->parent = parent;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; }
-};
-
-struct Ret: Stmt {
- Expr *expr;
-
- Ret(int id): Stmt(id, RetStmt) {}
-
- void init(Expr *expr)
- {
- this->expr = expr;
- }
-
- static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; }
-};
-
-// Phi nodes can only occur at the start of a basic block. If there are any, they need to be
-// subsequent to eachother, and the first phi node should be the first statement in the basic-block.
-// A number of loops rely on this behavior, so they don't need to walk through the whole list
-// of instructions in a basic-block (e.g. the calls to destroyData in BasicBlock::~BasicBlock).
-struct Phi: Stmt {
- Temp *targetTemp;
- VarLengthArray<Expr *, 4> incoming;
-
- Phi(int id): Stmt(id, PhiStmt) {}
-
- static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; }
-
- void destroyData()
- { incoming.~VarLengthArray(); }
-};
-
-inline Stmt *Stmt::asTerminator()
-{
- if (auto s = asJump()) {
- return s;
- } else if (auto s = asCJump()) {
- return s;
- } else if (auto s = asRet()) {
- return s;
- } else {
- return nullptr;
- }
-}
-
-struct Q_QML_PRIVATE_EXPORT Module {
- QQmlJS::MemoryPool pool;
- QVector<Function *> functions;
- Function *rootFunction;
- QString fileName;
- QDateTime sourceTimeStamp;
- bool isQmlModule; // implies rootFunction is always 0
- uint unitFlags; // flags merged into CompiledData::Unit::flags
- QString targetABI; // fallback to QSysInfo::buildAbi() if empty
-#ifdef QT_NO_QML_DEBUGGER
- static const bool debugMode = false;
-#else
- bool debugMode;
-#endif
-
- Function *newFunction(const QString &name, Function *outer);
-
- Module(bool debugMode)
- : rootFunction(0)
- , isQmlModule(false)
- , unitFlags(0)
-#ifndef QT_NO_QML_DEBUGGER
- , debugMode(debugMode)
- {}
-#else
- { Q_UNUSED(debugMode); }
-#endif
- ~Module();
-
- void setFileName(const QString &name);
-};
-
-struct BasicBlock {
-private:
- Q_DISABLE_COPY(BasicBlock)
-
-public:
- typedef VarLengthArray<BasicBlock *, 4> IncomingEdges;
- typedef VarLengthArray<BasicBlock *, 2> OutgoingEdges;
-
- Function *function;
- BasicBlock *catchBlock;
- IncomingEdges in;
- OutgoingEdges out;
- QQmlJS::AST::SourceLocation nextLocation;
-
- BasicBlock(Function *function, BasicBlock *catcher)
- : function(function)
- , catchBlock(catcher)
- , _containingGroup(0)
- , _index(-1)
- , _isExceptionHandler(false)
- , _groupStart(false)
- , _isRemoved(false)
- {}
-
- ~BasicBlock()
- {
- for (Stmt *s : qAsConst(_statements)) {
- if (Phi *p = s->asPhi()) {
- p->destroyData();
- } else {
- break;
- }
- }
- }
-
- const QVector<Stmt *> &statements() const
- {
- Q_ASSERT(!isRemoved());
- return _statements;
- }
-
- int statementCount() const
- {
- Q_ASSERT(!isRemoved());
- return _statements.size();
- }
-
- void setStatements(const QVector<Stmt *> &newStatements);
-
- template <typename Instr> inline Instr i(Instr i)
- {
- Q_ASSERT(!isRemoved());
- appendStatement(i);
- return i;
- }
-
- void appendStatement(Stmt *statement)
- {
- Q_ASSERT(!isRemoved());
- if (nextLocation.startLine)
- statement->location = nextLocation;
- _statements.append(statement);
- }
-
- void prependStatement(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- _statements.prepend(stmt);
- }
-
- void prependStatements(const QVector<Stmt *> &stmts)
- {
- Q_ASSERT(!isRemoved());
- QVector<Stmt *> newStmts = stmts;
- newStmts += _statements;
- _statements = newStmts;
- }
-
- void insertStatementBefore(Stmt *before, Stmt *newStmt)
- {
- int idx = _statements.indexOf(before);
- Q_ASSERT(idx >= 0);
- _statements.insert(idx, newStmt);
- }
-
- void insertStatementBefore(int index, Stmt *newStmt)
- {
- Q_ASSERT(index >= 0);
- _statements.insert(index, newStmt);
- }
-
- void insertStatementBeforeTerminator(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- _statements.insert(_statements.size() - 1, stmt);
- }
-
- void replaceStatement(int index, Stmt *newStmt)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = _statements[index]->asPhi()) {
- p->destroyData();
- }
- _statements[index] = newStmt;
- }
-
- void removeStatement(Stmt *stmt)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = stmt->asPhi()) {
- p->destroyData();
- }
- _statements.remove(_statements.indexOf(stmt));
- }
-
- void removeStatement(int idx)
- {
- Q_ASSERT(!isRemoved());
- if (Phi *p = _statements[idx]->asPhi()) {
- p->destroyData();
- }
- _statements.remove(idx);
- }
-
- inline bool isEmpty() const {
- Q_ASSERT(!isRemoved());
- return _statements.isEmpty();
- }
-
- inline Stmt *terminator() const {
- Q_ASSERT(!isRemoved());
- if (! _statements.isEmpty() && _statements.last()->asTerminator() != 0)
- return _statements.last();
- return 0;
- }
-
- inline bool isTerminated() const {
- Q_ASSERT(!isRemoved());
- if (terminator() != 0)
- return true;
- return false;
- }
-
- unsigned newTemp();
-
- Temp *TEMP(unsigned kind);
- ArgLocal *ARG(unsigned index, unsigned scope);
- ArgLocal *LOCAL(unsigned index, unsigned scope);
-
- Expr *CONST(Type type, double value);
- Expr *STRING(const QString *value);
- Expr *REGEXP(const QString *value, int flags);
-
- Name *NAME(const QString &id, quint32 line, quint32 column);
- Name *NAME(Name::Builtin builtin, quint32 line, quint32 column);
-
- Name *GLOBALNAME(const QString &id, quint32 line, quint32 column);
-
- Closure *CLOSURE(int functionInModule);
-
- Expr *CONVERT(Expr *expr, Type type);
- Expr *UNOP(AluOp op, Expr *expr);
- Expr *BINOP(AluOp op, Expr *left, Expr *right);
- Expr *CALL(Expr *base, ExprList *args = 0);
- Expr *NEW(Expr *base, ExprList *args = 0);
- Expr *SUBSCRIPT(Expr *base, Expr *index);
- Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = Member::UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0);
-
- Stmt *EXP(Expr *expr);
-
- Stmt *MOVE(Expr *target, Expr *source);
-
- Stmt *JUMP(BasicBlock *target);
- Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
- Stmt *RET(Expr *expr);
-
- BasicBlock *containingGroup() const
- {
- Q_ASSERT(!isRemoved());
- return _containingGroup;
- }
-
- void setContainingGroup(BasicBlock *loopHeader)
- {
- Q_ASSERT(!isRemoved());
- _containingGroup = loopHeader;
- }
-
- bool isGroupStart() const
- {
- Q_ASSERT(!isRemoved());
- return _groupStart;
- }
-
- void markAsGroupStart(bool mark = true)
- {
- Q_ASSERT(!isRemoved());
- _groupStart = mark;
- }
-
- // Returns the index of the basic-block.
- // See Function for the full description.
- int index() const
- {
- Q_ASSERT(!isRemoved());
- return _index;
- }
-
- bool isExceptionHandler() const
- { return _isExceptionHandler; }
-
- void setExceptionHandler(bool onoff)
- { _isExceptionHandler = onoff; }
-
- bool isRemoved() const
- { return _isRemoved; }
-
-private: // For Function's eyes only.
- friend struct Function;
- void setIndex(int index)
- {
- Q_ASSERT(_index < 0);
- changeIndex(index);
- }
-
- void changeIndex(int index)
- {
- Q_ASSERT(index >= 0);
- _index = index;
- }
-
- void markAsRemoved()
- {
- _isRemoved = true;
- _index = -1;
- }
-
-private:
- QVector<Stmt *> _statements;
- BasicBlock *_containingGroup;
- int _index;
- unsigned _isExceptionHandler : 1;
- unsigned _groupStart : 1;
- unsigned _isRemoved : 1;
-};
-
-template <typename T>
-class SmallSet: public QVarLengthArray<T, 8>
-{
-public:
- void insert(int value)
- {
- for (auto it : *this) {
- if (it == value)
- return;
- }
- this->append(value);
- }
-};
-
-// Map from meta property index (existence implies dependency) to notify signal index
-struct KeyValuePair
-{
- quint32 _key;
- quint32 _value;
-
- KeyValuePair(): _key(0), _value(0) {}
- KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
-
- quint32 key() const { return _key; }
- quint32 value() const { return _value; }
-};
-
-class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
-{
-public:
- void insert(quint32 key, quint32 value)
- {
- for (auto it = begin(), eit = end(); it != eit; ++it) {
- if (it->_key == key) {
- it->_value = value;
- return;
- }
- }
- append(KeyValuePair(key, value));
- }
-};
-
-// The Function owns (manages), among things, a list of basic-blocks. All the blocks have an index,
-// which corresponds to the index in the entry/index in the vector in which they are stored. This
-// means that algorithms/classes can also store any information about a basic block in an array,
-// where the index corresponds to the index of the basic block, which can then be used to query
-// the function for a pointer to a basic block. This also means that basic-blocks cannot be removed
-// or renumbered.
-//
-// Note that currently there is one exception: after optimization and block scheduling, the
-// method setScheduledBlocks can be called once, to register a newly ordered list. For debugging
-// purposes, these blocks are not immediately renumbered, so renumberBasicBlocks should be called
-// immediately after changing the order. That will restore the property of having a corresponding
-// block-index and block-position-in-basicBlocks-vector.
-//
-// In order for optimization/transformation passes to skip uninteresting basic blocks that will be
-// removed, the block can be marked as such. After doing so, any access will result in a failing
-// assertion.
-struct Function {
- Module *module;
- QQmlJS::MemoryPool *pool;
- const QString *name;
- int tempCount;
- int maxNumberOfArguments;
- QSet<QString> strings;
- QList<const QString *> formals;
- QList<const QString *> locals;
- QVector<Function *> nestedFunctions;
- Function *outer;
-
- int insideWithOrCatch;
-
- uint hasDirectEval: 1;
- uint usesArgumentsObject : 1;
- uint usesThis : 1;
- uint isStrict: 1;
- uint isNamedExpression : 1;
- uint hasTry: 1;
- uint hasWith: 1;
- uint isQmlBinding: 1;
- uint unused : 24;
-
- // Location of declaration in source code (0 if not specified)
- uint line;
- uint column;
-
- // Qml extension:
- SmallSet<int> idObjectDependencies;
- PropertyDependencyMap contextObjectPropertyDependencies;
- PropertyDependencyMap scopeObjectPropertyDependencies;
-
- template <typename T> T *New() { return new (pool->allocate(sizeof(T))) T(); }
- template <typename T> T *NewStmt() {
- return new (pool->allocate(sizeof(T))) T(getNewStatementId());
- }
-
- Function(Module *module, Function *outer, const QString &name);
- ~Function();
-
- enum BasicBlockInsertMode {
- InsertBlock,
- DontInsertBlock
- };
-
- BasicBlock *newBasicBlock(BasicBlock *catchBlock, BasicBlockInsertMode mode = InsertBlock);
- const QString *newString(const QString &text);
-
- void RECEIVE(const QString &name) { formals.append(newString(name)); }
- void LOCAL(const QString &name) { locals.append(newString(name)); }
-
- BasicBlock *addBasicBlock(BasicBlock *block);
- void removeBasicBlock(BasicBlock *block);
-
- const QVector<BasicBlock *> &basicBlocks() const
- { return _basicBlocks; }
-
- BasicBlock *basicBlock(int idx) const
- { return _basicBlocks.at(idx); }
-
- int basicBlockCount() const
- { return _basicBlocks.size(); }
-
- int liveBasicBlocksCount() const;
-
- void removeSharedExpressions();
-
- int indexOfArgument(const QStringRef &string) const;
-
- bool variablesCanEscape() const
- { return hasDirectEval || !nestedFunctions.isEmpty() || module->debugMode; }
-
- void setScheduledBlocks(const QVector<BasicBlock *> &scheduled);
-
- int getNewStatementId() { return _statementCount++; }
- int statementCount() const { return _statementCount; }
-
- bool canUseSimpleCall() const {
- return nestedFunctions.isEmpty() &&
- locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount &&
- !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval;
- }
-
- bool argLocalRequiresWriteBarrier(ArgLocal *al) const {
- uint scope = al->scope;
- const IR::Function *f = this;
- while (scope) {
- f = f->outer;
- --scope;
- }
- return !f->canUseSimpleCall();
- }
- int localsCountForScope(ArgLocal *al) const {
- uint scope = al->scope;
- const IR::Function *f = this;
- while (scope) {
- f = f->outer;
- --scope;
- }
- return f->locals.size();
- }
-
-private:
- BasicBlock *getOrCreateBasicBlock(int index);
- void setStatementCount(int cnt);
-
-private:
- QVector<BasicBlock *> _basicBlocks;
- QVector<BasicBlock *> *_allBasicBlocks;
- int _statementCount;
-};
-
-class CloneExpr
-{
-public:
- explicit CloneExpr(IR::BasicBlock *block = 0);
-
- void setBasicBlock(IR::BasicBlock *block);
-
- template <typename ExprSubclass>
- ExprSubclass *operator()(ExprSubclass *expr)
- {
- return clone(expr);
- }
-
- template <typename ExprSubclass>
- ExprSubclass *clone(ExprSubclass *expr)
- {
- Expr *c = expr;
- qSwap(cloned, c);
- visit(expr);
- qSwap(cloned, c);
- return static_cast<ExprSubclass *>(c);
- }
-
- static Const *cloneConst(Const *c, Function *f)
- {
- Const *newConst = f->New<Const>();
- newConst->init(c->type, c->value);
- return newConst;
- }
-
- static Name *cloneName(Name *n, Function *f)
- {
- Name *newName = f->New<Name>();
- newName->type = n->type;
- newName->id = n->id;
- newName->builtin = n->builtin;
- newName->global = n->global;
- newName->qmlSingleton = n->qmlSingleton;
- newName->freeOfSideEffects = n->freeOfSideEffects;
- newName->line = n->line;
- newName->column = n->column;
- return newName;
- }
-
- static Temp *cloneTemp(Temp *t, Function *f)
- {
- Temp *newTemp = f->New<Temp>();
- newTemp->init(t->kind, t->index);
- newTemp->type = t->type;
- newTemp->memberResolver = t->memberResolver;
- return newTemp;
- }
-
- static ArgLocal *cloneArgLocal(ArgLocal *argLocal, Function *f)
- {
- ArgLocal *newArgLocal = f->New<ArgLocal>();
- newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope);
- newArgLocal->type = argLocal->type;
- newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval;
- return newArgLocal;
- }
-
-private:
- IR::ExprList *clone(IR::ExprList *list);
-
- void visit(Expr *e);
-
-protected:
- IR::BasicBlock *block;
-
-private:
- IR::Expr *cloned;
-};
-
-class Q_AUTOTEST_EXPORT IRPrinter
-{
-public:
- IRPrinter(QTextStream *out);
- virtual ~IRPrinter();
-
- void print(Stmt *s);
- void print(Expr *e);
- void print(const Expr &e);
-
- virtual void print(Function *f);
- virtual void print(BasicBlock *bb);
-
- void visit(Stmt *s);
- virtual void visitExp(Exp *s);
- virtual void visitMove(Move *s);
- virtual void visitJump(Jump *s);
- virtual void visitCJump(CJump *s);
- virtual void visitRet(Ret *s);
- virtual void visitPhi(Phi *s);
-
- void visit(Expr *e);
- virtual void visitConst(Const *e);
- virtual void visitString(String *e);
- virtual void visitRegExp(RegExp *e);
- virtual void visitName(Name *e);
- virtual void visitTemp(Temp *e);
- virtual void visitArgLocal(ArgLocal *e);
- virtual void visitClosure(Closure *e);
- virtual void visitConvert(Convert *e);
- virtual void visitUnop(Unop *e);
- virtual void visitBinop(Binop *e);
- virtual void visitCall(Call *e);
- virtual void visitNew(New *e);
- virtual void visitSubscript(Subscript *e);
- virtual void visitMember(Member *e);
-
- static QString escape(const QString &s);
-
-protected:
- virtual void addStmtNr(Stmt *s);
- void addJustifiedNr(int pos);
- void printBlockStart();
-
-protected:
- QTextStream *out;
- int positionSize;
- BasicBlock *currentBB;
-};
-
-inline unsigned BasicBlock::newTemp()
-{
- Q_ASSERT(!isRemoved());
- return function->tempCount++;
-}
-
-inline Temp *BasicBlock::TEMP(unsigned index)
-{
- Q_ASSERT(!isRemoved());
- Temp *e = function->New<Temp>();
- e->init(Temp::VirtualRegister, index);
- return e;
-}
-
-inline ArgLocal *BasicBlock::ARG(unsigned index, unsigned scope)
-{
- Q_ASSERT(!isRemoved());
- ArgLocal *e = function->New<ArgLocal>();
- e->init(scope ? ArgLocal::ScopedFormal : ArgLocal::Formal, index, scope);
- return e;
-}
-
-inline ArgLocal *BasicBlock::LOCAL(unsigned index, unsigned scope)
-{
- Q_ASSERT(!isRemoved());
- ArgLocal *e = function->New<ArgLocal>();
- e->init(scope ? ArgLocal::ScopedLocal : ArgLocal::Local, index, scope);
- return e;
-}
-
-inline Expr *BasicBlock::CONST(Type type, double value)
-{
- Q_ASSERT(!isRemoved());
- Const *e = function->New<Const>();
- if (type == NumberType) {
- int ival = (int)value;
- // +0 != -0, so we need to convert to double when negating 0
- if (ival == value && !(value == 0 && isNegative(value)))
- type = SInt32Type;
- else
- type = DoubleType;
- } else if (type == NullType) {
- value = 0;
- } else if (type == UndefinedType) {
- value = qt_qnan();
- }
-
- e->init(type, value);
- return e;
-}
-
-inline Expr *BasicBlock::STRING(const QString *value)
-{
- Q_ASSERT(!isRemoved());
- String *e = function->New<String>();
- e->init(value);
- return e;
-}
-
-inline Expr *BasicBlock::REGEXP(const QString *value, int flags)
-{
- Q_ASSERT(!isRemoved());
- RegExp *e = function->New<RegExp>();
- e->init(value, flags);
- return e;
-}
-
-inline Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->init(function->newString(id), line, column);
- return e;
-}
-
-inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->initGlobal(function->newString(id), line, column);
- return e;
-}
-
-
-inline Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column)
-{
- Q_ASSERT(!isRemoved());
- Name *e = function->New<Name>();
- e->init(builtin, line, column);
- return e;
-}
-
-inline Closure *BasicBlock::CLOSURE(int functionInModule)
-{
- Q_ASSERT(!isRemoved());
- Closure *clos = function->New<Closure>();
- clos->init(functionInModule, function->module->functions.at(functionInModule)->name);
- return clos;
-}
-
-inline Expr *BasicBlock::CONVERT(Expr *expr, Type type)
-{
- Q_ASSERT(!isRemoved());
- Convert *e = function->New<Convert>();
- e->init(expr, type);
- return e;
-}
-
-inline Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- Unop *e = function->New<Unop>();
- e->init(op, expr);
- return e;
-}
-
-inline Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
-{
- Q_ASSERT(!isRemoved());
- Binop *e = function->New<Binop>();
- e->init(op, left, right);
- return e;
-}
-
-inline Expr *BasicBlock::CALL(Expr *base, ExprList *args)
-{
- Q_ASSERT(!isRemoved());
- Call *e = function->New<Call>();
- e->init(base, args);
- int argc = 0;
- for (ExprList *it = args; it; it = it->next)
- ++argc;
- function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc);
- return e;
-}
-
-inline Expr *BasicBlock::NEW(Expr *base, ExprList *args)
-{
- Q_ASSERT(!isRemoved());
- New *e = function->New<New>();
- e->init(base, args);
- return e;
-}
-
-inline Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
-{
- Q_ASSERT(!isRemoved());
- Subscript *e = function->New<Subscript>();
- e->init(base, index);
- return e;
-}
-
-inline Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property, uchar kind, int attachedPropertiesIdOrEnumValue)
-{
- Q_ASSERT(!isRemoved());
- Member*e = function->New<Member>();
- e->init(base, name, property, kind, attachedPropertiesIdOrEnumValue);
- return e;
-}
-
-inline Stmt *BasicBlock::EXP(Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Exp *s = function->NewStmt<Exp>();
- s->init(expr);
- appendStatement(s);
- return s;
-}
-
-inline Stmt *BasicBlock::MOVE(Expr *target, Expr *source)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Move *s = function->NewStmt<Move>();
- s->init(target, source);
- appendStatement(s);
- return s;
-}
-
-inline Stmt *BasicBlock::JUMP(BasicBlock *target)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Jump *s = function->NewStmt<Jump>();
- s->init(target);
- appendStatement(s);
-
- Q_ASSERT(! out.contains(target));
- out.append(target);
-
- Q_ASSERT(! target->in.contains(this));
- target->in.append(this);
-
- return s;
-}
-
-inline Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- if (iftrue == iffalse) {
- MOVE(TEMP(newTemp()), cond);
- return JUMP(iftrue);
- }
-
- CJump *s = function->NewStmt<CJump>();
- s->init(cond, iftrue, iffalse, this);
- appendStatement(s);
-
- Q_ASSERT(! out.contains(iftrue));
- out.append(iftrue);
-
- Q_ASSERT(! iftrue->in.contains(this));
- iftrue->in.append(this);
-
- Q_ASSERT(! out.contains(iffalse));
- out.append(iffalse);
-
- Q_ASSERT(! iffalse->in.contains(this));
- iffalse->in.append(this);
-
- return s;
-}
-
-inline Stmt *BasicBlock::RET(Expr *expr)
-{
- Q_ASSERT(!isRemoved());
- if (isTerminated())
- return 0;
-
- Ret *s = function->NewStmt<Ret>();
- s->init(expr);
- appendStatement(s);
- return s;
-}
-
-inline Const *Expr::asConst() { return as<Const>(); }
-inline String *Expr::asString() { return as<String>(); }
-inline RegExp *Expr::asRegExp() { return as<RegExp>(); }
-inline Name *Expr::asName() { return as<Name>(); }
-inline Temp *Expr::asTemp() { return as<Temp>(); }
-inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); }
-inline Closure *Expr::asClosure() { return as<Closure>(); }
-inline Convert *Expr::asConvert() { return as<Convert>(); }
-inline Unop *Expr::asUnop() { return as<Unop>(); }
-inline Binop *Expr::asBinop() { return as<Binop>(); }
-inline Call *Expr::asCall() { return as<Call>(); }
-inline New *Expr::asNew() { return as<New>(); }
-inline Subscript *Expr::asSubscript() { return as<Subscript>(); }
-inline Member *Expr::asMember() { return as<Member>(); }
-
-inline Exp *Stmt::asExp() { return as<Exp>(); }
-inline Move *Stmt::asMove() { return as<Move>(); }
-inline Jump *Stmt::asJump() { return as<Jump>(); }
-inline CJump *Stmt::asCJump() { return as<CJump>(); }
-inline Ret *Stmt::asRet() { return as<Ret>(); }
-inline Phi *Stmt::asPhi() { return as<Phi>(); }
-
-} // end of namespace IR
-
-} // end of namespace QV4
-
-QT_END_NAMESPACE
-
-#if defined(QT_POP_CONST)
-# pragma pop_macro("CONST") // Restore peace
-# undef QT_POP_CONST
-#endif
-
-#endif // QV4IR_P_H
diff --git a/src/qml/compiler/qv4jssimplifier.cpp b/src/qml/compiler/qv4jssimplifier.cpp
deleted file mode 100644
index 7d09218fe6..0000000000
--- a/src/qml/compiler/qv4jssimplifier.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jssimplifier_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator)
- : qmlObjects(qmlObjects)
- , jsModule(jsModule)
- , unitGenerator(unitGenerator)
-{
-
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings()
-{
- for (int i = 0; i < qmlObjects.count(); ++i)
- reduceTranslationBindings(i);
- if (!irFunctionsToRemove.isEmpty()) {
- QQmlIRFunctionCleanser cleanser(jsModule, qmlObjects, irFunctionsToRemove);
- cleanser.clean();
- }
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex)
-{
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
-
- for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
- continue;
-
- const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex);
- QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex);
- if (simplifyBinding(irFunction, binding)) {
- irFunctionsToRemove.append(irFunctionIndex);
- jsModule->functions[irFunctionIndex] = 0;
- delete irFunction;
- }
- }
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move)
-{
- QV4::IR::Temp *target = move->target->asTemp();
- if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
-
- if (QV4::IR::Call *call = move->source->asCall()) {
- if (QV4::IR::Name *n = call->base->asName()) {
- if (n->builtin == QV4::IR::Name::builtin_invalid) {
- visitFunctionCall(n->id, call->args, target);
- return;
- }
- }
- discard();
- return;
- }
-
- if (QV4::IR::Name *n = move->source->asName()) {
- if (n->builtin == QV4::IR::Name::builtin_qml_context
- || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) {
- // these are free of side-effects
- return;
- }
- discard();
- return;
- }
-
- if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) {
- discard();
- return;
- }
-
- _temps[target->index] = move->source;
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target)
-{
- // more than one function call?
- if (_nameOfFunctionCalled) {
- discard();
- return;
- }
-
- _nameOfFunctionCalled = name;
-
- _functionParameters.clear();
- while (args) {
- int slot;
- if (QV4::IR::Temp *param = args->expr->asTemp()) {
- if (param->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
- slot = param->index;
- _functionParameters.append(slot);
- } else if (QV4::IR::Const *param = args->expr->asConst()) {
- slot = --_synthesizedConsts;
- Q_ASSERT(!_temps.contains(slot));
- _temps[slot] = param;
- _functionParameters.append(slot);
- }
- args = args->next;
- }
-
- _functionCallReturnValue = target->index;
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret)
-{
- // nothing initialized earlier?
- if (_returnValueOfBindingExpression != -1) {
- discard();
- return;
- }
- QV4::IR::Temp *target = ret->expr->asTemp();
- if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
- _returnValueOfBindingExpression = target->index;
-}
-
-bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding)
-{
- _canSimplify = true;
- _nameOfFunctionCalled = 0;
- _functionParameters.clear();
- _functionCallReturnValue = -1;
- _temps.clear();
- _returnValueOfBindingExpression = -1;
- _synthesizedConsts = 0;
-
- // It would seem unlikely that function with some many basic blocks (after optimization)
- // consists merely of a qsTr call or a constant value return ;-)
- if (function->basicBlockCount() > 10)
- return false;
-
- for (QV4::IR::BasicBlock *bb : function->basicBlocks()) {
- for (QV4::IR::Stmt *s : bb->statements()) {
- visit(s);
- if (!_canSimplify)
- return false;
- }
- }
-
- if (_returnValueOfBindingExpression == -1)
- return false;
-
- if (_nameOfFunctionCalled) {
- if (_functionCallReturnValue != _returnValueOfBindingExpression)
- return false;
- return detectTranslationCallAndConvertBinding(binding);
- }
-
- return false;
-}
-
-bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding)
-{
- if (*_nameOfFunctionCalled == QLatin1String("qsTr")) {
- QString translation;
- QV4::CompiledData::TranslationData translationData;
- translationData.number = -1;
- translationData.commentIndex = 0; // empty string
-
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- translation = *stringParam->value;
-
- ++param;
- if (param != end) {
- stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
- translationData.commentIndex = unitGenerator->registerString(*stringParam->value);
- ++param;
-
- if (param != end) {
- QV4::IR::Const *constParam = _temps[*param]->asConst();
- if (!constParam || constParam->type != QV4::IR::SInt32Type)
- return false;
-
- translationData.number = int(constParam->value);
- ++param;
- }
- }
-
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_Translation;
- binding->stringIndex = unitGenerator->registerString(translation);
- binding->value.translationData = translationData;
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) {
- QString id;
- QV4::CompiledData::TranslationData translationData;
- translationData.number = -1;
- translationData.commentIndex = 0; // empty string, but unused
-
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- id = *stringParam->value;
-
- ++param;
- if (param != end) {
- QV4::IR::Const *constParam = _temps[*param]->asConst();
- if (!constParam || constParam->type != QV4::IR::SInt32Type)
- return false;
-
- translationData.number = int(constParam->value);
- ++param;
- }
-
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_TranslationById;
- binding->stringIndex = unitGenerator->registerString(id);
- binding->value.translationData = translationData;
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) {
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- ++param;
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_String;
- binding->stringIndex = unitGenerator->registerString(*stringParam->value);
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) {
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- ++param;
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- ++param;
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_String;
- binding->stringIndex = unitGenerator->registerString(*stringParam->value);
- return true;
- }
- return false;
-}
-
-QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object *> &qmlObjects, const QVector<int> &functionsToRemove)
- : module(module)
- , qmlObjects(qmlObjects)
- , functionsToRemove(functionsToRemove)
-{
-}
-
-void QQmlIRFunctionCleanser::clean()
-{
- QVector<QV4::IR::Function*> newFunctions;
- newFunctions.reserve(module->functions.count() - functionsToRemove.count());
-
- newFunctionIndices.resize(module->functions.count());
-
- for (int i = 0; i < module->functions.count(); ++i) {
- QV4::IR::Function *f = module->functions.at(i);
- Q_ASSERT(f || functionsToRemove.contains(i));
- if (f) {
- newFunctionIndices[i] = newFunctions.count();
- newFunctions << f;
- }
- }
-
- module->functions = newFunctions;
-
- for (QV4::IR::Function *function : qAsConst(module->functions)) {
- for (QV4::IR::BasicBlock *block : function->basicBlocks()) {
- for (QV4::IR::Stmt *s : block->statements()) {
- visit(s);
- }
- }
- }
-
- for (QmlIR::Object *obj : qmlObjects) {
- for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i)
- obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)];
- }
-}
-
-void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s)
-{
-
- switch (s->stmtKind) {
- case QV4::IR::Stmt::PhiStmt:
- // nothing to do
- break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
-}
-
-void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e)
-{
- switch (e->exprKind) {
- case QV4::IR::Expr::ClosureExpr: {
- auto closure = e->asClosure();
- closure->value = newFunctionIndices.at(closure->value);
- } break;
- default:
- EXPR_VISIT_ALL_KINDS(e);
- break;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4jssimplifier_p.h b/src/qml/compiler/qv4jssimplifier_p.h
deleted file mode 100644
index ae8d74135c..0000000000
--- a/src/qml/compiler/qv4jssimplifier_p.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4JSSIMPLIFIER
-#define QV4JSSIMPLIFIER
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <private/qv4global_p.h>
-
-#include "qqmlirbuilder_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QmlIR {
-struct Document;
-}
-
-namespace QV4 {
-namespace CompiledData {
-struct QmlUnit;
-struct Location;
-}
-}
-
-class QQmlJavaScriptBindingExpressionSimplificationPass
-{
-public:
- QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator);
-
- void reduceTranslationBindings();
-
-private:
- void reduceTranslationBindings(int objectIndex);
-
- void visit(QV4::IR::Stmt *s)
- {
- switch (s->stmtKind) {
- case QV4::IR::Stmt::MoveStmt:
- visitMove(s->asMove());
- break;
- case QV4::IR::Stmt::RetStmt:
- visitRet(s->asRet());
- break;
- case QV4::IR::Stmt::CJumpStmt:
- discard();
- break;
- case QV4::IR::Stmt::ExpStmt:
- discard();
- break;
- case QV4::IR::Stmt::JumpStmt:
- break;
- case QV4::IR::Stmt::PhiStmt:
- break;
- }
- }
-
- void visitMove(QV4::IR::Move *move);
- void visitRet(QV4::IR::Ret *ret);
-
- void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target);
-
- void discard() { _canSimplify = false; }
-
- bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding);
- bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding);
-
- const QVector<QmlIR::Object*> &qmlObjects;
- QV4::IR::Module *jsModule;
- QV4::Compiler::JSUnitGenerator *unitGenerator;
-
- bool _canSimplify;
- const QString *_nameOfFunctionCalled;
- QVector<int> _functionParameters;
- int _functionCallReturnValue;
-
- QHash<int, QV4::IR::Expr*> _temps;
- int _returnValueOfBindingExpression;
- int _synthesizedConsts;
-
- QVector<int> irFunctionsToRemove;
-};
-
-class QQmlIRFunctionCleanser
-{
-public:
- QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object*> &qmlObjects, const QVector<int> &functionsToRemove);
-
- void clean();
-
-private:
- virtual void visitMove(QV4::IR::Move *s) {
- visit(s->source);
- visit(s->target);
- }
-
- void visit(QV4::IR::Stmt *s);
- void visit(QV4::IR::Expr *e);
-
-private:
- QV4::IR::Module *module;
- const QVector<QmlIR::Object*> &qmlObjects;
- const QVector<int> &functionsToRemove;
-
- QVector<int> newFunctionIndices;
-};
-
-QT_END_NAMESPACE
-
-#endif // QV4JSSIMPLIFIER
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
deleted file mode 100644
index fc136b09ff..0000000000
--- a/src/qml/compiler/qv4ssa.cpp
+++ /dev/null
@@ -1,5848 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// When building with debug code, the macro below will enable debug helpers when using libc++.
-// For example, the std::vector<T>::operator[] will use _LIBCPP_ASSERT to check if the index is
-// within the array bounds. Note that this only works reliably with OSX 10.9 or later.
-//#define _LIBCPP_DEBUG2 2
-
-#include "qv4ssa_p.h"
-#include "qv4isel_util_p.h"
-#include "qv4util_p.h"
-
-#include <QtCore/QBuffer>
-#include <QtCore/QCoreApplication>
-#include <QtCore/QStringList>
-#include <QtCore/QSet>
-#include <QtCore/QLinkedList>
-#include <QtCore/QStack>
-#include <qv4runtime_p.h>
-#include <cmath>
-#include <iostream>
-#include <cassert>
-
-QT_USE_NAMESPACE
-
-using namespace QV4;
-using namespace IR;
-
-namespace {
-
-enum { DebugMoveMapping = 0 };
-
-#ifdef QT_NO_DEBUG
-enum { DoVerification = 0 };
-#else
-enum { DoVerification = 1 };
-#endif
-
-static void showMeTheCode(IR::Function *function, const char *marker)
-{
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (showCode) {
- qDebug() << marker;
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream stream(&buf);
- IRPrinter(&stream).print(function);
- stream << endl;
- qDebug("%s", buf.data().constData());
- }
-}
-
-class ProcessedBlocks
-{
- BitVector processed;
-
-public:
- ProcessedBlocks(IR::Function *function)
- : processed(function->basicBlockCount(), false)
- {}
-
- bool alreadyProcessed(BasicBlock *bb) const
- {
- Q_ASSERT(bb);
-
- return processed.at(bb->index());
- }
-
- void markAsProcessed(BasicBlock *bb)
- {
- processed.setBit(bb->index());
- }
-};
-
-class BasicBlockSet
-{
- typedef BitVector Flags;
-
- QVarLengthArray<int, 8> blockNumbers;
- Flags *blockFlags;
- IR::Function *function;
- enum { MaxVectorCapacity = 8 };
-
-public:
- class const_iterator
- {
- const BasicBlockSet &set;
- // ### These two members could go into a union, but clang won't compile (https://codereview.qt-project.org/#change,74259)
- QVarLengthArray<int, 8>::const_iterator numberIt;
- int flagIt;
-
- friend class BasicBlockSet;
- const_iterator(const BasicBlockSet &set, bool end)
- : set(set)
- {
- if (end || !set.function) {
- if (!set.blockFlags)
- numberIt = set.blockNumbers.end();
- else
- flagIt = set.blockFlags->size();
- } else {
- if (!set.blockFlags)
- numberIt = set.blockNumbers.begin();
- else
- findNextWithFlags(0);
- }
- }
-
- void findNextWithFlags(int start)
- {
- flagIt = set.blockFlags->findNext(start, true, /*wrapAround = */false);
- Q_ASSERT(flagIt <= set.blockFlags->size());
- }
-
- public:
- BasicBlock *operator*() const
- {
- if (!set.blockFlags) {
- return set.function->basicBlock(*numberIt);
- } else {
- Q_ASSERT(flagIt <= set.function->basicBlockCount());
- return set.function->basicBlock(flagIt);
- }
- }
-
- bool operator==(const const_iterator &other) const
- {
- if (&set != &other.set)
- return false;
- if (!set.blockFlags)
- return numberIt == other.numberIt;
- else
- return flagIt == other.flagIt;
- }
-
- bool operator!=(const const_iterator &other) const
- { return !(*this == other); }
-
- const_iterator &operator++()
- {
- if (!set.blockFlags)
- ++numberIt;
- else
- findNextWithFlags(flagIt + 1);
-
- return *this;
- }
- };
-
- friend class const_iterator;
-
-public:
- BasicBlockSet(IR::Function *f = 0): blockFlags(0), function(0)
- {
- if (f)
- init(f);
- }
-
-#ifdef Q_COMPILER_RVALUE_REFS
- BasicBlockSet(BasicBlockSet &&other): blockFlags(0)
- {
- std::swap(blockNumbers, other.blockNumbers);
- std::swap(blockFlags, other.blockFlags);
- std::swap(function, other.function);
- }
-#endif // Q_COMPILER_RVALUE_REFS
-
- BasicBlockSet(const BasicBlockSet &other)
- : blockFlags(0)
- , function(other.function)
- {
- if (other.blockFlags)
- blockFlags = new Flags(*other.blockFlags);
- blockNumbers = other.blockNumbers;
- }
-
- BasicBlockSet &operator=(const BasicBlockSet &other)
- {
- if (blockFlags) {
- delete blockFlags;
- blockFlags = 0;
- }
- function = other.function;
- if (other.blockFlags)
- blockFlags = new Flags(*other.blockFlags);
- blockNumbers = other.blockNumbers;
- return *this;
- }
-
- ~BasicBlockSet()
- {
- delete blockFlags;
- }
-
- void init(IR::Function *f)
- {
- Q_ASSERT(!function);
- Q_ASSERT(f);
- function = f;
- }
-
- bool empty() const
- {
- return begin() == end();
- }
-
- void insert(BasicBlock *bb)
- {
- Q_ASSERT(function);
-
- if (blockFlags) {
- blockFlags->setBit(bb->index());
- return;
- }
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index())
- return;
- }
-
- if (blockNumbers.size() == MaxVectorCapacity) {
- blockFlags = new Flags(function->basicBlockCount(), false);
- for (int i = 0; i < blockNumbers.size(); ++i) {
- blockFlags->setBit(blockNumbers[i]);
- }
- blockNumbers.clear();
- blockFlags->setBit(bb->index());
- } else {
- blockNumbers.append(bb->index());
- }
- }
-
- void remove(BasicBlock *bb)
- {
- Q_ASSERT(function);
-
- if (blockFlags) {
- blockFlags->clearBit(bb->index());
- return;
- }
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index()) {
- blockNumbers.remove(i);
- return;
- }
- }
- }
-
- const_iterator begin() const { return const_iterator(*this, false); }
- const_iterator end() const { return const_iterator(*this, true); }
-
- void collectValues(std::vector<BasicBlock *> &bbs) const
- {
- Q_ASSERT(function);
-
- for (const_iterator it = begin(), eit = end(); it != eit; ++it)
- bbs.push_back(*it);
- }
-
- bool contains(BasicBlock *bb) const
- {
- Q_ASSERT(function);
-
- if (blockFlags)
- return blockFlags->at(bb->index());
-
- for (int i = 0; i < blockNumbers.size(); ++i) {
- if (blockNumbers[i] == bb->index())
- return true;
- }
-
- return false;
- }
-};
-
-class DominatorTree
-{
- enum {
- DebugDominatorFrontiers = 0,
- DebugImmediateDominators = 0,
-
- DebugCodeCanUseLotsOfCpu = 0
- };
-
- typedef int BasicBlockIndex;
- enum { InvalidBasicBlockIndex = -1 };
-
- struct Data
- {
- int N;
- std::vector<int> dfnum; // BasicBlock index -> dfnum
- std::vector<int> vertex;
- std::vector<BasicBlockIndex> parent; // BasicBlock index -> parent BasicBlock index
- std::vector<BasicBlockIndex> ancestor; // BasicBlock index -> ancestor BasicBlock index
- std::vector<BasicBlockIndex> best; // BasicBlock index -> best BasicBlock index
- std::vector<BasicBlockIndex> semi; // BasicBlock index -> semi dominator BasicBlock index
- std::vector<BasicBlockIndex> samedom; // BasicBlock index -> same dominator BasicBlock index
-
- Data(): N(0) {}
- };
-
- IR::Function *function;
- QScopedPointer<Data> d;
- std::vector<BasicBlockIndex> idom; // BasicBlock index -> immediate dominator BasicBlock index
- std::vector<BasicBlockSet> DF; // BasicBlock index -> dominator frontier
-
- struct DFSTodo {
- BasicBlockIndex node, parent;
-
- DFSTodo()
- : node(InvalidBasicBlockIndex)
- , parent(InvalidBasicBlockIndex)
- {}
-
- DFSTodo(BasicBlockIndex node, BasicBlockIndex parent)
- : node(node)
- , parent(parent)
- {}
- };
-
- void DFS(BasicBlockIndex node) {
- std::vector<DFSTodo> worklist;
- worklist.reserve(d->vertex.capacity() / 2);
- DFSTodo todo(node, InvalidBasicBlockIndex);
-
- while (true) {
- BasicBlockIndex n = todo.node;
-
- if (d->dfnum[n] == 0) {
- d->dfnum[n] = d->N;
- d->vertex[d->N] = n;
- d->parent[n] = todo.parent;
- ++d->N;
- const BasicBlock::OutgoingEdges &out = function->basicBlock(n)->out;
- for (int i = out.size() - 1; i > 0; --i)
- worklist.push_back(DFSTodo(out[i]->index(), n));
-
- if (out.size() > 0) {
- todo.node = out.first()->index();
- todo.parent = n;
- continue;
- }
- }
-
- if (worklist.empty())
- break;
-
- todo = worklist.back();
- worklist.pop_back();
- }
- }
-
- BasicBlockIndex ancestorWithLowestSemi(BasicBlockIndex v, std::vector<BasicBlockIndex> &worklist) {
- worklist.clear();
- for (BasicBlockIndex it = v; it != InvalidBasicBlockIndex; it = d->ancestor[it])
- worklist.push_back(it);
-
- if (worklist.size() < 2)
- return d->best[v];
-
- BasicBlockIndex b = InvalidBasicBlockIndex;
- BasicBlockIndex last = worklist.back();
- Q_ASSERT(worklist.size() <= INT_MAX);
- for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) {
- BasicBlockIndex bbIt = worklist[it];
- d->ancestor[bbIt] = last;
- BasicBlockIndex &best_it = d->best[bbIt];
- if (b != InvalidBasicBlockIndex && d->dfnum[d->semi[b]] < d->dfnum[d->semi[best_it]])
- best_it = b;
- else
- b = best_it;
- }
- return b;
- }
-
- void link(BasicBlockIndex p, BasicBlockIndex n) {
- d->ancestor[n] = p;
- d->best[n] = n;
- }
-
- void calculateIDoms() {
- Q_ASSERT(function->basicBlock(0)->in.isEmpty());
-
- const int bbCount = function->basicBlockCount();
- d->vertex = std::vector<int>(bbCount, InvalidBasicBlockIndex);
- d->parent = std::vector<int>(bbCount, InvalidBasicBlockIndex);
- d->dfnum = std::vector<int>(size_t(bbCount), 0);
- d->semi = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->ancestor = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- idom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->samedom = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
- d->best = std::vector<BasicBlockIndex>(bbCount, InvalidBasicBlockIndex);
-
- QHash<BasicBlockIndex, std::vector<BasicBlockIndex> > bucket;
- bucket.reserve(bbCount);
-
- DFS(function->basicBlock(0)->index());
- Q_ASSERT(d->N == function->liveBasicBlocksCount());
-
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(d->vertex.capacity() / 2);
-
- for (int i = d->N - 1; i > 0; --i) {
- BasicBlockIndex n = d->vertex[i];
- BasicBlockIndex p = d->parent[n];
- BasicBlockIndex s = p;
-
- for (BasicBlock *v : function->basicBlock(n)->in) {
- BasicBlockIndex ss = InvalidBasicBlockIndex;
- if (d->dfnum[v->index()] <= d->dfnum[n])
- ss = v->index();
- else
- ss = d->semi[ancestorWithLowestSemi(v->index(), worklist)];
- if (d->dfnum[ss] < d->dfnum[s])
- s = ss;
- }
- d->semi[n] = s;
- bucket[s].push_back(n);
- link(p, n);
- if (bucket.contains(p)) {
- for (BasicBlockIndex v : bucket[p]) {
- BasicBlockIndex y = ancestorWithLowestSemi(v, worklist);
- BasicBlockIndex semi_v = d->semi[v];
- if (d->semi[y] == semi_v)
- idom[v] = semi_v;
- else
- d->samedom[v] = y;
- }
- bucket.remove(p);
- }
- }
-
- for (int i = 1; i < d->N; ++i) {
- BasicBlockIndex n = d->vertex[i];
- Q_ASSERT(n != InvalidBasicBlockIndex);
- Q_ASSERT(!bucket.contains(n));
- Q_ASSERT(d->ancestor[n] != InvalidBasicBlockIndex
- && ((d->semi[n] != InvalidBasicBlockIndex
- && d->dfnum[d->ancestor[n]] <= d->dfnum[d->semi[n]]) || d->semi[n] == n));
- BasicBlockIndex sdn = d->samedom[n];
- if (sdn != InvalidBasicBlockIndex)
- idom[n] = idom[sdn];
- }
-
- dumpImmediateDominators();
- }
-
- struct NodeProgress {
- std::vector<BasicBlockIndex> children;
- std::vector<BasicBlockIndex> todo;
- };
-
-public:
- DominatorTree(IR::Function *function)
- : function(function)
- , d(new Data)
- {
- calculateIDoms();
- d.reset();
- }
-
- void computeDF() {
- DF.resize(function->basicBlockCount());
-
- // compute children of each node in the dominator tree
- std::vector<std::vector<BasicBlockIndex> > children; // BasicBlock index -> children
- children.resize(function->basicBlockCount());
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
- const BasicBlockIndex nodeIndex = n->index();
- Q_ASSERT(function->basicBlock(nodeIndex) == n);
- const BasicBlockIndex nodeDominator = idom[nodeIndex];
- if (nodeDominator == InvalidBasicBlockIndex)
- continue; // there is no dominator to add this node to as a child (e.g. the start node)
- children[nodeDominator].push_back(nodeIndex);
- }
-
- // Fill the worklist and initialize the node status for each basic-block
- std::vector<NodeProgress> nodeStatus;
- nodeStatus.resize(function->basicBlockCount());
- std::vector<BasicBlockIndex> worklist;
- worklist.reserve(function->basicBlockCount());
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- BasicBlockIndex nodeIndex = bb->index();
- worklist.push_back(nodeIndex);
- NodeProgress &np = nodeStatus[nodeIndex];
- np.children = children[nodeIndex];
- np.todo = children[nodeIndex];
- }
-
- BitVector DF_done(function->basicBlockCount(), false);
-
- while (!worklist.empty()) {
- BasicBlockIndex node = worklist.back();
-
- if (DF_done.at(node)) {
- worklist.pop_back();
- continue;
- }
-
- NodeProgress &np = nodeStatus[node];
- std::vector<BasicBlockIndex>::iterator it = np.todo.begin();
- while (it != np.todo.end()) {
- if (DF_done.at(*it)) {
- it = np.todo.erase(it);
- } else {
- worklist.push_back(*it);
- break;
- }
- }
-
- if (np.todo.empty()) {
- BasicBlockSet &S = DF[node];
- S.init(function);
- for (BasicBlock *y : function->basicBlock(node)->out)
- if (idom[y->index()] != node)
- S.insert(y);
- for (BasicBlockIndex child : np.children) {
- const BasicBlockSet &ws = DF[child];
- for (BasicBlockSet::const_iterator it = ws.begin(), eit = ws.end(); it != eit; ++it) {
- BasicBlock *w = *it;
- const BasicBlockIndex wIndex = w->index();
- if (node == wIndex || !dominates(node, w->index()))
- S.insert(w);
- }
- }
- DF_done.setBit(node);
- worklist.pop_back();
- }
- }
-
- if (DebugDominatorFrontiers) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Dominator Frontiers:" << endl;
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
-
- qout << "\tDF[" << n->index() << "]: {";
- const BasicBlockSet &SList = DF[n->index()];
- for (BasicBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) {
- if (i != SList.begin())
- qout << ", ";
- qout << (*i)->index();
- }
- qout << "}" << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-
- if (DebugDominatorFrontiers && DebugCodeCanUseLotsOfCpu) {
- for (BasicBlock *n : function->basicBlocks()) {
- if (n->isRemoved())
- continue;
- const BasicBlockSet &fBlocks = DF[n->index()];
- for (BasicBlockSet::const_iterator it = fBlocks.begin(), eit = fBlocks.end(); it != eit; ++it) {
- BasicBlock *fBlock = *it;
- Q_ASSERT(!dominates(n, fBlock) || fBlock == n);
- bool hasDominatedSucc = false;
- for (BasicBlock *succ : fBlock->in) {
- if (dominates(n, succ)) {
- hasDominatedSucc = true;
- break;
- }
- }
- if (!hasDominatedSucc) {
- qDebug("%d in DF[%d] has no dominated predecessors", fBlock->index(), n->index());
- }
- Q_ASSERT(hasDominatedSucc);
- }
- }
- }
- }
-
- const BasicBlockSet &dominatorFrontier(BasicBlock *n) const {
- return DF[n->index()];
- }
-
- BasicBlock *immediateDominator(BasicBlock *bb) const {
- const BasicBlockIndex idx = idom[bb->index()];
- if (idx == -1)
- return 0;
- return function->basicBlock(idx);
- }
-
- void dumpImmediateDominators() const
- {
- if (DebugImmediateDominators) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Immediate dominators:" << endl;
- for (BasicBlock *to : function->basicBlocks()) {
- if (to->isRemoved())
- continue;
-
- qout << '\t';
- BasicBlockIndex from = idom.at(to->index());
- if (from != InvalidBasicBlockIndex)
- qout << from;
- else
- qout << "(none)";
- qout << " dominates " << to->index() << endl;
- }
- qDebug("%s", buf.data().constData());
- }
- }
-
- void setImmediateDominator(BasicBlock *bb, BasicBlock *newDominator)
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(!newDominator || newDominator->index() >= 0);
-
- if (static_cast<std::vector<BasicBlockIndex>::size_type>(bb->index()) >= idom.size()) {
- // This is a new block, probably introduced by edge splitting. So, we'll have to grow
- // the array before inserting the immediate dominator.
- idom.resize(function->basicBlockCount(), InvalidBasicBlockIndex);
- }
-
- const BasicBlockIndex newIdx = newDominator ? newDominator->index() : InvalidBasicBlockIndex;
- if (DebugImmediateDominators)
- qDebug() << "Setting idom of" << bb->index() << "from" << idom[bb->index()] << "to" << newIdx;
- idom[bb->index()] = newIdx;
- }
-
- void collectSiblings(BasicBlock *node, BasicBlockSet &siblings)
- {
- siblings.insert(node);
- const BasicBlockIndex dominator = idom[node->index()];
- if (dominator == InvalidBasicBlockIndex)
- return;
- for (size_t i = 0, ei = idom.size(); i != ei; ++i) {
- if (idom[i] == dominator) {
- BasicBlock *bb = function->basicBlock(int(i));
- if (!bb->isRemoved())
- siblings.insert(bb);
- }
- }
- }
-
- void recalculateIDoms(const BasicBlockSet &nodes, BasicBlock *limit = 0)
- {
- const BasicBlockIndex limitIndex = limit ? limit->index() : InvalidBasicBlockIndex;
- BasicBlockSet todo(nodes), postponed(function);
- while (!todo.empty())
- recalculateIDom(*todo.begin(), todo, postponed, limitIndex);
- }
-
- bool dominates(BasicBlock *dominator, BasicBlock *dominated) const {
- return dominates(dominator->index(), dominated->index());
- }
-
- struct Cmp {
- std::vector<int> *nodeDepths;
- Cmp(std::vector<int> *nodeDepths)
- : nodeDepths(nodeDepths)
- { Q_ASSERT(nodeDepths); }
- bool operator()(BasicBlock *one, BasicBlock *two) const
- {
- if (one->isRemoved())
- return false;
- if (two->isRemoved())
- return true;
- return nodeDepths->at(one->index()) > nodeDepths->at(two->index());
- }
- };
-
- // Calculate a depth-first iteration order on the nodes of the dominator tree.
- //
- // The order of the nodes in the vector is not the same as one where a recursive depth-first
- // iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth.
- // So for the:
- // 1 dominates 2
- // 2 dominates 3
- // 3 dominates 4
- // 2 dominates 5
- // the order will be:
- // 4, 3, 5, 2, 1
- // or:
- // 4, 5, 3, 2, 1
- // So the order of nodes on the same depth is undefined, but it will be after the nodes
- // they dominate, and before the nodes that dominate them.
- //
- // The reason for this order is that a proper DFS pre-/post-order would require inverting
- // the idom vector by either building a real tree datastructure or by searching the idoms
- // for siblings and children. Both have a higher time complexity than sorting by depth.
- QVector<BasicBlock *> calculateDFNodeIterOrder() const
- {
- std::vector<int> depths = calculateNodeDepths();
- QVector<BasicBlock *> order = function->basicBlocks();
- std::sort(order.begin(), order.end(), Cmp(&depths));
- for (int i = 0; i < order.size(); ) {
- if (order[i]->isRemoved())
- order.remove(i);
- else
- ++i;
- }
- return order;
- }
-
- void mergeIntoPredecessor(BasicBlock *successor)
- {
- int succIdx = successor->index();
- if (succIdx == InvalidBasicBlockIndex) {
- return;
- }
-
- int succDom = idom[unsigned(succIdx)];
- for (BasicBlockIndex &idx : idom) {
- if (idx == succIdx) {
- idx = succDom;
- }
- }
- }
-
-private:
- bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const {
- // dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
- Q_ASSERT(dominated != InvalidBasicBlockIndex);
-
- if (dominator == dominated)
- return false;
-
- for (BasicBlockIndex it = idom[dominated]; it != InvalidBasicBlockIndex; it = idom[it]) {
- if (it == dominator)
- return true;
- }
-
- return false;
- }
-
- // Algorithm:
- // - for each node:
- // - get the depth of a node. If it's unknown (-1):
- // - get the depth of the immediate dominator.
- // - if that's unknown too, calculate it by calling calculateNodeDepth
- // - set the current node's depth to that of immediate dominator + 1
- std::vector<int> calculateNodeDepths() const
- {
- std::vector<int> nodeDepths(size_t(function->basicBlockCount()), -1);
- nodeDepths[0] = 0;
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- int &bbDepth = nodeDepths[bb->index()];
- if (bbDepth == -1) {
- const int immDom = idom[bb->index()];
- int immDomDepth = nodeDepths[immDom];
- if (immDomDepth == -1)
- immDomDepth = calculateNodeDepth(immDom, nodeDepths);
- bbDepth = immDomDepth + 1;
- }
- }
- return nodeDepths;
- }
-
- // Algorithm:
- // - search for the first dominator of a node that has a known depth. As all nodes are
- // reachable from the start node, and that node's depth is 0, this is finite.
- // - while doing that search, put all unknown nodes in the worklist
- // - pop all nodes from the worklist, and set their depth to the previous' (== dominating)
- // node's depth + 1
- // This way every node's depth is calculated once, and the complexity is O(n).
- int calculateNodeDepth(int nodeIdx, std::vector<int> &nodeDepths) const
- {
- std::vector<int> worklist;
- worklist.reserve(8);
- int depth = -1;
-
- do {
- worklist.push_back(nodeIdx);
- nodeIdx = idom[nodeIdx];
- depth = nodeDepths[nodeIdx];
- } while (depth == -1);
-
- for (std::vector<int>::const_reverse_iterator it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it)
- nodeDepths[*it] = ++depth;
-
- return depth;
- }
-
- // The immediate-dominator recalculation is used when edges are removed from the CFG. See
- // [Ramalingam] for a description. Note that instead of calculating the priority, a recursive
- // algorithm is used: when recalculating the immediate dominator of a node by looking for the
- // least-common ancestor, and a node is hit that also needs recalculation, a recursive call
- // is done to calculate that nodes immediate dominator first.
- //
- // Note that this simplified algorithm cannot cope with back-edges. It only works for
- // non-looping edges (which is our use-case).
- void recalculateIDom(BasicBlock *node, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit) {
- Q_ASSERT(!postponed.contains(node));
- Q_ASSERT(todo.contains(node));
- todo.remove(node);
-
- if (node->in.size() == 1) {
- // Special case: if the node has only one incoming edge, then that is the immediate
- // dominator.
- setImmediateDominator(node, node->in.first());
- return;
- }
-
- std::vector<BasicBlockIndex> prefix;
- prefix.reserve(32);
-
- for (BasicBlock *in : node->in) {
- if (node == in) // back-edge to self
- continue;
- if (dominates(node->index(), in->index())) // a known back-edge
- continue;
-
- if (prefix.empty()) {
- calculatePrefix(node, in, prefix, todo, postponed, limit);
-
- if (!prefix.empty()) {
- std::reverse(prefix.begin(), prefix.end());
- Q_ASSERT(!prefix.empty());
- Q_ASSERT(prefix.front() == limit || limit == InvalidBasicBlockIndex);
- }
- } else {
- std::vector<BasicBlockIndex> anotherPrefix;
- anotherPrefix.reserve(prefix.size());
- calculatePrefix(node, in, anotherPrefix, todo, postponed, limit);
-
- if (!anotherPrefix.empty())
- commonPrefix(prefix, anotherPrefix);
- }
- }
-
- Q_ASSERT(!prefix.empty());
- idom[node->index()] = prefix.back();
- }
-
- void calculatePrefix(BasicBlock *node, BasicBlock *in, std::vector<BasicBlockIndex> &prefix, BasicBlockSet &todo, BasicBlockSet &postponed, BasicBlockIndex limit)
- {
- for (BasicBlockIndex it = in->index(); it != InvalidBasicBlockIndex; it = idom[it]) {
- prefix.push_back(it);
- if (it == limit)
- return;
- BasicBlock *n = function->basicBlock(it);
- if (postponed.contains(n)) { // possible back-edge, bail out.
- prefix.clear();
- return;
- }
- if (todo.contains(n)) {
- postponed.insert(node);
- recalculateIDom(n, todo, postponed, limit);
- postponed.remove(node);
- }
- }
- }
-
- // Calculate the LCA (Least Common Ancestor) by finding the longest common prefix between two
- // dominator chains. Note that "anotherPrefix" has the node's immediate dominator first, while
- // "bestPrefix" has it last (meaning: is in reverse order). The reason for this is that removing
- // nodes from "bestPrefix" is cheaper because it's done at the end of the vector, while
- // reversing all "anotherPrefix" nodes would take unnecessary time.
- static void commonPrefix(std::vector<BasicBlockIndex> &bestPrefix, const std::vector<BasicBlockIndex> &anotherPrefix)
- {
- const size_t anotherSize = anotherPrefix.size();
- size_t minLen = qMin(bestPrefix.size(), anotherPrefix.size());
- while (minLen != 0) {
- --minLen;
- if (bestPrefix[minLen] == anotherPrefix[anotherSize - minLen - 1]) {
- ++minLen;
- break;
- }
- }
- if (minLen != bestPrefix.size())
- bestPrefix.erase(bestPrefix.begin() + minLen, bestPrefix.end());
- }
-};
-
-class VariableCollector {
- std::vector<Temp> _allTemps;
- std::vector<BasicBlockSet> _defsites;
- std::vector<std::vector<int> > A_orig;
- BitVector nonLocals;
- BitVector killed;
-
- BasicBlock *currentBB;
- bool isCollectable(Temp *t) const
- {
- Q_UNUSED(t);
- Q_ASSERT(t->kind != Temp::PhysicalRegister && t->kind != Temp::StackSlot);
- return true;
- }
-
- void addDefInCurrentBlock(Temp *t)
- {
- std::vector<int> &temps = A_orig[currentBB->index()];
- if (std::find(temps.begin(), temps.end(), t->index) == temps.end())
- temps.push_back(t->index);
- }
-
- void addTemp(Temp *t)
- {
- if (_allTemps[t->index].kind == Temp::Invalid)
- _allTemps[t->index] = *t;
- }
-
-public:
- VariableCollector(IR::Function *function)
- {
- _allTemps.resize(function->tempCount);
- _defsites.resize(function->tempCount);
- for (int i = 0; i < function->tempCount; ++i)
- _defsites[i].init(function);
- nonLocals.resize(function->tempCount);
- const size_t ei = function->basicBlockCount();
- A_orig.resize(ei);
- for (size_t i = 0; i != ei; ++i)
- A_orig[i].reserve(8);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- currentBB = bb;
- killed.assign(function->tempCount, false);
- for (Stmt *s : bb->statements())
- visit(s);
- }
- }
-
- const std::vector<Temp> &allTemps() const
- { return _allTemps; }
-
- void collectDefSites(const Temp &n, std::vector<BasicBlock *> &bbs) const {
- Q_ASSERT(!n.isInvalid());
- Q_ASSERT(n.index < _defsites.size());
- _defsites[n.index].collectValues(bbs);
- }
-
- const std::vector<int> &inBlock(BasicBlock *n) const
- {
- return A_orig.at(n->index());
- }
-
- bool isNonLocal(const Temp &var) const
- {
- Q_ASSERT(!var.isInvalid());
- Q_ASSERT(static_cast<int>(var.index) < nonLocals.size());
- return nonLocals.at(var.index);
- }
-
-private:
- void visit(Stmt *s)
- {
- if (s->asPhi()) {
- // nothing to do
- } else if (auto move = s->asMove()) {
- visit(move->source);
-
- if (Temp *t = move->target->asTemp()) {
- addTemp(t);
-
- if (isCollectable(t)) {
- _defsites[t->index].insert(currentBB);
- addDefInCurrentBlock(t);
-
- // For semi-pruned SSA:
- killed.setBit(t->index);
- }
- } else {
- visit(move->target);
- }
- } else {
- STMT_VISIT_ALL_KINDS(s)
- }
- }
-
- void visit(Expr *e)
- {
- if (auto t = e->asTemp()) {
- addTemp(t);
-
- if (isCollectable(t)) {
- if (!killed.at(t->index)) {
- nonLocals.setBit(t->index);
- }
- }
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-
-struct UntypedTemp {
- Temp temp;
- UntypedTemp() {}
- UntypedTemp(const Temp &t): temp(t) {}
-};
-inline bool operator==(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW
-{ return t1.temp.index == t2.temp.index && t1.temp.kind == t2.temp.kind; }
-inline bool operator!=(const UntypedTemp &t1, const UntypedTemp &t2) Q_DECL_NOTHROW
-{ return !(t1 == t2); }
-
-class DefUses
-{
-public:
- struct DefUse {
- DefUse()
- : defStmt(0)
- , blockOfStatement(0)
- { uses.reserve(8); }
- Temp temp;
- Stmt *defStmt;
- BasicBlock *blockOfStatement;
- QVector<Stmt *> uses;
-
- bool isValid() const
- { return temp.kind != Temp::Invalid; }
-
- void clear()
- { defStmt = 0; blockOfStatement = 0; uses.clear(); }
- };
-
-private:
- std::vector<DefUse> _defUses;
- typedef QVarLengthArray<Temp, 4> Temps;
- std::vector<Temps> _usesPerStatement;
-
- void ensure(Temp *newTemp)
- {
- if (_defUses.size() <= newTemp->index) {
- _defUses.resize(newTemp->index + 1);
- }
- }
-
- void ensure(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- if (static_cast<unsigned>(s->id()) >= _usesPerStatement.size()) {
- _usesPerStatement.resize(s->id() + 1);
- }
- }
-
- void addUseForStatement(Stmt *s, const Temp &var)
- {
- ensure(s);
- _usesPerStatement[s->id()].push_back(var);
- }
-
-public:
- DefUses(IR::Function *function)
- {
- _usesPerStatement.resize(function->statementCount());
- _defUses.resize(function->tempCount);
- }
-
- void cleanup()
- {
- for (size_t i = 0, ei = _defUses.size(); i != ei; ++i) {
- DefUse &defUse = _defUses[i];
- if (defUse.isValid() && !defUse.defStmt)
- defUse.clear();
- }
- }
-
- unsigned statementCount() const
- { return unsigned(_usesPerStatement.size()); }
-
- unsigned tempCount() const
- { return unsigned(_defUses.size()); }
-
- const Temp &temp(int idx) const
- { return _defUses[idx].temp; }
-
- void addDef(Temp *newTemp, Stmt *defStmt, BasicBlock *defBlock)
- {
- ensure(newTemp);
- DefUse &defUse = _defUses[newTemp->index];
- Q_ASSERT(!defUse.isValid());
- defUse.temp = *newTemp;
- defUse.defStmt = defStmt;
- defUse.blockOfStatement = defBlock;
- }
-
- QVector<UntypedTemp> defsUntyped() const
- {
- QVector<UntypedTemp> res;
- res.reserve(tempCount());
- for (const DefUse &du : _defUses)
- if (du.isValid())
- res.append(UntypedTemp(du.temp));
- return res;
- }
-
- std::vector<const Temp *> defs() const {
- std::vector<const Temp *> res;
- const size_t ei = _defUses.size();
- res.reserve(ei);
- for (size_t i = 0; i != ei; ++i) {
- const DefUse &du = _defUses.at(i);
- if (du.isValid())
- res.push_back(&du.temp);
- }
- return res;
- }
-
- void removeDef(const Temp &variable) {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- _defUses[variable.index].clear();
- }
-
- void addUses(const Temp &variable, const QVector<Stmt *> &newUses)
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- QVector<Stmt *> &uses = _defUses[variable.index].uses;
- for (Stmt *stmt : newUses)
- if (std::find(uses.begin(), uses.end(), stmt) == uses.end())
- uses.push_back(stmt);
- }
-
- void addUse(const Temp &variable, Stmt *newUse)
- {
- if (_defUses.size() <= variable.index) {
- _defUses.resize(variable.index + 1);
- DefUse &du = _defUses[variable.index];
- du.temp = variable;
- du.uses.push_back(newUse);
- addUseForStatement(newUse, variable);
- return;
- }
-
- QVector<Stmt *> &uses = _defUses[variable.index].uses;
- if (std::find(uses.begin(), uses.end(), newUse) == uses.end())
- uses.push_back(newUse);
- addUseForStatement(newUse, variable);
- }
-
- int useCount(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].uses.size();
- }
-
- Stmt *defStmt(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].defStmt;
- }
-
- BasicBlock *defStmtBlock(const Temp &variable) const
- {
- Q_ASSERT(static_cast<unsigned>(variable.index) < _defUses.size());
- return _defUses[variable.index].blockOfStatement;
- }
-
- void replaceBasicBlock(BasicBlock *from, BasicBlock *to)
- {
- for (auto &du : _defUses) {
- if (du.blockOfStatement == from) {
- du.blockOfStatement = to;
- }
- }
- }
-
- void removeUse(Stmt *usingStmt, const Temp &var)
- {
- Q_ASSERT(static_cast<unsigned>(var.index) < _defUses.size());
- QVector<Stmt *> &uses = _defUses[var.index].uses;
- uses.erase(std::remove(uses.begin(), uses.end(), usingStmt), uses.end());
- }
-
- void registerNewStatement(Stmt *s)
- {
- ensure(s);
- }
-
- const Temps &usedVars(Stmt *s) const
- {
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(static_cast<unsigned>(s->id()) < _usesPerStatement.size());
- return _usesPerStatement[s->id()];
- }
-
- const QVector<Stmt *> &uses(const Temp &var) const
- {
- return _defUses[var.index].uses;
- }
-
- QVector<Stmt*> removeDefUses(Stmt *s)
- {
- QVector<Stmt*> defStmts;
- for (const Temp &usedVar : usedVars(s)) {
- if (Stmt *ds = defStmt(usedVar))
- defStmts += ds;
- removeUse(s, usedVar);
- }
- if (Move *m = s->asMove()) {
- if (Temp *t = m->target->asTemp())
- removeDef(*t);
- } else if (Phi *p = s->asPhi()) {
- removeDef(*p->targetTemp);
- }
-
- return defStmts;
- }
-
- void dump() const
- {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Defines and uses:" << endl;
- for (const DefUse &du : _defUses) {
- if (!du.isValid())
- continue;
- qout << '%' << du.temp.index;
- qout << " -> defined in block " << du.blockOfStatement->index()
- << ", statement: " << du.defStmt->id()
- << endl;
- qout << " uses:";
- for (Stmt *s : du.uses)
- qout << ' ' << s->id();
- qout << endl;
- }
- qout << "Uses per statement:" << endl;
- for (size_t i = 0, ei = _usesPerStatement.size(); i != ei; ++i) {
- qout << " " << i << ":";
- for (const Temp &t : _usesPerStatement[i])
- qout << ' ' << t.index;
- qout << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-};
-
-void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) {
- Phi *phiNode = f->NewStmt<Phi>();
- phiNode->targetTemp = f->New<Temp>();
- phiNode->targetTemp->init(a.kind, a.index);
- y->prependStatement(phiNode);
-
- phiNode->incoming.resize(y->in.size());
- for (int i = 0, ei = y->in.size(); i < ei; ++i) {
- Temp *t = f->New<Temp>();
- t->init(a.kind, a.index);
- phiNode->incoming[i] = t;
- }
-}
-
-// High-level (recursive) algorithm:
-// Mapping: old temp number -> new temp number
-//
-// Start:
-// Rename(start-node)
-//
-// Rename(node, mapping):
-// for each statement S in block n
-// if S not in a phi-function
-// for each use of some variable x in S
-// y = mapping[x]
-// replace the use of x with y in S
-// for each definition of some variable a in S [1]
-// a_new = generate new/unique temp
-// mapping[a] = a_new
-// replace definition of a with definition of a_new in S
-// for each successor Y of block n
-// Suppose n is the j-th predecessor of Y
-// for each phi function in Y
-// suppose the j-th operand of the phi-function is a
-// i = mapping[a]
-// replace the j-th operand with a_i
-// for each child X of n [2]
-// Rename(X)
-// for each newly generated temp from step [1] restore the old value [3]
-//
-// This algorithm can run out of CPU stack space when there are lots of basic-blocks, like in a
-// switch statement with 8000 cases that all fall-through. The iterativer version below uses a
-// work-item stack, where step [1] from the algorithm above also pushes an "undo mapping change",
-// and step [2] pushes a "rename(X)" action. This eliminates step [3].
-//
-// Iterative version:
-// Mapping: old temp number -> new temp number
-//
-// The stack can hold two kinds of actions:
-// "Rename basic block n"
-// "Restore count for temp"
-//
-// Start:
-// counter = 0
-// push "Rename start node" onto the stack
-// while the stack is not empty:
-// take the last item, and process it
-//
-// Rename(n) =
-// for each statement S in block n
-// if S not in a phi-function
-// for each use of some variable x in S
-// y = mapping[x]
-// replace the use of x with y in S
-// for each definition of some variable a in S
-// old = mapping[a]
-// push Undo(a, old)
-// counter = counter + 1
-// new = counter;
-// mapping[a] = new
-// replace definition of a with definition of a_new in S
-// for each successor Y of block n
-// Suppose n is the j-th predecessor of Y
-// for each phi function in Y
-// suppose the j-th operand of the phi-function is a
-// i = mapping[a]
-// replace the j-th operand with a_i
-// for each child X of n
-// push Rename(X)
-//
-// Undo(t, c) =
-// mapping[t] = c
-class VariableRenamer
-{
- Q_DISABLE_COPY(VariableRenamer)
-
- IR::Function *function;
- DefUses &defUses;
- unsigned tempCount;
-
- typedef std::vector<int> Mapping; // maps from existing/old temp number to the new and unique temp number.
- enum { Absent = -1 };
- Mapping vregMapping;
- ProcessedBlocks processed;
-
- BasicBlock *currentBB;
- Stmt *currentStmt;
-
- struct TodoAction {
- enum { RestoreVReg, Rename } action;
- union {
- struct {
- unsigned temp;
- int previous;
- } restoreData;
- struct {
- BasicBlock *basicBlock;
- } renameData;
- };
-
- bool isValid() const { return action != Rename || renameData.basicBlock != 0; }
-
- TodoAction()
- {
- action = Rename;
- renameData.basicBlock = 0;
- }
-
- TodoAction(const Temp &t, int prev)
- {
- Q_ASSERT(t.kind == Temp::VirtualRegister);
-
- action = RestoreVReg;
- restoreData.temp = t.index;
- restoreData.previous = prev;
- }
-
- TodoAction(BasicBlock *bb)
- {
- Q_ASSERT(bb);
-
- action = Rename;
- renameData.basicBlock = bb;
- }
- };
-
- QVector<TodoAction> todo;
-
-public:
- VariableRenamer(IR::Function *f, DefUses &defUses)
- : function(f)
- , defUses(defUses)
- , tempCount(0)
- , processed(f)
- {
- vregMapping.assign(f->tempCount, Absent);
- todo.reserve(f->basicBlockCount());
- }
-
- void run() {
- todo.append(TodoAction(function->basicBlock(0)));
-
- while (!todo.isEmpty()) {
- TodoAction todoAction = todo.back();
- Q_ASSERT(todoAction.isValid());
- todo.pop_back();
-
- switch (todoAction.action) {
- case TodoAction::Rename:
- rename(todoAction.renameData.basicBlock);
- break;
- case TodoAction::RestoreVReg:
- restore(vregMapping, todoAction.restoreData.temp, todoAction.restoreData.previous);
- break;
- default:
- Q_UNREACHABLE();
- }
- }
-
- function->tempCount = tempCount;
- }
-
-private:
- static inline void restore(Mapping &mapping, int temp, int previous)
- {
- mapping[temp] = previous;
- }
-
- void rename(BasicBlock *bb)
- {
- while (bb && !processed.alreadyProcessed(bb)) {
- renameStatementsAndPhis(bb);
- processed.markAsProcessed(bb);
-
- BasicBlock *next = 0;
- for (BasicBlock *out : bb->out) {
- if (processed.alreadyProcessed(out))
- continue;
- if (!next)
- next = out;
- else
- todo.append(TodoAction(out));
- }
- bb = next;
- }
- }
-
- void renameStatementsAndPhis(BasicBlock *bb)
- {
- currentBB = bb;
-
- for (Stmt *s : bb->statements()) {
- currentStmt = s;
- visit(s);
- }
-
- for (BasicBlock *Y : bb->out) {
- const int j = Y->in.indexOf(bb);
- Q_ASSERT(j >= 0 && j < Y->in.size());
- for (Stmt *s : Y->statements()) {
- if (Phi *phi = s->asPhi()) {
- Temp *t = phi->incoming[j]->asTemp();
- unsigned newTmp = currentNumber(*t);
-// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index;
- t->index = newTmp;
- t->kind = Temp::VirtualRegister;
- defUses.addUse(*t, phi);
- } else {
- break;
- }
- }
- }
- }
-
- unsigned currentNumber(const Temp &t)
- {
- int nr = Absent;
- switch (t.kind) {
- case Temp::VirtualRegister:
- nr = vregMapping[t.index];
- break;
- default:
- Q_UNREACHABLE();
- nr = Absent;
- break;
- }
- if (nr == Absent) {
- // Special case: we didn't prune the Phi nodes yet, so for proper temps (virtual
- // registers) the SSA algorithm might insert superfluous Phis that have uses without
- // definition. E.g.: if a temporary got introduced in the "then" clause, it "could"
- // reach the "end-if" block, so there will be a phi node for that temp. A later pass
- // will clean this up by looking for uses-without-defines in phi nodes. So, what we do
- // is to generate a new unique number, and leave it dangling.
- nr = nextFreeTemp(t);
- }
-
- return nr;
- }
-
- unsigned nextFreeTemp(const Temp &t)
- {
- unsigned newIndex = tempCount++;
- Q_ASSERT(newIndex <= INT_MAX);
- int oldIndex = Absent;
-
- switch (t.kind) {
- case Temp::VirtualRegister:
- oldIndex = vregMapping[t.index];
- vregMapping[t.index] = newIndex;
- break;
- default:
- Q_UNREACHABLE();
- }
-
- todo.append(TodoAction(t, oldIndex));
-
- return newIndex;
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto move = s->asMove()) {
- // uses:
- visit(move->source);
-
- // defs:
- if (Temp *t = move->target->asTemp()) {
- renameTemp(t);
- } else {
- visit(move->target);
- }
- } else if (auto phi = s->asPhi()) {
- renameTemp(phi->targetTemp);
- } else {
- STMT_VISIT_ALL_KINDS(s);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto temp = e->asTemp()) {
- temp->index = currentNumber(*temp);
- temp->kind = Temp::VirtualRegister;
- defUses.addUse(*temp, currentStmt);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-
- void renameTemp(Temp *t) { // only called for defs, not uses
- const int newIdx = nextFreeTemp(*t);
-// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx;
- t->kind = Temp::VirtualRegister;
- t->index = newIdx;
- defUses.addDef(t, currentStmt, currentBB);
- }
-};
-
-// This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm,
-// see [Appel]. For the changes needed for semi-pruned SSA form, and for its advantages, see [Briggs].
-void convertToSSA(IR::Function *function, const DominatorTree &df, DefUses &defUses)
-{
- // Collect all applicable variables:
- VariableCollector variables(function);
-
- // Prepare for phi node insertion:
- std::vector<BitVector > A_phi;
- const size_t ei = function->basicBlockCount();
- A_phi.resize(ei);
- for (size_t i = 0; i != ei; ++i)
- A_phi[i].assign(function->tempCount, false);
-
- std::vector<BasicBlock *> W;
- W.reserve(8);
-
- // Place phi functions:
- for (const Temp &a : variables.allTemps()) {
- if (a.isInvalid())
- continue;
- if (!variables.isNonLocal(a))
- continue; // for semi-pruned SSA
-
- W.clear();
- variables.collectDefSites(a, W);
- while (!W.empty()) {
- BasicBlock *n = W.back();
- W.pop_back();
- const BasicBlockSet &dominatorFrontierForN = df.dominatorFrontier(n);
- for (BasicBlockSet::const_iterator it = dominatorFrontierForN.begin(), eit = dominatorFrontierForN.end();
- it != eit; ++it) {
- BasicBlock *y = *it;
- if (!A_phi.at(y->index()).at(a.index)) {
- insertPhiNode(a, y, function);
- A_phi[y->index()].setBit(a.index);
- const std::vector<int> &varsInBlockY = variables.inBlock(y);
- if (std::find(varsInBlockY.begin(), varsInBlockY.end(), a.index) == varsInBlockY.end())
- W.push_back(y);
- }
- }
- }
- }
-
- // Rename variables:
- VariableRenamer(function, defUses).run();
-}
-
-/// Calculate if a phi node result is used only by other phi nodes, and if those uses are
-/// in turn also used by other phi nodes.
-bool hasPhiOnlyUses(Phi *phi, const DefUses &defUses, QBitArray &collectedPhis)
-{
- collectedPhis.setBit(phi->id());
-
- for (Stmt *use : defUses.uses(*phi->targetTemp)) {
- Phi *dependentPhi = use->asPhi();
- if (!dependentPhi)
- return false; // there is a use by a non-phi node
-
- if (collectedPhis.at(dependentPhi->id()))
- continue; // we already found this node
-
- if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis))
- return false;
- }
-
- return true;
-}
-
-void cleanupPhis(DefUses &defUses)
-{
- QBitArray toRemove(defUses.statementCount());
- QBitArray collectedPhis(defUses.statementCount());
- std::vector<Phi *> allPhis;
- allPhis.reserve(32);
-
- for (const Temp *def : defUses.defs()) {
- Stmt *defStmt = defUses.defStmt(*def);
- if (!defStmt)
- continue;
-
- Phi *phi = defStmt->asPhi();
- if (!phi)
- continue;
- allPhis.push_back(phi);
- if (toRemove.at(phi->id()))
- continue;
-
- collectedPhis.fill(false);
- if (hasPhiOnlyUses(phi, defUses, collectedPhis))
- toRemove |= collectedPhis;
- }
-
- for (Phi *phi : allPhis) {
- if (!toRemove.at(phi->id()))
- continue;
-
- const Temp &targetVar = *phi->targetTemp;
- defUses.defStmtBlock(targetVar)->removeStatement(phi);
-
- for (const Temp &usedVar : defUses.usedVars(phi))
- defUses.removeUse(phi, usedVar);
- defUses.removeDef(targetVar);
- }
-
- defUses.cleanup();
-}
-
-class StatementWorklist
-{
- IR::Function *theFunction;
- std::vector<Stmt *> stmts;
- BitVector worklist;
- unsigned worklistSize;
- std::vector<int> replaced;
- BitVector removed;
-
- Q_DISABLE_COPY(StatementWorklist)
-
-public:
- StatementWorklist(IR::Function *function)
- : theFunction(function)
- , stmts(function->statementCount(), static_cast<Stmt *>(0))
- , worklist(function->statementCount(), false)
- , worklistSize(0)
- , replaced(function->statementCount(), Stmt::InvalidId)
- , removed(function->statementCount())
- {
- grow();
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- if (!s)
- continue;
-
- stmts[s->id()] = s;
- worklist.setBit(s->id());
- ++worklistSize;
- }
- }
- }
-
- void reset()
- {
- worklist.assign(worklist.size(), false);
- worklistSize = 0;
-
- for (Stmt *s : stmts) {
- if (!s)
- continue;
-
- worklist.setBit(s->id());
- ++worklistSize;
- }
-
- replaced.assign(replaced.size(), Stmt::InvalidId);
- removed.assign(removed.size(), false);
- }
-
- void remove(Stmt *stmt)
- {
- replaced[stmt->id()] = Stmt::InvalidId;
- removed.setBit(stmt->id());
- if (worklist.at(stmt->id())) {
- worklist.clearBit(stmt->id());
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- }
- }
-
- void replace(Stmt *oldStmt, Stmt *newStmt)
- {
- Q_ASSERT(oldStmt);
- Q_ASSERT(replaced[oldStmt->id()] == Stmt::InvalidId);
- Q_ASSERT(removed.at(oldStmt->id()) == false);
-
- Q_ASSERT(newStmt);
- registerNewStatement(newStmt);
- Q_ASSERT(replaced[newStmt->id()] == Stmt::InvalidId);
- Q_ASSERT(removed.at(newStmt->id()) == false);
-
- replaced[oldStmt->id()] = newStmt->id();
- worklist.clearBit(oldStmt->id());
- }
-
- void applyToFunction()
- {
- for (BasicBlock *bb : theFunction->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (int i = 0; i < bb->statementCount();) {
- Stmt *stmt = bb->statements().at(i);
-
- int id = stmt->id();
- Q_ASSERT(id != Stmt::InvalidId);
- Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size());
-
- for (int replacementId = replaced[id]; replacementId != Stmt::InvalidId; replacementId = replaced[replacementId])
- id = replacementId;
- Q_ASSERT(id != Stmt::InvalidId);
- Q_ASSERT(static_cast<unsigned>(stmt->id()) < stmts.size());
-
- if (removed.at(id)) {
- bb->removeStatement(i);
- } else {
- if (id != stmt->id())
- bb->replaceStatement(i, stmts[id]);
-
- ++i;
- }
- }
- }
-
- replaced.assign(replaced.size(), Stmt::InvalidId);
- removed.assign(removed.size(), false);
- }
-
- StatementWorklist &operator+=(const QVector<Stmt *> &stmts)
- {
- for (Stmt *s : stmts)
- this->operator+=(s);
-
- return *this;
- }
-
- StatementWorklist &operator+=(Stmt *s)
- {
- if (!s)
- return *this;
-
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(s->id() < worklist.size());
-
- if (!worklist.at(s->id())) {
- worklist.setBit(s->id());
- ++worklistSize;
- }
-
- return *this;
- }
-
- StatementWorklist &operator-=(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- Q_ASSERT(s->id() < worklist.size());
-
- if (worklist.at(s->id())) {
- worklist.clearBit(s->id());
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- }
-
- return *this;
- }
-
- unsigned size() const
- {
- return worklistSize;
- }
-
- Stmt *takeNext(Stmt *last)
- {
- if (worklistSize == 0)
- return 0;
-
- const int startAt = last ? last->id() + 1 : 0;
- Q_ASSERT(startAt >= 0);
- Q_ASSERT(startAt <= worklist.size());
-
- Q_ASSERT(static_cast<size_t>(worklist.size()) == stmts.size());
-
- int pos = worklist.findNext(startAt, true, /*wrapAround = */true);
-
- worklist.clearBit(pos);
- Q_ASSERT(worklistSize > 0);
- --worklistSize;
- Stmt *s = stmts.at(pos);
- Q_ASSERT(s);
-
- if (removed.at(s->id()))
- return takeNext(s);
-
- return s;
- }
-
- IR::Function *function() const
- {
- return theFunction;
- }
-
- void registerNewStatement(Stmt *s)
- {
- Q_ASSERT(s->id() >= 0);
- if (static_cast<unsigned>(s->id()) >= stmts.size()) {
- if (static_cast<unsigned>(s->id()) >= stmts.capacity())
- grow();
-
- int newSize = s->id() + 1;
- stmts.resize(newSize, 0);
- worklist.resize(newSize);
- replaced.resize(newSize, Stmt::InvalidId);
- removed.resize(newSize);
- }
-
- stmts[s->id()] = s;
- }
-
-private:
- void grow()
- {
- Q_ASSERT(stmts.capacity() < INT_MAX / 2);
- int newCapacity = ((static_cast<int>(stmts.capacity()) + 1) * 3) / 2;
- stmts.reserve(newCapacity);
- worklist.reserve(newCapacity);
- replaced.reserve(newCapacity);
- removed.reserve(newCapacity);
- }
-};
-
-class SideEffectsChecker
-{
- bool _sideEffect;
-
-public:
- SideEffectsChecker()
- : _sideEffect(false)
- {}
-
- ~SideEffectsChecker()
- {}
-
- bool hasSideEffects(Expr *expr)
- {
- bool sideEffect = false;
- qSwap(_sideEffect, sideEffect);
- visit(expr);
- qSwap(_sideEffect, sideEffect);
- return sideEffect;
- }
-
-protected:
- void markAsSideEffect()
- {
- _sideEffect = true;
- }
-
- bool seenSideEffects() const { return _sideEffect; }
-
- void visit(Expr *e)
- {
- if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- }
- }
-
- virtual void visitTemp(Temp *) {}
-
-private:
- void visitName(Name *e) {
- if (e->freeOfSideEffects)
- return;
- // TODO: maybe we can distinguish between built-ins of which we know that they do not have
- // a side-effect.
- if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QLatin1String("this")))
- markAsSideEffect();
- }
-
- void visitClosure(Closure *) {
- markAsSideEffect();
- }
-
- void visitConvert(Convert *e) {
- visit(e->expr);
-
- switch (e->expr->type) {
- case QObjectType:
- case StringType:
- case VarType:
- markAsSideEffect();
- break;
- default:
- break;
- }
- }
-
- void visitUnop(Unop *e) {
- visit(e->expr);
-
- switch (e->op) {
- case OpUPlus:
- case OpUMinus:
- case OpNot:
- case OpIncrement:
- case OpDecrement:
- if (e->expr->type == VarType || e->expr->type == StringType || e->expr->type == QObjectType)
- markAsSideEffect();
- break;
-
- default:
- break;
- }
- }
-
- void visitBinop(Binop *e) {
- // TODO: prune parts that don't have a side-effect. For example, in:
- // function f(x) { +x+1; return 0; }
- // we can prune the binop and leave the unop/conversion.
- _sideEffect = hasSideEffects(e->left);
- _sideEffect |= hasSideEffects(e->right);
-
- if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType
- || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType)
- markAsSideEffect();
- }
-
- void visitSubscript(Subscript *e) {
- visit(e->base);
- visit(e->index);
- markAsSideEffect();
- }
-
- void visitMember(Member *e) {
- visit(e->base);
- if (e->freeOfSideEffects)
- return;
- markAsSideEffect();
- }
-
- void visitCall(Call *e) {
- visit(e->base);
- for (ExprList *args = e->args; args; args = args->next)
- visit(args->expr);
- markAsSideEffect(); // TODO: there are built-in functions that have no side effect.
- }
-
- void visitNew(New *e) {
- visit(e->base);
- for (ExprList *args = e->args; args; args = args->next)
- visit(args->expr);
- markAsSideEffect(); // TODO: there are built-in types that have no side effect.
- }
-};
-
-class EliminateDeadCode: public SideEffectsChecker
-{
- DefUses &_defUses;
- StatementWorklist &_worklist;
- QVarLengthArray<Temp *, 8> _collectedTemps;
-
-public:
- EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist)
- : _defUses(defUses)
- , _worklist(worklist)
- {}
-
- void run(Expr *&expr, Stmt *stmt) {
- _collectedTemps.clear();
- if (!hasSideEffects(expr)) {
- expr = 0;
- for (Temp *t : _collectedTemps) {
- _defUses.removeUse(stmt, *t);
- _worklist += _defUses.defStmt(*t);
- }
- }
- }
-
-protected:
- void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL
- {
- _collectedTemps.append(e);
- }
-};
-
-class PropagateTempTypes
-{
- const DefUses &defUses;
- UntypedTemp theTemp;
- DiscoveredType newType;
-
-public:
- PropagateTempTypes(const DefUses &defUses)
- : defUses(defUses)
- {}
-
- void run(const UntypedTemp &temp, const DiscoveredType &type)
- {
- newType = type;
- theTemp = temp;
- if (Stmt *defStmt = defUses.defStmt(temp.temp))
- visit(defStmt);
- for (Stmt *use : defUses.uses(temp.temp))
- visit(use);
- }
-
-private:
- void visit(Stmt *s)
- {
- STMT_VISIT_ALL_KINDS(s);
- }
-
- void visit(Expr *e)
- {
- if (auto temp = e->asTemp()) {
- if (theTemp == UntypedTemp(*temp)) {
- temp->type = static_cast<Type>(newType.type);
- temp->memberResolver = newType.memberResolver;
- }
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-};
-
-class TypeInference
-{
- enum { DebugTypeInference = 0 };
-
- QQmlEnginePrivate *qmlEngine;
- const DefUses &_defUses;
- typedef std::vector<DiscoveredType> TempTypes;
- TempTypes _tempTypes;
- StatementWorklist *_worklist;
- struct TypingResult {
- DiscoveredType type;
- bool fullyTyped;
-
- TypingResult(const DiscoveredType &type = DiscoveredType()) {
-#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 6
- // avoid optimization bug in gcc 4.6.3 armhf
- ((int volatile &) this->type.type) = type.type;
-#endif
- this->type = type;
- fullyTyped = type.type != UnknownType;
- }
- explicit TypingResult(MemberExpressionResolver *memberResolver)
- : type(memberResolver)
- , fullyTyped(true)
- {}
- };
- TypingResult _ty;
- Stmt *_currentStmt;
-
-public:
- TypeInference(QQmlEnginePrivate *qmlEngine, const DefUses &defUses)
- : qmlEngine(qmlEngine)
- , _defUses(defUses)
- , _tempTypes(_defUses.tempCount())
- , _worklist(0)
- , _ty(UnknownType)
- , _currentStmt(nullptr)
- {}
-
- void run(StatementWorklist &w) {
- _worklist = &w;
-
- Stmt *s = 0;
- while ((s = _worklist->takeNext(s))) {
- if (s->asJump())
- continue;
-
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Typing stmt ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
-
- qDebug("%u left in the worklist", _worklist->size());
- }
-
- if (!run(s)) {
- *_worklist += s;
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Pushing back stmt: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- } else {
- if (DebugTypeInference) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout<<"Finished: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- }
- }
-
- PropagateTempTypes propagator(_defUses);
- for (size_t i = 0, ei = _tempTypes.size(); i != ei; ++i) {
- const Temp &temp = _defUses.temp(int(i));
- if (temp.kind == Temp::Invalid)
- continue;
- const DiscoveredType &tempType = _tempTypes[i];
- if (tempType.type == UnknownType)
- continue;
- propagator.run(temp, tempType);
- }
-
- _worklist = 0;
- }
-
-private:
- bool run(Stmt *s) {
- TypingResult ty;
- std::swap(_ty, ty);
- std::swap(_currentStmt, s);
- visit(_currentStmt);
- std::swap(_currentStmt, s);
- std::swap(_ty, ty);
- return ty.fullyTyped;
- }
-
- TypingResult run(Expr *e) {
- TypingResult ty;
- std::swap(_ty, ty);
- visit(e);
- std::swap(_ty, ty);
-
- if (ty.type != UnknownType)
- setType(e, ty.type);
- return ty;
- }
-
- void setType(Expr *e, DiscoveredType ty) {
- if (Temp *t = e->asTemp()) {
- if (DebugTypeInference)
- qDebug() << "Setting type for temp" << t->index
- << " to " << typeName(Type(ty.type)) << "(" << ty.type << ")"
- << endl;
-
- DiscoveredType &it = _tempTypes[t->index];
- if (it != ty) {
- it = ty;
-
- if (DebugTypeInference) {
- for (Stmt *s : _defUses.uses(*t)) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
- qout << "Pushing back dependent stmt: ";
- IRPrinter(&qout).print(s);
- qout.flush();
- qDebug("%s", buf.data().constData());
- }
- }
-
- for (Stmt *s : qAsConst(_defUses.uses(*t))) {
- if (s != _currentStmt) {
- *_worklist += s;
- }
- }
- }
- } else {
- e->type = (Type) ty.type;
- }
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitConst(Const *c) {
- if (c->type & NumberType) {
- if (canConvertToSignedInteger(c->value))
- _ty = TypingResult(SInt32Type);
- else if (canConvertToUnsignedInteger(c->value))
- _ty = TypingResult(UInt32Type);
- else
- _ty = TypingResult(c->type);
- } else
- _ty = TypingResult(c->type);
- }
- void visitString(IR::String *) { _ty = TypingResult(StringType); }
- void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); }
- void visitName(Name *) { _ty = TypingResult(VarType); }
- void visitTemp(Temp *e) {
- if (e->memberResolver && e->memberResolver->isValid())
- _ty = TypingResult(e->memberResolver);
- else
- _ty = TypingResult(_tempTypes[e->index]);
- setType(e, _ty.type);
- }
- void visitArgLocal(ArgLocal *e) {
- _ty = TypingResult(VarType);
- setType(e, _ty.type);
- }
-
- void visitClosure(Closure *) { _ty = TypingResult(VarType); }
- void visitConvert(Convert *e) {
- _ty = TypingResult(e->type);
- }
-
- void visitUnop(Unop *e) {
- _ty = run(e->expr);
- switch (e->op) {
- case OpUPlus: _ty.type = DoubleType; return;
- case OpUMinus: _ty.type = DoubleType; return;
- case OpCompl: _ty.type = SInt32Type; return;
- case OpNot: _ty.type = BoolType; return;
-
- case OpIncrement:
- case OpDecrement:
- Q_ASSERT(!"Inplace operators should have been removed!");
- Q_UNREACHABLE();
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void visitBinop(Binop *e) {
- TypingResult leftTy = run(e->left);
- TypingResult rightTy = run(e->right);
- _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped;
-
- switch (e->op) {
- case OpAdd:
- if (leftTy.type.test(VarType) || leftTy.type.test(QObjectType) || rightTy.type.test(VarType) || rightTy.type.test(QObjectType))
- _ty.type = VarType;
- else if (leftTy.type.test(StringType) || rightTy.type.test(StringType))
- _ty.type = StringType;
- else if (leftTy.type != UnknownType && rightTy.type != UnknownType)
- _ty.type = DoubleType;
- else
- _ty.type = UnknownType;
- break;
- case OpSub:
- _ty.type = DoubleType;
- break;
-
- case OpMul:
- case OpDiv:
- case OpMod:
- _ty.type = DoubleType;
- break;
-
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpLShift:
- case OpRShift:
- _ty.type = SInt32Type;
- break;
- case OpURShift:
- _ty.type = UInt32Type;
- break;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- case OpStrictEqual:
- case OpStrictNotEqual:
- case OpAnd:
- case OpOr:
- case OpInstanceof:
- case OpIn:
- _ty.type = BoolType;
- break;
-
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void visitCall(Call *e) {
- _ty = run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- _ty.fullyTyped &= run(it->expr).fullyTyped;
- _ty.type = VarType;
- }
- void visitNew(New *e) {
- _ty = run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- _ty.fullyTyped &= run(it->expr).fullyTyped;
- _ty.type = VarType;
- }
- void visitSubscript(Subscript *e) {
- _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped;
- _ty.type = VarType;
- }
-
- void visitMember(Member *e) {
- _ty = run(e->base);
-
- if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) {
- MemberExpressionResolver *resolver = _ty.type.memberResolver;
- _ty.type = resolver->resolveMember(qmlEngine, resolver, e);
- } else
- _ty.type = VarType;
- }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitExp(Exp *s) { _ty = run(s->expr); }
- void visitMove(Move *s) {
- if (Temp *t = s->target->asTemp()) {
- if (Name *n = s->source->asName()) {
- if (n->builtin == Name::builtin_qml_context) {
- _ty = TypingResult(t->memberResolver);
- setType(n, _ty.type);
- setType(t, _ty.type);
- return;
- }
- }
- TypingResult sourceTy = run(s->source);
- setType(t, sourceTy.type);
- _ty = sourceTy;
- return;
- }
-
- TypingResult sourceTy = run(s->source);
- _ty = run(s->target);
- _ty.fullyTyped &= sourceTy.fullyTyped;
- }
-
- void visitJump(Jump *) { _ty = TypingResult(MissingType); }
- void visitCJump(CJump *s) { _ty = run(s->cond); }
- void visitRet(Ret *s) { _ty = run(s->expr); }
- void visitPhi(Phi *s) {
- _ty = run(s->incoming[0]);
- for (int i = 1, ei = s->incoming.size(); i != ei; ++i) {
- TypingResult ty = run(s->incoming[i]);
- if (!ty.fullyTyped && _ty.fullyTyped) {
- // When one of the temps not fully typed, we already know that we cannot completely type this node.
- // So, pick the type we calculated upto this point, and wait until the unknown one will be typed.
- // At that point, this statement will be re-scheduled, and then we can fully type this node.
- _ty.fullyTyped = false;
- break;
- }
- _ty.type.type |= ty.type.type;
- _ty.fullyTyped &= ty.fullyTyped;
- if (_ty.type.test(QObjectType) && _ty.type.memberResolver)
- _ty.type.memberResolver->clear(); // ### TODO: find common ancestor meta-object
- }
-
- switch (_ty.type.type) {
- case UnknownType:
- case UndefinedType:
- case NullType:
- case BoolType:
- case SInt32Type:
- case UInt32Type:
- case DoubleType:
- case StringType:
- case QObjectType:
- case VarType:
- // The type is not a combination of two or more types, so we're done.
- break;
-
- default:
- // There are multiple types involved, so:
- if (_ty.type.isNumber())
- // The type is any combination of double/int32/uint32, but nothing else. So we can
- // type it as double.
- _ty.type = DoubleType;
- else
- // There just is no single type that can hold this combination, so:
- _ty.type = VarType;
- }
-
- setType(s->targetTemp, _ty.type);
- }
-};
-
-class ReverseInference
-{
- const DefUses &_defUses;
-
-public:
- ReverseInference(const DefUses &defUses)
- : _defUses(defUses)
- {}
-
- void run(IR::Function *f)
- {
- Q_UNUSED(f);
-
- QVector<UntypedTemp> knownOk;
- QVector<UntypedTemp> candidates = _defUses.defsUntyped();
- while (!candidates.isEmpty()) {
- UntypedTemp temp = candidates.last();
- candidates.removeLast();
-
- if (knownOk.contains(temp))
- continue;
-
- if (!isUsedAsInt32(temp, knownOk))
- continue;
-
- Stmt *s = _defUses.defStmt(temp.temp);
- Move *m = s->asMove();
- if (!m)
- continue;
- Temp *target = m->target->asTemp();
- if (!target || temp != UntypedTemp(*target) || target->type == SInt32Type)
- continue;
- if (Temp *t = m->source->asTemp()) {
- candidates.append(*t);
- } else if (m->source->asConvert()) {
- break;
- } else if (Binop *b = m->source->asBinop()) {
- bool iterateOnOperands = true;
-
- switch (b->op) {
- case OpSub:
- case OpMul:
- case OpAdd:
- if (b->left->type == SInt32Type && b->right->type == SInt32Type) {
- iterateOnOperands = false;
- break;
- } else {
- continue;
- }
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpLShift:
- case OpRShift:
- case OpURShift:
- break;
- default:
- continue;
- }
-
- if (iterateOnOperands) {
- if (Temp *lt = b->left->asTemp())
- candidates.append(*lt);
- if (Temp *rt = b->right->asTemp())
- candidates.append(*rt);
- }
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op == OpCompl || u->op == OpUPlus) {
- if (Temp *t = u->expr->asTemp())
- candidates.append(*t);
- }
- } else {
- continue;
- }
-
- knownOk.append(temp);
- }
-
- PropagateTempTypes propagator(_defUses);
- for (const UntypedTemp &t : qAsConst(knownOk)) {
- propagator.run(t, SInt32Type);
- if (Stmt *defStmt = _defUses.defStmt(t.temp)) {
- if (Move *m = defStmt->asMove()) {
- if (Convert *c = m->source->asConvert()) {
- c->type = SInt32Type;
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op != OpUMinus)
- u->type = SInt32Type;
- } else if (Binop *b = m->source->asBinop()) {
- b->type = SInt32Type;
- }
- }
- }
- }
- }
-
-private:
- bool isUsedAsInt32(const UntypedTemp &t, const QVector<UntypedTemp> &knownOk) const
- {
- const QVector<Stmt *> &uses = _defUses.uses(t.temp);
- if (uses.isEmpty())
- return false;
-
- for (Stmt *use : uses) {
- if (Move *m = use->asMove()) {
- Temp *targetTemp = m->target->asTemp();
-
- if (m->source->asTemp()) {
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- } else if (m->source->asConvert()) {
- continue;
- } else if (Binop *b = m->source->asBinop()) {
- switch (b->op) {
- case OpAdd:
- case OpSub:
- case OpMul:
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- Q_FALLTHROUGH();
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- case OpRShift:
- case OpLShift:
- case OpURShift:
- continue;
- default:
- return false;
- }
- } else if (Unop *u = m->source->asUnop()) {
- if (u->op == OpUPlus) {
- if (!targetTemp || !knownOk.contains(*targetTemp))
- return false;
- } else if (u->op != OpCompl) {
- return false;
- }
- } else {
- return false;
- }
- } else
- return false;
- }
-
- return true;
- }
-};
-
-void convertConst(Const *c, Type targetType)
-{
- switch (targetType) {
- case DoubleType:
- break;
- case SInt32Type:
- c->value = QV4::Primitive::toInt32(c->value);
- break;
- case UInt32Type:
- c->value = QV4::Primitive::toUInt32(c->value);
- break;
- case BoolType:
- c->value = !(c->value == 0 || std::isnan(c->value));
- break;
- case NullType:
- case UndefinedType:
- c->value = qt_qnan();
- c->type = targetType;
- break;
- default:
- Q_UNIMPLEMENTED();
- Q_ASSERT(!"Unimplemented!");
- break;
- }
- c->type = targetType;
-}
-
-class TypePropagation
-{
- DefUses &_defUses;
- Type _ty;
- IR::Function *_f;
-
- bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) {
- qSwap(_ty, requestedType);
- visit(e);
- qSwap(_ty, requestedType);
-
- if (requestedType != UnknownType) {
- if (e->type != requestedType) {
- if (requestedType & NumberType || requestedType == BoolType) {
- if (insertConversion)
- addConversion(e, requestedType);
- return true;
- }
- }
- }
-
- return false;
- }
-
- struct Conversion {
- Expr **expr;
- Type targetType;
- Stmt *stmt;
-
- Conversion(Expr **expr = 0, Type targetType = UnknownType, Stmt *stmt = 0)
- : expr(expr)
- , targetType(targetType)
- , stmt(stmt)
- {}
- };
-
- Stmt *_currStmt;
- QVector<Conversion> _conversions;
-
- void addConversion(Expr *&expr, Type targetType) {
- _conversions.append(Conversion(&expr, targetType, _currStmt));
- }
-
-public:
- TypePropagation(DefUses &defUses) : _defUses(defUses), _ty(UnknownType) {}
-
- void run(IR::Function *f, StatementWorklist &worklist) {
- _f = f;
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- _conversions.clear();
-
- for (Stmt *s : bb->statements()) {
- _currStmt = s;
- visit(s);
- }
-
- for (const Conversion &conversion : qAsConst(_conversions)) {
- IR::Move *move = conversion.stmt->asMove();
-
- // Note: isel only supports move into member when source is a temp, so convert
- // is not a supported source.
- if (move && move->source->asTemp() && !move->target->asMember()) {
- *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType);
- } else if (Const *c = (*conversion.expr)->asConst()) {
- convertConst(c, conversion.targetType);
- } else if (ArgLocal *al = (*conversion.expr)->asArgLocal()) {
- Temp *target = bb->TEMP(bb->newTemp());
- target->type = conversion.targetType;
- Expr *convert = bb->CONVERT(al, conversion.targetType);
- Move *convCall = f->NewStmt<Move>();
- worklist.registerNewStatement(convCall);
- convCall->init(target, convert);
- _defUses.addDef(target, convCall, bb);
-
- Temp *source = bb->TEMP(target->index);
- source->type = conversion.targetType;
- _defUses.addUse(*source, conversion.stmt);
-
- if (conversion.stmt->asPhi()) {
- // Only temps can be used as arguments to phi nodes, so this is a sanity check...:
- Q_UNREACHABLE();
- } else {
- bb->insertStatementBefore(conversion.stmt, convCall);
- }
-
- *conversion.expr = source;
- } else if (Temp *t = (*conversion.expr)->asTemp()) {
- Temp *target = bb->TEMP(bb->newTemp());
- target->type = conversion.targetType;
- Expr *convert = bb->CONVERT(t, conversion.targetType);
- Move *convCall = f->NewStmt<Move>();
- worklist.registerNewStatement(convCall);
- convCall->init(target, convert);
- _defUses.addDef(target, convCall, bb);
- _defUses.addUse(*t, convCall);
-
- Temp *source = bb->TEMP(target->index);
- source->type = conversion.targetType;
- _defUses.removeUse(conversion.stmt, *t);
- _defUses.addUse(*source, conversion.stmt);
-
- if (Phi *phi = conversion.stmt->asPhi()) {
- int idx = phi->incoming.indexOf(t);
- Q_ASSERT(idx != -1);
- bb->in[idx]->insertStatementBeforeTerminator(convCall);
- } else {
- bb->insertStatementBefore(conversion.stmt, convCall);
- }
-
- *conversion.expr = source;
- } else if (Unop *u = (*conversion.expr)->asUnop()) {
- // convert:
- // int32{%2} = double{-double{%1}};
- // to:
- // double{%3} = double{-double{%1}};
- // int32{%2} = int32{convert(double{%3})};
- Temp *tmp = bb->TEMP(bb->newTemp());
- tmp->type = u->type;
- Move *extraMove = f->NewStmt<Move>();
- worklist.registerNewStatement(extraMove);
- extraMove->init(tmp, u);
- _defUses.addDef(tmp, extraMove, bb);
-
- if (Temp *unopOperand = u->expr->asTemp()) {
- _defUses.addUse(*unopOperand, extraMove);
- _defUses.removeUse(move, *unopOperand);
- }
-
- bb->insertStatementBefore(conversion.stmt, extraMove);
-
- *conversion.expr = bb->CONVERT(CloneExpr::cloneTemp(tmp, f), conversion.targetType);
- _defUses.addUse(*tmp, move);
- } else {
- Q_UNREACHABLE();
- }
- }
- }
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto c = e->asConvert()) {
- run(c->expr, c->type);
- } else if (auto u = e->asUnop()) {
- run(u->expr, u->type);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- }
- }
-
- void visitConst(Const *c) {
- if (_ty & NumberType && c->type & NumberType) {
- if (_ty == SInt32Type)
- c->value = QV4::Primitive::toInt32(c->value);
- else if (_ty == UInt32Type)
- c->value = QV4::Primitive::toUInt32(c->value);
- c->type = _ty;
- }
- }
-
- void visitBinop(Binop *e) {
- // FIXME: This routine needs more tuning!
- switch (e->op) {
- case OpAdd:
- case OpSub:
- case OpMul:
- case OpDiv:
- case OpMod:
- case OpBitAnd:
- case OpBitOr:
- case OpBitXor:
- run(e->left, e->type);
- run(e->right, e->type);
- break;
-
- case OpLShift:
- case OpRShift:
- case OpURShift:
- run(e->left, SInt32Type);
- run(e->right, SInt32Type);
- break;
-
- case OpGt:
- case OpLt:
- case OpGe:
- case OpLe:
- case OpEqual:
- case OpNotEqual:
- if (e->left->type == DoubleType) {
- run(e->right, DoubleType);
- } else if (e->right->type == DoubleType) {
- run(e->left, DoubleType);
- } else {
- run(e->left, e->left->type);
- run(e->right, e->right->type);
- }
- break;
-
- case OpStrictEqual:
- case OpStrictNotEqual:
- case OpInstanceof:
- case OpIn:
- run(e->left, e->left->type);
- run(e->right, e->right->type);
- break;
-
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
- void visitCall(Call *e) {
- run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- run(it->expr);
- }
- void visitNew(New *e) {
- run(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- run(it->expr);
- }
- void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
- void visitMember(Member *e) { run(e->base); }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- }
- }
-
- void visitExp(Exp *s) { run(s->expr); }
- void visitMove(Move *s) {
- if (s->source->asConvert())
- return; // this statement got inserted for a phi-node type conversion
-
- run(s->target);
-
- if (Unop *u = s->source->asUnop()) {
- if (u->op == OpUPlus) {
- if (run(u->expr, s->target->type, false)) {
- Convert *convert = _f->New<Convert>();
- convert->init(u->expr, s->target->type);
- s->source = convert;
- } else {
- s->source = u->expr;
- }
-
- return;
- }
- }
-
- const Member *targetMember = s->target->asMember();
- const bool inhibitConversion = targetMember && targetMember->inhibitTypeConversionOnWrite;
-
- run(s->source, s->target->type, !inhibitConversion);
- }
- void visitCJump(CJump *s) {
- run(s->cond, BoolType);
- }
- void visitRet(Ret *s) { run(s->expr); }
- void visitPhi(Phi *s) {
- Type ty = s->targetTemp->type;
- for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
- run(s->incoming[i], ty);
- }
-};
-
-void splitCriticalEdges(IR::Function *f, DominatorTree &df, StatementWorklist &worklist, DefUses &defUses)
-{
- const QVector<BasicBlock *> copy = f->basicBlocks();
- for (BasicBlock *toBB : copy) {
- if (toBB->isRemoved())
- continue;
- if (toBB->in.size() < 2)
- continue;
-
- for (int inIdx = 0, eInIdx = toBB->in.size(); inIdx != eInIdx; ++inIdx) {
- BasicBlock *fromBB = toBB->in[inIdx];
- if (fromBB->out.size() < 2)
- continue;
-
- // We found a critical edge.
- // create the basic block:
- BasicBlock *newBB = f->newBasicBlock(toBB->catchBlock);
- Jump *s = f->NewStmt<Jump>();
- worklist.registerNewStatement(s);
- defUses.registerNewStatement(s);
- s->init(toBB);
- newBB->appendStatement(s);
-
- // rewire the old outgoing edge
- int outIdx = fromBB->out.indexOf(toBB);
- fromBB->out[outIdx] = newBB;
- newBB->in.append(fromBB);
-
- // rewire the old incoming edge
- toBB->in[inIdx] = newBB;
- newBB->out.append(toBB);
-
- // add newBB to the correct loop group
- if (toBB->isGroupStart()) {
- if (fromBB == toBB) {
- // special case: the loop header points back to itself (so it's a small loop).
- newBB->setContainingGroup(toBB);
- } else {
- BasicBlock *container;
- for (container = fromBB->containingGroup(); container; container = container->containingGroup())
- if (container == toBB)
- break;
- if (container == toBB) // if we were already inside the toBB loop
- newBB->setContainingGroup(toBB);
- else
- newBB->setContainingGroup(toBB->containingGroup());
- }
- } else {
- newBB->setContainingGroup(toBB->containingGroup());
- }
-
- // patch the terminator
- Stmt *terminator = fromBB->terminator();
- if (Jump *j = terminator->asJump()) {
- Q_ASSERT(outIdx == 0);
- j->target = newBB;
- } else if (CJump *j = terminator->asCJump()) {
- if (outIdx == 0)
- j->iftrue = newBB;
- else if (outIdx == 1)
- j->iffalse = newBB;
- else
- Q_ASSERT(!"Invalid out edge index for CJUMP!");
- } else if (terminator->asRet()) {
- Q_ASSERT(!"A block with a RET at the end cannot have outgoing edges.");
- } else {
- Q_ASSERT(!"Unknown terminator!");
- }
-
-// qDebug() << "splitting edge" << fromBB->index() << "->" << toBB->index()
-// << "by inserting block" << newBB->index();
-
- // Set the immediate dominator of the new block to inBB
- df.setImmediateDominator(newBB, fromBB);
-
- bool toNeedsNewIdom = true;
- for (BasicBlock *bb : toBB->in) {
- if (bb != newBB && bb != toBB && !df.dominates(toBB, bb)) {
- toNeedsNewIdom = false;
- break;
- }
- }
- if (toNeedsNewIdom)
- df.setImmediateDominator(toBB, newBB);
- }
- }
-}
-
-// Detect all (sub-)loops in a function.
-//
-// Doing loop detection on the CFG is better than relying on the statement information in
-// order to mark loops. Although JavaScript only has natural loops, it can still be the case
-// that something is not a loop even though a loop-like-statement is in the source. For
-// example:
-// while (true) {
-// if (i > 0)
-// break;
-// else
-// break;
-// }
-//
-// Algorithm:
-// - do a DFS on the dominator tree, where for each node:
-// - collect all back-edges
-// - if there are back-edges, the node is a loop-header for a new loop, so:
-// - walk the CFG is reverse-direction, and for every node:
-// - if the node already belongs to a loop, we've found a nested loop:
-// - get the loop-header for the (outermost) nested loop
-// - add that loop-header to the current loop
-// - continue by walking all incoming edges that do not yet belong to the current loop
-// - if the node does not belong to a loop yet, add it to the current loop, and
-// go on with all incoming edges
-//
-// Loop-header detection by checking for back-edges is very straight forward: a back-edge is
-// an incoming edge where the other node is dominated by the current node. Meaning: all
-// execution paths that reach that other node have to go through the current node, that other
-// node ends with a (conditional) jump back to the loop header.
-//
-// The exact order of the DFS on the dominator tree is not important. The only property has to
-// be that a node is only visited when all the nodes it dominates have been visited before.
-// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated
-// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before
-// their containing loops, which makes nested-loop identification free. An added benefit is
-// that the nodes for those sub-loops are only processed once.
-//
-// Note: independent loops that share the same header are merged together. For example, in
-// the code snippet below, there are 2 back-edges into the loop-header, but only one single
-// loop will be detected.
-// while (a) {
-// if (b)
-// continue;
-// else
-// continue;
-// }
-class LoopDetection
-{
- enum { DebugLoopDetection = 0 };
-
- Q_DISABLE_COPY(LoopDetection)
-
-public:
- struct LoopInfo
- {
- BasicBlock *loopHeader;
- QVector<BasicBlock *> loopBody;
- QVector<LoopInfo *> nestedLoops;
- LoopInfo *parentLoop;
-
- LoopInfo(BasicBlock *loopHeader = 0)
- : loopHeader(loopHeader)
- , parentLoop(0)
- {}
-
- bool isValid() const
- { return loopHeader != 0; }
-
- void addNestedLoop(LoopInfo *nested)
- {
- Q_ASSERT(nested);
- Q_ASSERT(!nestedLoops.contains(nested));
- Q_ASSERT(nested->parentLoop == 0);
- nested->parentLoop = this;
- nestedLoops.append(nested);
- }
- };
-
-public:
- LoopDetection(const DominatorTree &dt)
- : dt(dt)
- {}
-
- ~LoopDetection()
- {
- qDeleteAll(loopInfos);
- }
-
- void run(IR::Function *function)
- {
- std::vector<BasicBlock *> backedges;
- backedges.reserve(4);
-
- const auto order = dt.calculateDFNodeIterOrder();
- for (BasicBlock *bb : order) {
- Q_ASSERT(!bb->isRemoved());
-
- backedges.clear();
-
- for (BasicBlock *in : bb->in)
- if (bb == in || dt.dominates(bb, in))
- backedges.push_back(in);
-
- if (!backedges.empty()) {
- subLoop(bb, backedges);
- }
- }
-
- createLoopInfos(function);
- dumpLoopInfo();
- }
-
- void dumpLoopInfo() const
- {
- if (!DebugLoopDetection)
- return;
-
- qDebug() << "Found" << loopInfos.size() << "loops";
- for (const LoopInfo *info : loopInfos) {
- qDebug() << "Loop header:" << info->loopHeader->index()
- << "for loop" << quint64(info);
- for (BasicBlock *bb : info->loopBody)
- qDebug() << " " << bb->index();
- for (LoopInfo *nested : info->nestedLoops)
- qDebug() << " sub loop:" << quint64(nested);
- qDebug() << " parent loop:" << quint64(info->parentLoop);
- }
- }
-
- QVector<LoopInfo *> allLoops() const
- { return loopInfos; }
-
- // returns all loop headers for loops that have no nested loops.
- QVector<LoopInfo *> innermostLoops() const
- {
- QVector<LoopInfo *> inner(loopInfos);
-
- for (int i = 0; i < inner.size(); ) {
- if (inner.at(i)->nestedLoops.isEmpty())
- ++i;
- else
- inner.remove(i);
- }
-
- return inner;
- }
-
-private:
- void subLoop(BasicBlock *loopHead, const std::vector<BasicBlock *> &backedges)
- {
- loopHead->markAsGroupStart();
- LoopInfo *info = new LoopInfo;
- info->loopHeader = loopHead;
- loopInfos.append(info);
-
- std::vector<BasicBlock *> worklist;
- worklist.reserve(backedges.size() + 8);
- worklist.insert(worklist.end(), backedges.begin(), backedges.end());
- while (!worklist.empty()) {
- BasicBlock *predIt = worklist.back();
- worklist.pop_back();
-
- BasicBlock *subloop = predIt->containingGroup();
- if (subloop) {
- // This is a discovered block. Find its outermost discovered loop.
- while (BasicBlock *parentLoop = subloop->containingGroup())
- subloop = parentLoop;
-
- // If it is already discovered to be a subloop of this loop, continue.
- if (subloop == loopHead)
- continue;
-
- // Yay, it's a subloop of this loop.
- subloop->setContainingGroup(loopHead);
- predIt = subloop;
-
- // Add all predecessors of the subloop header to the worklist, as long as
- // those predecessors are not in the current subloop. It might be the case
- // that they are in other loops, which we will then add as a subloop to the
- // current loop.
- for (BasicBlock *predIn : predIt->in)
- if (predIn->containingGroup() != subloop)
- worklist.push_back(predIn);
- } else {
- if (predIt == loopHead)
- continue;
-
- // This is an undiscovered block. Map it to the current loop.
- predIt->setContainingGroup(loopHead);
-
- // Add all incoming edges to the worklist.
- for (BasicBlock *bb : predIt->in)
- worklist.push_back(bb);
- }
- }
- }
-
-private:
- const DominatorTree &dt;
- QVector<LoopInfo *> loopInfos;
-
- void createLoopInfos(IR::Function *function)
- {
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
- if (BasicBlock *loopHeader = bb->containingGroup())
- findLoop(loopHeader)->loopBody.append(bb);
- }
-
- for (int i = 0, size = loopInfos.size(); i < size; ++i) {
- if (BasicBlock *containingLoopHeader = loopInfos.at(i)->loopHeader->containingGroup())
- findLoop(containingLoopHeader)->addNestedLoop(loopInfos.at(i));
- }
- }
-
- LoopInfo *findLoop(BasicBlock *loopHeader)
- {
- for (LoopInfo *info : qAsConst(loopInfos)) {
- if (info->loopHeader == loopHeader)
- return info;
- }
-
- Q_UNREACHABLE();
- return nullptr;
- }
-};
-
-// High-level algorithm:
-// 0. start with the first node (the start node) of a function
-// 1. emit the node
-// 2. add all outgoing edges that are not yet emitted to the postponed stack
-// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too,
-// we're done.
-// 4. pop a node from the postponed stack, and check if it can be scheduled:
-// a. if all incoming edges are scheduled, go to 4.
-// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps
-// back to the start of the loop), ignore it
-// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4.
-// 5. if this node is the start of a loop, push the postponed stack on the loop stack.
-// 6. go back to 1.
-//
-// The postponing action in step 2 will put the node into its containing group. The case where this
-// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the
-// outgoing edge points to a node that is not part of the current loop (and possibly not of the
-// parent loop).
-//
-// Linear scan register allocation benefits greatly from short life-time intervals with few holes
-// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the
-// blocks of a group are scheduled together, with no non-loop blocks in between. This applies
-// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for
-// the same reason.
-class BlockScheduler
-{
- enum { DebugBlockScheduler = 0 };
-
- IR::Function *function;
- const DominatorTree &dominatorTree;
-
- struct WorkForGroup
- {
- BasicBlock *group;
- QStack<BasicBlock *> postponed;
-
- WorkForGroup(BasicBlock *group = 0): group(group) {}
- };
- WorkForGroup currentGroup;
- QStack<WorkForGroup> postponedGroups;
- QVector<BasicBlock *> sequence;
- ProcessedBlocks emitted;
- QHash<BasicBlock *, BasicBlock *> loopsStartEnd;
-
- bool checkCandidate(BasicBlock *candidate)
- {
- Q_ASSERT(candidate->containingGroup() == currentGroup.group);
-
- for (BasicBlock *in : candidate->in) {
- if (emitted.alreadyProcessed(in))
- continue;
-
- if (dominatorTree.dominates(candidate, in))
- // this is a loop, where there in -> candidate edge is the jump back to the top of the loop.
- continue;
-
- if (in == candidate)
- // this is a very tight loop, e.g.:
- // L1: ...
- // goto L1
- // This can happen when, for example, the basic-block merging gets rid of the empty
- // body block. In this case, we can safely schedule this block (if all other
- // incoming edges are either loop-back edges, or have been scheduled already).
- continue;
-
- return false; // an incoming edge that is not yet emitted, and is not a back-edge
- }
-
- if (candidate->isGroupStart()) {
- // postpone everything, and schedule the loop first.
- postponedGroups.push(currentGroup);
- currentGroup = WorkForGroup(candidate);
- }
-
- return true;
- }
-
- BasicBlock *pickNext()
- {
- while (true) {
- while (currentGroup.postponed.isEmpty()) {
- if (postponedGroups.isEmpty())
- return 0;
- if (currentGroup.group) // record the first and the last node of a group
- loopsStartEnd.insert(currentGroup.group, sequence.last());
- currentGroup = postponedGroups.pop();
- }
-
- BasicBlock *next = currentGroup.postponed.pop();
- if (checkCandidate(next))
- return next;
- }
-
- Q_UNREACHABLE();
- return 0;
- }
-
- void emitBlock(BasicBlock *bb)
- {
- Q_ASSERT(!bb->isRemoved());
- if (emitted.alreadyProcessed(bb))
- return;
-
- sequence.append(bb);
- emitted.markAsProcessed(bb);
- }
-
- void schedule(BasicBlock *functionEntryPoint)
- {
- BasicBlock *next = functionEntryPoint;
-
- while (next) {
- emitBlock(next);
- for (int i = next->out.size(); i != 0; ) {
- // postpone all outgoing edges, if they were not already processed
- --i;
- BasicBlock *out = next->out[i];
- if (!emitted.alreadyProcessed(out))
- postpone(out);
- }
- next = pickNext();
- }
- }
-
- void postpone(BasicBlock *bb)
- {
- if (currentGroup.group == bb->containingGroup()) {
- currentGroup.postponed.append(bb);
- return;
- }
-
- for (int i = postponedGroups.size(); i != 0; ) {
- --i;
- WorkForGroup &g = postponedGroups[i];
- if (g.group == bb->containingGroup()) {
- g.postponed.append(bb);
- return;
- }
- }
-
- Q_UNREACHABLE();
- }
-
- void dumpLoopStartsEnds() const
- {
- qDebug() << "Found" << loopsStartEnd.size() << "loops:";
- for (auto key : loopsStartEnd.keys())
- qDebug("Loop starting at L%d ends at L%d.", key->index(),
- loopsStartEnd.value(key)->index());
- }
-
-public:
- BlockScheduler(IR::Function *function, const DominatorTree &dominatorTree)
- : function(function)
- , dominatorTree(dominatorTree)
- , sequence(0)
- , emitted(function)
- {}
-
- QHash<BasicBlock *, BasicBlock *> go()
- {
- showMeTheCode(function, "Before block scheduling");
- if (DebugBlockScheduler)
- dominatorTree.dumpImmediateDominators();
-
- schedule(function->basicBlock(0));
-
- Q_ASSERT(function->liveBasicBlocksCount() == sequence.size());
- function->setScheduledBlocks(sequence);
- if (DebugBlockScheduler)
- dumpLoopStartsEnds();
- return loopsStartEnd;
- }
-};
-
-#ifndef QT_NO_DEBUG
-void checkCriticalEdges(const QVector<BasicBlock *> &basicBlocks) {
- for (BasicBlock *bb : basicBlocks) {
- if (bb && bb->out.size() > 1) {
- for (BasicBlock *bb2 : bb->out) {
- if (bb2 && bb2->in.size() > 1) {
- qDebug() << "found critical edge between block"
- << bb->index() << "and block" << bb2->index();
- Q_ASSERT(false);
- }
- }
- }
- }
-}
-#endif
-
-static void cleanupBasicBlocks(IR::Function *function)
-{
- showMeTheCode(function, "Before basic block cleanup");
-
- // Algorithm: this is the iterative version of a depth-first search for all blocks that are
- // reachable through outgoing edges, starting with the start block and all exception handler
- // blocks.
- QBitArray reachableBlocks(function->basicBlockCount());
- QVarLengthArray<BasicBlock *, 16> postponed;
- for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
- BasicBlock *bb = function->basicBlock(i);
- if (i == 0 || bb->isExceptionHandler())
- postponed.append(bb);
- }
-
- while (!postponed.isEmpty()) {
- BasicBlock *bb = postponed.back();
- postponed.pop_back();
- if (bb->isRemoved()) // this block was removed before, we don't need to clean it up.
- continue;
-
- reachableBlocks.setBit(bb->index());
-
- for (BasicBlock *outBB : bb->out) {
- if (!reachableBlocks.at(outBB->index()))
- postponed.append(outBB);
- }
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved()) // the block has already been removed, so ignore it
- continue;
- if (reachableBlocks.at(bb->index())) // the block is reachable, so ignore it
- continue;
-
- for (BasicBlock *outBB : bb->out) {
- if (outBB->isRemoved() || !reachableBlocks.at(outBB->index()))
- continue; // We do not need to unlink from blocks that are scheduled to be removed.
-
- int idx = outBB->in.indexOf(bb);
- if (idx != -1) {
- outBB->in.remove(idx);
- for (Stmt *s : outBB->statements()) {
- if (Phi *phi = s->asPhi())
- phi->incoming.remove(idx);
- else
- break;
- }
- }
- }
-
- function->removeBasicBlock(bb);
- }
-
- showMeTheCode(function, "After basic block cleanup");
-}
-
-inline Const *isConstPhi(Phi *phi)
-{
- if (Const *c = phi->incoming[0]->asConst()) {
- for (int i = 1, ei = phi->incoming.size(); i != ei; ++i) {
- if (Const *cc = phi->incoming[i]->asConst()) {
- if (c->value != cc->value)
- return 0;
- if (!(c->type == cc->type || (c->type & NumberType && cc->type & NumberType)))
- return 0;
- if (int(c->value) == 0 && int(cc->value) == 0)
- if (isNegative(c->value) != isNegative(cc->value))
- return 0;
- } else {
- return 0;
- }
- }
- return c;
- }
- return 0;
-}
-
-static Expr *clone(Expr *e, IR::Function *function) {
- if (Temp *t = e->asTemp()) {
- return CloneExpr::cloneTemp(t, function);
- } else if (Const *c = e->asConst()) {
- return CloneExpr::cloneConst(c, function);
- } else if (Name *n = e->asName()) {
- return CloneExpr::cloneName(n, function);
- } else {
- Q_UNREACHABLE();
- return e;
- }
-}
-
-class ExprReplacer
-{
- DefUses &_defUses;
- IR::Function* _function;
- Temp *_toReplace;
- Expr *_replacement;
-
-public:
- ExprReplacer(DefUses &defUses, IR::Function *function)
- : _defUses(defUses)
- , _function(function)
- , _toReplace(0)
- , _replacement(0)
- {}
-
- bool operator()(Temp *toReplace, Expr *replacement, StatementWorklist &W, QVector<Stmt *> *newUses = 0)
- {
- Q_ASSERT(replacement->asTemp() || replacement->asConst() || replacement->asName());
-
- qSwap(_toReplace, toReplace);
- qSwap(_replacement, replacement);
-
- const QVector<Stmt *> &uses = _defUses.uses(*_toReplace);
-
- // Prevent the following:
- // L3:
- // %1 = phi L1: %2, L2: %3
- // %4 = phi L1: %5, L2: %6
- // %6 = %1
- // From turning into:
- // L3:
- // %1 = phi L1: %2, L2: %3
- // %4 = phi L1: %5, L2: %1
- //
- // Because both phi nodes are "executed in parallel", we cannot replace %6 by %1 in the
- // second phi node. So, if the defining statement for a temp is a phi node, and one of the
- // uses of the to-be-replaced statement is a phi node in the same block as the defining
- // statement, bail out.
- if (Temp *r = _replacement->asTemp()) {
- if (_defUses.defStmt(*r)->asPhi()) {
- BasicBlock *replacementDefBlock = _defUses.defStmtBlock(*r);
- for (Stmt *use : uses) {
- if (Phi *usePhi = use->asPhi()) {
- if (_defUses.defStmtBlock(*usePhi->targetTemp) == replacementDefBlock)
- return false;
- }
- }
- }
- }
-
-// qout << "Replacing ";toReplace->dump(qout);qout<<" by ";replacement->dump(qout);qout<<endl;
-
- if (newUses)
- newUses->reserve(uses.size());
-
-// qout << " " << uses.size() << " uses:"<<endl;
- for (Stmt *use : uses) {
-// qout<<" ";use->dump(qout);qout<<"\n";
- visit(use);
-// qout<<" -> ";use->dump(qout);qout<<"\n";
- W += use;
- if (newUses)
- newUses->push_back(use);
- }
-
- qSwap(_replacement, replacement);
- qSwap(_toReplace, toReplace);
- return true;
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto c = e->asConst()) {
- visitConst(c);
- } else if (auto s = e->asString()) {
- visitString(s);
- } else if (auto r = e->asRegExp()) {
- visitRegExp(r);
- } else if (auto n = e->asName()) {
- visitName(n);
- } else if (auto t = e->asTemp()) {
- visitTemp(t);
- } else if (auto a = e->asArgLocal()) {
- visitArgLocal(a);
- } else if (auto c = e->asClosure()) {
- visitClosure(c);
- } else if (auto c = e->asConvert()) {
- visitConvert(c);
- } else if (auto u = e->asUnop()) {
- visitUnop(u);
- } else if (auto b = e->asBinop()) {
- visitBinop(b);
- } else if (auto c = e->asCall()) {
- visitCall(c);
- } else if (auto n = e->asNew()) {
- visitNew(n);
- } else if (auto s = e->asSubscript()) {
- visitSubscript(s);
- } else if (auto m = e->asMember()) {
- visitMember(m);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitConst(Const *) {}
- void visitString(IR::String *) {}
- void visitRegExp(IR::RegExp *) {}
- void visitName(Name *) {}
- void visitTemp(Temp *) {}
- void visitArgLocal(ArgLocal *) {}
- void visitClosure(Closure *) {}
- void visitConvert(Convert *e) { check(e->expr); }
- void visitUnop(Unop *e) { check(e->expr); }
- void visitBinop(Binop *e) { check(e->left); check(e->right); }
- void visitCall(Call *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- void visitNew(New *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- void visitMember(Member *e) { check(e->base); }
-
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- visitExp(e);
- } else if (auto m = s->asMove()) {
- visitMove(m);
- } else if (auto j = s->asJump()) {
- visitJump(j);
- } else if (auto c = s->asCJump()) {
- visitCJump(c);
- } else if (auto r = s->asRet()) {
- visitRet(r);
- } else if (auto p = s->asPhi()) {
- visitPhi(p);
- } else {
- Q_UNREACHABLE();
- }
- }
-
- void visitExp(Exp *s) { check(s->expr); }
- void visitMove(Move *s) { check(s->target); check(s->source); }
- void visitJump(Jump *) {}
- void visitCJump(CJump *s) { check(s->cond); }
- void visitRet(Ret *s) { check(s->expr); }
- void visitPhi(Phi *s) {
- for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
- check(s->incoming[i]);
- }
-
-private:
- void check(Expr *&e) {
- if (equals(e, _toReplace)) {
- e = clone(_replacement, _function);
- } else {
- visit(e);
- }
- }
-
- // This only calculates equality for everything needed by constant propagation
- bool equals(Expr *e1, Expr *e2) const {
- if (e1 == e2)
- return true;
-
- if (Const *c1 = e1->asConst()) {
- if (Const *c2 = e2->asConst())
- return c1->value == c2->value && (c1->type == c2->type || (c1->type & NumberType && c2->type & NumberType));
- } else if (Temp *t1 = e1->asTemp()) {
- if (Temp *t2 = e2->asTemp())
- return *t1 == *t2;
- } else if (Name *n1 = e1->asName()) {
- if (Name *n2 = e2->asName()) {
- if (n1->id) {
- if (n2->id)
- return *n1->id == *n2->id;
- } else {
- return n1->builtin == n2->builtin;
- }
- }
- }
-
- if (e1->type == IR::NullType && e2->type == IR::NullType)
- return true;
- if (e1->type == IR::UndefinedType && e2->type == IR::UndefinedType)
- return true;
-
- return false;
- }
-};
-
-namespace {
-/// This function removes the basic-block from the function's list, unlinks any uses and/or defs,
-/// and removes unreachable staements from the worklist, so that optimiseSSA won't consider them
-/// anymore.
-void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses,
- StatementWorklist &W, DominatorTree &dt)
-{
- enum { DebugUnlinking = 0 };
-
- struct Util {
- static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W)
- {
- int idx = to->in.indexOf(from);
- if (idx == -1)
- return;
-
- to->in.remove(idx);
- for (Stmt *outStmt : to->statements()) {
- if (!outStmt)
- continue;
- if (Phi *phi = outStmt->asPhi()) {
- if (Temp *t = phi->incoming[idx]->asTemp()) {
- defUses.removeUse(phi, *t);
- W += defUses.defStmt(*t);
- }
- phi->incoming.remove(idx);
- W += phi;
- } else {
- break;
- }
- }
- }
-
- static bool isReachable(BasicBlock *bb, const DominatorTree &dt)
- {
- for (BasicBlock *in : bb->in) {
- if (in->isRemoved())
- continue;
- if (dt.dominates(bb, in)) // a back-edge, not interesting
- continue;
- return true;
- }
-
- return false;
- }
- };
-
- Q_ASSERT(!from->isRemoved());
- Q_ASSERT(!to->isRemoved());
-
- // don't purge blocks that are entry points for catch statements. They might not be directly
- // connected, but are required anyway
- if (to->isExceptionHandler())
- return;
-
- if (DebugUnlinking)
- qDebug("Unlinking L%d -> L%d...", from->index(), to->index());
-
- // First, unlink the edge
- from->out.removeOne(to);
- Util::removeIncomingEdge(from, to, defUses, W);
-
- BasicBlockSet siblings;
- siblings.init(func);
-
- // Check if the target is still reachable...
- if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done.
- if (DebugUnlinking)
- qDebug(".. L%d is still reachable, recalulate idom.", to->index());
- dt.collectSiblings(to, siblings);
- } else {
- if (DebugUnlinking)
- qDebug(".. L%d is unreachable, purging it:", to->index());
- // The target is unreachable, so purge it:
- QVector<BasicBlock *> toPurge;
- toPurge.reserve(8);
- toPurge.append(to);
- while (!toPurge.isEmpty()) {
- BasicBlock *bb = toPurge.first();
- toPurge.removeFirst();
- if (DebugUnlinking)
- qDebug("... purging L%d", bb->index());
-
- if (bb->isRemoved())
- continue;
-
- // unlink all incoming edges
- for (BasicBlock *in : bb->in) {
- int idx = in->out.indexOf(bb);
- if (idx != -1)
- in->out.remove(idx);
- }
-
- // unlink all outgoing edges, including "arguments" to phi statements
- for (BasicBlock *out : bb->out) {
- if (out->isRemoved())
- continue;
-
- Util::removeIncomingEdge(bb, out, defUses, W);
-
- if (Util::isReachable(out, dt)) {
- dt.collectSiblings(out, siblings);
- } else {
- // if a successor has no incoming edges after unlinking the current basic block,
- // then it is unreachable, and can be purged too
- toPurge.append(out);
- }
- }
-
- // unlink all defs/uses from the statements in the basic block
- for (Stmt *s : bb->statements()) {
- if (!s)
- continue;
-
- W += defUses.removeDefUses(s);
- W -= s;
- }
-
- siblings.remove(bb);
- dt.setImmediateDominator(bb, 0);
- func->removeBasicBlock(bb);
- }
- }
-
- dt.recalculateIDoms(siblings);
- if (DebugUnlinking)
- qDebug("Unlinking done.");
-}
-
-bool tryOptimizingComparison(Expr *&expr)
-{
- Binop *b = expr->asBinop();
- if (!b)
- return false;
- Const *leftConst = b->left->asConst();
- if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType)
- return false;
- Const *rightConst = b->right->asConst();
- if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType)
- return false;
-
- QV4::Primitive l = convertToValue(leftConst);
- QV4::Primitive r = convertToValue(rightConst);
-
- switch (b->op) {
- case OpGt:
- leftConst->value = Runtime::method_compareGreaterThan(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpLt:
- leftConst->value = Runtime::method_compareLessThan(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpGe:
- leftConst->value = Runtime::method_compareGreaterEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpLe:
- leftConst->value = Runtime::method_compareLessEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpStrictEqual:
- leftConst->value = Runtime::method_compareStrictEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpEqual:
- leftConst->value = Runtime::method_compareEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpStrictNotEqual:
- leftConst->value = Runtime::method_compareStrictNotEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- case OpNotEqual:
- leftConst->value = Runtime::method_compareNotEqual(l, r);
- leftConst->type = BoolType;
- expr = leftConst;
- return true;
- default:
- break;
- }
-
- return false;
-}
-
-void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QVector<LoopDetection::LoopInfo *>())
-{
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_IR");
- if (!showCode)
- return;
-
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
-
- struct Util {
- QTextStream &qout;
- Util(QTextStream &qout): qout(qout) {}
- void genLoop(const LoopDetection::LoopInfo *loop)
- {
- qout << " subgraph \"cluster" << quint64(loop) << "\" {\n";
- qout << " L" << loop->loopHeader->index() << ";\n";
- for (BasicBlock *bb : loop->loopBody)
- qout << " L" << bb->index() << ";\n";
- for (LoopDetection::LoopInfo *nested : loop->nestedLoops)
- genLoop(nested);
- qout << " }\n";
- }
- };
-
- QString name;
- if (f->name) name = *f->name;
- else name = QStringLiteral("%1").arg((unsigned long long)f);
- qout << "digraph \"" << name << "\" { ordering=out;\n";
-
- for (LoopDetection::LoopInfo *l : loops) {
- if (l->parentLoop == 0)
- Util(qout).genLoop(l);
- }
-
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- int idx = bb->index();
- qout << " L" << idx << " [label=\"L" << idx << "\"";
- if (idx == 0 || bb->terminator()->asRet())
- qout << ", shape=doublecircle";
- else
- qout << ", shape=circle";
- qout << "];\n";
- for (BasicBlock *out : bb->out)
- qout << " L" << idx << " -> L" << out->index() << "\n";
- }
-
- qout << "}\n";
- buf.close();
- qDebug("%s", buf.data().constData());
-}
-
-} // anonymous namespace
-
-void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
-{
- IR::Function *function = W.function();
- ExprReplacer replaceUses(defUses, function);
-
- Stmt *s = 0;
- while ((s = W.takeNext(s))) {
-
- if (Phi *phi = s->asPhi()) {
- // dead code elimination:
- if (defUses.useCount(*phi->targetTemp) == 0) {
- W += defUses.removeDefUses(phi);
- W.remove(s);
- continue;
- }
-
- // constant propagation:
- if (Const *c = isConstPhi(phi)) {
- replaceUses(phi->targetTemp, c, W);
- defUses.removeDef(*phi->targetTemp);
- W.remove(s);
- continue;
- }
-
- // copy propagation:
- if (phi->incoming.size() == 1) {
- Temp *t = phi->targetTemp;
- Expr *e = phi->incoming.first();
-
- QVector<Stmt *> newT2Uses;
- replaceUses(t, e, W, &newT2Uses);
- if (Temp *t2 = e->asTemp()) {
- defUses.removeUse(s, *t2);
- defUses.addUses(*t2, newT2Uses);
- W += defUses.defStmt(*t2);
- }
- defUses.removeDef(*t);
- W.remove(s);
- continue;
- }
- } else if (Move *m = s->asMove()) {
- if (Convert *convert = m->source->asConvert()) {
- if (Const *sourceConst = convert->expr->asConst()) {
- convertConst(sourceConst, convert->type);
- m->source = sourceConst;
- W += m;
- continue;
- } else if (Temp *sourceTemp = convert->expr->asTemp()) {
- if (sourceTemp->type == convert->type) {
- m->source = sourceTemp;
- W += m;
- continue;
- }
- }
- }
-
- if (Temp *targetTemp = m->target->asTemp()) {
- // dead code elimination:
- if (defUses.useCount(*targetTemp) == 0) {
- EliminateDeadCode(defUses, W).run(m->source, s);
- if (!m->source)
- W.remove(s);
- continue;
- }
-
- // constant propagation:
- if (Const *sourceConst = m->source->asConst()) {
- Q_ASSERT(sourceConst->type != UnknownType);
- replaceUses(targetTemp, sourceConst, W);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- continue;
- }
- if (Member *member = m->source->asMember()) {
- if (member->kind == Member::MemberOfEnum) {
- Const *c = function->New<Const>();
- const int enumValue = member->enumValue;
- c->init(SInt32Type, enumValue);
- replaceUses(targetTemp, c, W);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- defUses.removeUse(s, *member->base->asTemp());
- continue;
- } else if (member->kind != IR::Member::MemberOfIdObjectsArray && member->attachedPropertiesId != 0 && member->property && member->base->asTemp()) {
- // Attached properties have no dependency on their base. Isel doesn't
- // need it and we can eliminate the temp used to initialize it.
- defUses.removeUse(s, *member->base->asTemp());
- Const *c = function->New<Const>();
- c->init(SInt32Type, 0);
- member->base = c;
- continue;
- }
- }
-
- // copy propagation:
- if (Temp *sourceTemp = m->source->asTemp()) {
- QVector<Stmt *> newT2Uses;
- if (replaceUses(targetTemp, sourceTemp, W, &newT2Uses)) {
- defUses.removeUse(s, *sourceTemp);
- defUses.addUses(*sourceTemp, newT2Uses);
- defUses.removeDef(*targetTemp);
- W.remove(s);
- }
- continue;
- }
-
- if (Unop *unop = m->source->asUnop()) {
- // Constant unary expression evaluation:
- if (Const *constOperand = unop->expr->asConst()) {
- if (constOperand->type & NumberType || constOperand->type == BoolType) {
- // TODO: implement unop propagation for other constant types
- bool doneSomething = false;
- switch (unop->op) {
- case OpNot:
- constOperand->value = !constOperand->value;
- constOperand->type = BoolType;
- doneSomething = true;
- break;
- case OpUMinus:
- if (int(constOperand->value) == 0 && int(constOperand->value) == constOperand->value) {
- if (isNegative(constOperand->value))
- constOperand->value = 0;
- else
- constOperand->value = -1 / Q_INFINITY;
- constOperand->type = DoubleType;
- doneSomething = true;
- break;
- }
-
- constOperand->value = -constOperand->value;
- doneSomething = true;
- break;
- case OpUPlus:
- if (unop->type != UnknownType)
- constOperand->type = unop->type;
- doneSomething = true;
- break;
- case OpCompl:
- constOperand->value = ~QV4::Primitive::toInt32(constOperand->value);
- constOperand->type = SInt32Type;
- doneSomething = true;
- break;
- case OpIncrement:
- constOperand->value = constOperand->value + 1;
- doneSomething = true;
- break;
- case OpDecrement:
- constOperand->value = constOperand->value - 1;
- doneSomething = true;
- break;
- default:
- break;
- };
-
- if (doneSomething) {
- m->source = constOperand;
- W += m;
- }
- }
- }
- // TODO: if the result of a unary not operation is only used in a cjump,
- // then inline it.
-
- continue;
- }
-
- if (Binop *binop = m->source->asBinop()) {
- Const *leftConst = binop->left->asConst();
- Const *rightConst = binop->right->asConst();
-
- { // Typical casts to int32:
- Expr *casted = 0;
- switch (binop->op) {
- case OpBitAnd:
- if (leftConst && !rightConst && QV4::Primitive::toUInt32(leftConst->value) == 0xffffffff)
- casted = binop->right;
- else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0xffffffff)
- casted = binop->left;
- break;
- case OpBitOr:
- if (leftConst && !rightConst && QV4::Primitive::toInt32(leftConst->value) == 0)
- casted = binop->right;
- else if (!leftConst && rightConst && QV4::Primitive::toUInt32(rightConst->value) == 0)
- casted = binop->left;
- break;
- default:
- break;
- }
- if (casted && casted->type == SInt32Type) {
- m->source = casted;
- W += m;
- continue;
- }
- }
- if (rightConst) {
- switch (binop->op) {
- case OpLShift:
- case OpRShift:
- if (double v = QV4::Primitive::toInt32(rightConst->value) & 0x1f) {
- // mask right hand side of shift operations
- rightConst->value = v;
- rightConst->type = SInt32Type;
- } else {
- // shifting a value over 0 bits is a move:
- if (rightConst->value == 0) {
- m->source = binop->left;
- W += m;
- }
- }
-
- break;
- default:
- break;
- }
- }
-
- // TODO: More constant binary expression evaluation
- // TODO: If the result of the move is only used in one single cjump, then
- // inline the binop into the cjump.
- if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType)
- continue;
- if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType)
- continue;
-
- QV4::Primitive lc = convertToValue(leftConst);
- QV4::Primitive rc = convertToValue(rightConst);
- double l = lc.toNumber();
- double r = rc.toNumber();
-
- switch (binop->op) {
- case OpMul:
- leftConst->value = l * r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpAdd:
- leftConst->value = l + r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpSub:
- leftConst->value = l - r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpDiv:
- leftConst->value = l / r;
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- case OpMod:
- leftConst->value = std::fmod(l, r);
- leftConst->type = DoubleType;
- m->source = leftConst;
- W += m;
- break;
- default:
- if (tryOptimizingComparison(m->source))
- W += m;
- break;
- }
-
- continue;
- }
- } // TODO: var{#0} = double{%10} where %10 is defined once and used once. E.g.: function(t){t = t % 2; return t; }
-
- } else if (CJump *cjump = s->asCJump()) {
- if (Const *constantCondition = cjump->cond->asConst()) {
- // Note: this assumes that there are no critical edges! Meaning, we can safely purge
- // any basic blocks that are found to be unreachable.
- Jump *jump = function->NewStmt<Jump>();
- W.registerNewStatement(jump);
- if (convertToValue(constantCondition).toBoolean()) {
- jump->target = cjump->iftrue;
- unlink(cjump->parent, cjump->iffalse, function, defUses, W, df);
- } else {
- jump->target = cjump->iffalse;
- unlink(cjump->parent, cjump->iftrue, function, defUses, W, df);
- }
- W.replace(s, jump);
-
- continue;
- } else if (cjump->cond->asBinop()) {
- if (tryOptimizingComparison(cjump->cond))
- W += cjump;
- continue;
- }
- // TODO: Constant unary expression evaluation
- // TODO: if the expression is an unary not operation, lift the expression, and switch
- // the then/else blocks.
- }
- }
-
- W.applyToFunction();
-}
-
-//### TODO: use DefUses from the optimizer, because it already has all this information
-class InputOutputCollector
-{
- void setOutput(Temp *out)
- {
- Q_ASSERT(!output);
- output = out;
- }
-
-public:
- std::vector<Temp *> inputs;
- Temp *output;
-
- InputOutputCollector()
- { inputs.reserve(4); }
-
- void collect(Stmt *s) {
- inputs.resize(0);
- output = 0;
- visit(s);
- }
-
-private:
- void visit(Expr *e)
- {
- if (auto t = e->asTemp()) {
- inputs.push_back(t);
- } else {
- EXPR_VISIT_ALL_KINDS(e);
- }
- }
-
- void visit(Stmt *s)
- {
- if (auto m = s->asMove()) {
- visit(m->source);
- if (Temp *t = m->target->asTemp()) {
- setOutput(t);
- } else {
- visit(m->target);
- }
- } else if (s->asPhi()) {
- // Handled separately
- } else {
- STMT_VISIT_ALL_KINDS(s);
- }
- }
-};
-
-/*
- * The algorithm is described in:
- *
- * Linear Scan Register Allocation on SSA Form
- * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010
- *
- * See LifeTimeIntervals::renumber for details on the numbering.
- */
-class LifeRanges {
- class LiveRegs
- {
- typedef std::vector<int> Storage;
- Storage regs;
-
- public:
- void insert(int r)
- {
- if (find(r) == end())
- regs.push_back(r);
- }
-
- void unite(const LiveRegs &other)
- {
- if (other.empty())
- return;
- if (empty()) {
- regs = other.regs;
- return;
- }
- for (int r : other.regs)
- insert(r);
- }
-
- typedef Storage::iterator iterator;
- iterator find(int r)
- { return std::find(regs.begin(), regs.end(), r); }
-
- iterator begin()
- { return regs.begin(); }
-
- iterator end()
- { return regs.end(); }
-
- void erase(iterator it)
- { regs.erase(it); }
-
- void remove(int r)
- {
- iterator it = find(r);
- if (it != end())
- erase(it);
- }
-
- bool empty() const
- { return regs.empty(); }
-
- int size() const
- { return int(regs.size()); }
-
- int at(int idx) const
- { return regs.at(idx); }
- };
-
- std::vector<LiveRegs> _liveIn;
- std::vector<LifeTimeInterval *> _intervals;
- LifeTimeIntervals::Ptr _sortedIntervals;
-
- LifeTimeInterval &interval(const Temp *temp)
- {
- LifeTimeInterval *lti = _intervals[temp->index];
- Q_ASSERT(lti);
- return *lti;
- }
-
- void ensureInterval(const IR::Temp &temp)
- {
- Q_ASSERT(!temp.isInvalid());
- LifeTimeInterval *&lti = _intervals[temp.index];
- if (lti)
- return;
- lti = new LifeTimeInterval;
- lti->setTemp(temp);
- }
-
- int defPosition(IR::Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(IR::Stmt *s) const
- {
- return _sortedIntervals->positionForStatement(s);
- }
-
- int start(IR::BasicBlock *bb) const
- {
- return _sortedIntervals->startPosition(bb);
- }
-
- int end(IR::BasicBlock *bb) const
- {
- return _sortedIntervals->endPosition(bb);
- }
-
-public:
- LifeRanges(IR::Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops)
- : _intervals(function->tempCount)
- {
- _sortedIntervals = LifeTimeIntervals::create(function);
- _liveIn.resize(function->basicBlockCount());
-
- for (int i = function->basicBlockCount() - 1; i >= 0; --i) {
- BasicBlock *bb = function->basicBlock(i);
- buildIntervals(bb, startEndLoops.value(bb, 0));
- }
-
- _intervals.clear();
- }
-
- LifeTimeIntervals::Ptr intervals() const { return _sortedIntervals; }
-
- void dump() const
- {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream qout(&buf);
-
- qout << "Life ranges:" << endl;
- qout << "Intervals:" << endl;
- const auto intervals = _sortedIntervals->intervals();
- for (const LifeTimeInterval *range : intervals) {
- range->dump(qout);
- qout << endl;
- }
-
- IRPrinter printer(&qout);
- for (size_t i = 0, ei = _liveIn.size(); i != ei; ++i) {
- qout << "L" << i <<" live-in: ";
- auto live = _liveIn.at(i);
- if (live.empty())
- qout << "(none)";
- std::sort(live.begin(), live.end());
- for (int i = 0; i < live.size(); ++i) {
- if (i > 0) qout << ", ";
- qout << '%' << live.at(i);
- }
- qout << endl;
- }
- buf.close();
- qDebug("%s", buf.data().constData());
- }
-
-private:
- void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd)
- {
- LiveRegs live;
- for (BasicBlock *successor : bb->out) {
- live.unite(_liveIn[successor->index()]);
- const int bbIndex = successor->in.indexOf(bb);
- Q_ASSERT(bbIndex >= 0);
-
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- if (Temp *t = phi->incoming.at(bbIndex)->asTemp()) {
- ensureInterval(*t);
- live.insert(t->index);
- }
- } else {
- break;
- }
- }
- }
-
- const QVector<Stmt *> &statements = bb->statements();
-
- for (int reg : live)
- _intervals[reg]->addRange(start(bb), end(bb));
-
- InputOutputCollector collector;
- for (int i = statements.size() - 1; i >= 0; --i) {
- Stmt *s = statements.at(i);
- if (Phi *phi = s->asPhi()) {
- ensureInterval(*phi->targetTemp);
- LiveRegs::iterator it = live.find(phi->targetTemp->index);
- if (it == live.end()) {
- // a phi node target that is only defined, but never used
- interval(phi->targetTemp).setFrom(start(bb));
- } else {
- live.erase(it);
- }
- _sortedIntervals->add(&interval(phi->targetTemp));
- continue;
- }
- collector.collect(s);
- //### TODO: use DefUses from the optimizer, because it already has all this information
- if (Temp *opd = collector.output) {
- ensureInterval(*opd);
- LifeTimeInterval &lti = interval(opd);
- lti.setFrom(defPosition(s));
- live.remove(lti.temp().index);
- _sortedIntervals->add(&lti);
- }
- //### TODO: use DefUses from the optimizer, because it already has all this information
- for (size_t i = 0, ei = collector.inputs.size(); i != ei; ++i) {
- Temp *opd = collector.inputs[i];
- ensureInterval(*opd);
- interval(opd).addRange(start(bb), usePosition(s));
- live.insert(opd->index);
- }
- }
-
- if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null.
- for (int reg : live)
- _intervals[reg]->addRange(start(bb), usePosition(loopEnd->terminator()));
- }
-
- _liveIn[bb->index()] = std::move(live);
- }
-};
-
-void removeUnreachleBlocks(IR::Function *function)
-{
- QVector<BasicBlock *> newSchedule;
- newSchedule.reserve(function->basicBlockCount());
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- newSchedule.append(bb);
- function->setScheduledBlocks(newSchedule);
-}
-
-class ConvertArgLocals
-{
-public:
- ConvertArgLocals(IR::Function *function)
- : function(function)
- , convertArgs(!function->usesArgumentsObject)
- {
- tempForFormal.resize(function->formals.size(), -1);
- tempForLocal.resize(function->locals.size(), -1);
- }
-
- void toTemps()
- {
- if (function->variablesCanEscape())
- return;
-
- QVector<Stmt *> extraMoves;
- if (convertArgs) {
- const int formalCount = function->formals.size();
- extraMoves.reserve(formalCount + function->basicBlock(0)->statementCount());
- extraMoves.resize(formalCount);
-
- for (int i = 0; i != formalCount; ++i) {
- const int newTemp = function->tempCount++;
- tempForFormal[i] = newTemp;
-
- ArgLocal *source = function->New<ArgLocal>();
- source->init(ArgLocal::Formal, i, 0);
-
- Temp *target = function->New<Temp>();
- target->init(Temp::VirtualRegister, newTemp);
-
- Move *m = function->NewStmt<Move>();
- m->init(target, source);
- extraMoves[i] = m;
- }
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (!bb->isRemoved()) {
- for (Stmt *s : bb->statements()) {
- visit(s);
- }
- }
- }
-
- if (convertArgs && function->formals.size() > 0)
- function->basicBlock(0)->prependStatements(extraMoves);
-
- function->locals.clear();
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- check(e->expr);
- } else if (auto m = s->asMove()) {
- check(m->target); check(m->source);
- } else if (auto c = s->asCJump()) {
- check(c->cond);
- } else if (auto r = s->asRet()) {
- check(r->expr);
- }
- }
-
- void visit(Expr *e)
- {
- if (auto c = e->asConvert()) {
- check(c->expr);
- } else if (auto u = e->asUnop()) {
- check(u->expr);
- } else if (auto b = e->asBinop()) {
- check(b->left); check(b->right);
- } else if (auto c = e->asCall()) {
- check(c->base);
- for (ExprList *it = c->args; it; it = it->next) {
- check(it->expr);
- }
- } else if (auto n = e->asNew()) {
- check(n->base);
- for (ExprList *it = n->args; it; it = it->next) {
- check(it->expr);
- }
- } else if (auto s = e->asSubscript()) {
- check(s->base); check(s->index);
- } else if (auto m = e->asMember()) {
- check(m->base);
- }
- }
-
- void check(Expr *&e) {
- if (ArgLocal *al = e->asArgLocal()) {
- if (al->kind == ArgLocal::Local) {
- Temp *t = function->New<Temp>();
- t->init(Temp::VirtualRegister, fetchTempForLocal(al->index));
- e = t;
- } else if (convertArgs && al->kind == ArgLocal::Formal) {
- Temp *t = function->New<Temp>();
- t->init(Temp::VirtualRegister, fetchTempForFormal(al->index));
- e = t;
- }
- } else {
- visit(e);
- }
- }
-
- int fetchTempForLocal(int local)
- {
- int &ref = tempForLocal[local];
- if (ref == -1)
- ref = function->tempCount++;
- return ref;
- }
-
- int fetchTempForFormal(int formal)
- {
- return tempForFormal[formal];
- }
-
- IR::Function *function;
- bool convertArgs;
- std::vector<int> tempForFormal;
- std::vector<int> tempForLocal;
-};
-
-class CloneBasicBlock: protected CloneExpr
-{
-public:
- BasicBlock *operator()(IR::BasicBlock *originalBlock)
- {
- block = new BasicBlock(originalBlock->function, 0);
-
- for (Stmt *s : originalBlock->statements()) {
- visit(s);
- clonedStmt->location = s->location;
- }
-
- return block;
- }
-
-private:
- void visit(Stmt *s)
- {
- if (auto e = s->asExp()) {
- clonedStmt = block->EXP(clone(e->expr));
- } else if (auto m = s->asMove()) {
- clonedStmt = block->MOVE(clone(m->target), clone(m->source));
- } else if (auto j = s->asJump()) {
- clonedStmt = block->JUMP(j->target);
- } else if (auto c = s->asCJump()) {
- clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse);
- } else if (auto r = s->asRet()) {
- clonedStmt = block->RET(clone(r->expr));
- } else if (auto p = s->asPhi()) {
- Phi *phi = block->function->NewStmt<Phi>();
- clonedStmt = phi;
-
- phi->targetTemp = clone(p->targetTemp);
- for (Expr *in : p->incoming)
- phi->incoming.append(clone(in));
- block->appendStatement(phi);
- } else {
- Q_UNREACHABLE();
- }
- }
-
-private:
- IR::Stmt *clonedStmt;
-};
-
-static void verifyCFG(IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved()) {
- Q_ASSERT(bb->in.isEmpty());
- Q_ASSERT(bb->out.isEmpty());
- continue;
- }
-
- Q_ASSERT(function->basicBlock(bb->index()) == bb);
-
- // Check the terminators:
- Stmt *terminator = bb->terminator();
- if (terminator == nullptr) {
- Stmt *last = bb->statements().last();
- Call *call = last->asExp()->expr->asCall();
- Name *baseName = call->base->asName();
- Q_ASSERT(baseName->builtin == Name::builtin_rethrow);
- Q_UNUSED(baseName);
- } else if (Jump *jump = terminator->asJump()) {
- Q_UNUSED(jump);
- Q_ASSERT(jump->target);
- Q_ASSERT(!jump->target->isRemoved());
- Q_ASSERT(bb->out.size() == 1);
- Q_ASSERT(bb->out.first() == jump->target);
- } else if (CJump *cjump = terminator->asCJump()) {
- Q_UNUSED(cjump);
- Q_ASSERT(bb->out.size() == 2);
- Q_ASSERT(cjump->iftrue);
- Q_ASSERT(!cjump->iftrue->isRemoved());
- Q_ASSERT(cjump->iftrue == bb->out[0]);
- Q_ASSERT(cjump->iffalse);
- Q_ASSERT(!cjump->iffalse->isRemoved());
- Q_ASSERT(cjump->iffalse == bb->out[1]);
- } else if (terminator->asRet()) {
- Q_ASSERT(bb->out.size() == 0);
- } else {
- Q_UNREACHABLE();
- }
-
- // Check the outgoing edges:
- for (BasicBlock *out : bb->out) {
- Q_UNUSED(out);
- Q_ASSERT(!out->isRemoved());
- Q_ASSERT(out->in.contains(bb));
- }
-
- // Check the incoming edges:
- for (BasicBlock *in : bb->in) {
- Q_UNUSED(in);
- Q_ASSERT(!in->isRemoved());
- Q_ASSERT(in->out.contains(bb));
- }
- }
-}
-
-static void verifyImmediateDominators(const DominatorTree &dt, IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- cfg2dot(function);
- dt.dumpImmediateDominators();
- DominatorTree referenceTree(function);
-
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- BasicBlock *idom = dt.immediateDominator(bb);
- BasicBlock *referenceIdom = referenceTree.immediateDominator(bb);
- Q_UNUSED(idom);
- Q_UNUSED(referenceIdom);
- Q_ASSERT(idom == referenceIdom);
- }
-}
-
-static void verifyNoPointerSharing(IR::Function *function)
-{
- if (!DoVerification)
- return;
-
- class {
- public:
- void operator()(IR::Function *f)
- {
- for (BasicBlock *bb : f->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- visit(s);
- }
- }
- }
-
- private:
- void visit(Stmt *s)
- {
- check(s);
- STMT_VISIT_ALL_KINDS(s);
- }
-
- void visit(Expr *e)
- {
- check(e);
- EXPR_VISIT_ALL_KINDS(e);
- }
-
- private:
- void check(Stmt *s)
- {
- Q_ASSERT(!stmts.contains(s));
- stmts.insert(s);
- }
-
- void check(Expr *e)
- {
- Q_ASSERT(!exprs.contains(e));
- exprs.insert(e);
- }
-
- QSet<Stmt *> stmts;
- QSet<Expr *> exprs;
- } V;
- V(function);
-}
-
-// Loop-peeling is done by unfolding the loop once. The "original" loop basic blocks stay where they
-// are, and a copy of the loop is placed after it. Special care is taken while copying the loop body:
-// by having the copies of the basic-blocks point to the same nodes as the "original" basic blocks,
-// updating the immediate dominators is easy: if the edge of a copied basic-block B points to a
-// block C that has also been copied, set the immediate dominator of B to the corresponding
-// immediate dominator of C. Finally, for any node outside the loop that gets a new edge attached,
-// the immediate dominator has to be re-calculated.
-class LoopPeeling
-{
- DominatorTree &dt;
-
-public:
- LoopPeeling(DominatorTree &dt)
- : dt(dt)
- {}
-
- void run(const QVector<LoopDetection::LoopInfo *> &loops)
- {
- for (LoopDetection::LoopInfo *loopInfo : loops)
- peelLoop(loopInfo);
- }
-
-private:
- // All copies have their outgoing edges pointing to the same successor block as the originals.
- // For each copied block, check where the outgoing edges point to. If it's a block inside the
- // (original) loop, rewire it to the corresponding copy. Otherwise, which is when it points
- // out of the loop, leave it alone.
- // As an extra, collect all edges that point out of the copied loop, because the targets need
- // to have their immediate dominator rechecked.
- void rewire(BasicBlock *newLoopBlock, const QVector<BasicBlock *> &from, const QVector<BasicBlock *> &to, QVector<BasicBlock *> &loopExits)
- {
- for (int i = 0, ei = newLoopBlock->out.size(); i != ei; ++i) {
- BasicBlock *&out = newLoopBlock->out[i];
- const int idx = from.indexOf(out);
- if (idx == -1) {
- if (!loopExits.contains(out))
- loopExits.append(out);
- } else {
- out->in.removeOne(newLoopBlock);
- BasicBlock *newTo = to.at(idx);
- newTo->in.append(newLoopBlock);
- out = newTo;
-
- Stmt *terminator = newLoopBlock->terminator();
- if (Jump *jump = terminator->asJump()) {
- Q_ASSERT(i == 0);
- jump->target = newTo;
- } else if (CJump *cjump = terminator->asCJump()) {
- Q_ASSERT(i == 0 || i == 1);
- if (i == 0)
- cjump->iftrue = newTo;
- else
- cjump->iffalse = newTo;
- }
- }
- }
- }
-
- void peelLoop(LoopDetection::LoopInfo *loop)
- {
- IR::Function *f = loop->loopHeader->function;
- CloneBasicBlock clone;
-
- LoopDetection::LoopInfo unpeeled(*loop);
- unpeeled.loopHeader = clone(unpeeled.loopHeader);
- unpeeled.loopHeader->setContainingGroup(loop->loopHeader->containingGroup());
- unpeeled.loopHeader->markAsGroupStart(true);
- f->addBasicBlock(unpeeled.loopHeader);
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *&bodyBlock = unpeeled.loopBody[i];
- bodyBlock = clone(bodyBlock);
- bodyBlock->setContainingGroup(unpeeled.loopHeader);
- Q_ASSERT(bodyBlock->statementCount() == loop->loopBody[i]->statementCount());
- }
-
- // The cloned blocks will have no incoming edges, but they do have outgoing ones (copying
- // the terminators will automatically insert that edge). The blocks where the originals
- // pointed to will have an extra incoming edge from the copied blocks.
-
- BasicBlock::IncomingEdges inCopy = loop->loopHeader->in;
- for (BasicBlock *in : inCopy) {
- if (loop->loopHeader != in // this can happen for really tight loops (where there are no body blocks). This is a back-edge in that case.
- && unpeeled.loopHeader != in && !unpeeled.loopBody.contains(in) // if the edge is not coming from within the copied set, leave it alone
- && !dt.dominates(loop->loopHeader, in)) // an edge coming from within the loop (so a back-edge): this is handled when rewiring all outgoing edges
- continue;
-
- unpeeled.loopHeader->in.append(in);
- loop->loopHeader->in.removeOne(in);
-
- Stmt *terminator = in->terminator();
- if (Jump *jump = terminator->asJump()) {
- jump->target = unpeeled.loopHeader;
- in->out[0] = unpeeled.loopHeader;
- } else if (CJump *cjump = terminator->asCJump()) {
- if (cjump->iftrue == loop->loopHeader) {
- cjump->iftrue = unpeeled.loopHeader;
- Q_ASSERT(in->out[0] == loop->loopHeader);
- in->out[0] = unpeeled.loopHeader;
- } else if (cjump->iffalse == loop->loopHeader) {
- cjump->iffalse = unpeeled.loopHeader;
- Q_ASSERT(in->out[1] == loop->loopHeader);
- in->out[1] = unpeeled.loopHeader;
- } else {
- Q_UNREACHABLE();
- }
- }
- }
-
- QVector<BasicBlock *> loopExits;
- loopExits.reserve(8);
- loopExits.append(unpeeled.loopHeader);
-
- rewire(unpeeled.loopHeader, loop->loopBody, unpeeled.loopBody, loopExits);
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
- rewire(bodyBlock, loop->loopBody, unpeeled.loopBody, loopExits);
- f->addBasicBlock(bodyBlock);
- }
-
- // The original loop is now peeled off, and won't jump back to the loop header. Meaning, it
- // is not a loop anymore, so unmark it.
- loop->loopHeader->markAsGroupStart(false);
- for (BasicBlock *bb : qAsConst(loop->loopBody))
- bb->setContainingGroup(loop->loopHeader->containingGroup());
-
- // Set the immediate dominator of the new loop header to the old one. The real immediate
- // dominator will be calculated later.
- dt.setImmediateDominator(unpeeled.loopHeader, loop->loopHeader);
- // calculate the idoms in a separate loop, because addBasicBlock in the previous loop will
- // set the block index, which in turn is used by the dominator tree.
- for (int i = 0, ei = unpeeled.loopBody.size(); i != ei; ++i) {
- BasicBlock *bodyBlock = unpeeled.loopBody.at(i);
- BasicBlock *idom = dt.immediateDominator(loop->loopBody.at(i));
- const int idx = loop->loopBody.indexOf(idom);
- if (idom == loop->loopHeader)
- idom = unpeeled.loopHeader;
- else if (idx != -1)
- idom = unpeeled.loopBody.at(idx);
- Q_ASSERT(idom);
- dt.setImmediateDominator(bodyBlock, idom);
- }
-
- BasicBlockSet siblings(f);
- for (BasicBlock *bb : qAsConst(loopExits))
- dt.collectSiblings(bb, siblings);
-
- siblings.insert(unpeeled.loopHeader);
- dt.recalculateIDoms(siblings, loop->loopHeader);
- dt.dumpImmediateDominators();
- verifyImmediateDominators(dt, f);
- }
-};
-
-class RemoveLineNumbers: private SideEffectsChecker
-{
-public:
- static void run(IR::Function *function)
- {
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- for (Stmt *s : bb->statements()) {
- if (!hasSideEffects(s)) {
- s->location = QQmlJS::AST::SourceLocation();
- }
- }
- }
- }
-
-private:
- ~RemoveLineNumbers() {}
-
- static bool hasSideEffects(Stmt *stmt)
- {
- RemoveLineNumbers checker;
- if (auto e = stmt->asExp()) {
- checker.visit(e->expr);
- } else if (auto m = stmt->asMove()) {
- checker.visit(m->source);
- if (!checker.seenSideEffects()) {
- checker.visit(m->target);
- }
- } else if (auto c = stmt->asCJump()) {
- checker.visit(c->cond);
- } else if (auto r = stmt->asRet()) {
- checker.visit(r->expr);
- }
- return checker.seenSideEffects();
- }
-
- void visitTemp(Temp *) Q_DECL_OVERRIDE Q_DECL_FINAL {}
-};
-
-void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt)
-{
- enum { DebugBlockMerging = 0 };
-
- if (function->hasTry)
- return;
-
- showMeTheCode(function, "Before basic block merging");
-
- // Now merge a basic block with its successor when there is one outgoing edge, and the
- // successor has one incoming edge.
- for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
- BasicBlock *bb = function->basicBlock(i);
-
- bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info
-
- if (bb->isRemoved()) continue; // the block has been removed, so ignore it
- if (bb->out.size() != 1) continue; // more than one outgoing edge
- BasicBlock *successor = bb->out.first();
- if (successor->in.size() != 1) continue; // more than one incoming edge
-
- // Loop header? No efficient way to update the other blocks that refer to this as containing group,
- // so don't do merging yet.
- if (successor->isGroupStart()) continue;
-
- // Ok, we can merge the two basic blocks.
- if (DebugBlockMerging) {
- qDebug("Merging L%d into L%d", successor->index(), bb->index());
- }
- Q_ASSERT(bb->terminator()->asJump());
- bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with:
- for (Stmt *s : successor->statements()) {
- bb->appendStatement(s); // add all statements from the successor to the current basic block
- if (auto cjump = s->asCJump())
- cjump->parent = bb;
- }
- bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator
- for (auto newSuccessor : bb->out) {
- for (auto &backlink : newSuccessor->in) {
- if (backlink == successor) {
- backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there.
- }
- }
- }
- if (du) {
- // all statements in successor have moved to bb, so make sure that the containing blocks
- // stored in DefUses get updated (meaning: point to bb)
- du->replaceBasicBlock(successor, bb);
- }
- if (dt) {
- // update the immediate dominators to: any block that was dominated by the successor
- // will now need to point to bb's immediate dominator. The reason is that bb itself
- // won't be anyones immediate dominator, because it had just one outgoing edge.
- dt->mergeIntoPredecessor(successor);
- }
- function->removeBasicBlock(successor);
- --i; // re-run on the current basic-block, so any chain gets collapsed.
- }
-
- showMeTheCode(function, "After basic block merging");
- verifyCFG(function);
-}
-
-} // anonymous namespace
-
-void LifeTimeInterval::setFrom(int from) {
- Q_ASSERT(from > 0);
-
- if (_ranges.isEmpty()) { // this is the case where there is no use, only a define
- _ranges.prepend(LifeTimeIntervalRange(from, from));
- if (_end == InvalidPosition)
- _end = from;
- } else {
- _ranges.first().start = from;
- }
-}
-
-void LifeTimeInterval::addRange(int from, int to) {
- Q_ASSERT(from > 0);
- Q_ASSERT(to > 0);
- Q_ASSERT(to >= from);
-
- if (_ranges.isEmpty()) {
- _ranges.prepend(LifeTimeIntervalRange(from, to));
- _end = to;
- return;
- }
-
- LifeTimeIntervalRange *p = &_ranges.first();
- if (to + 1 >= p->start && p->end + 1 >= from) {
- p->start = qMin(p->start, from);
- p->end = qMax(p->end, to);
- while (_ranges.count() > 1) {
- LifeTimeIntervalRange *p1 = p + 1;
- if (p->end + 1 < p1->start || p1->end + 1 < p->start)
- break;
- p1->start = qMin(p->start, p1->start);
- p1->end = qMax(p->end, p1->end);
- _ranges.remove(0);
- p = &_ranges.first();
- }
- } else {
- if (to < p->start) {
- _ranges.prepend(LifeTimeIntervalRange(from, to));
- } else {
- Q_ASSERT(from > _ranges.last().end);
- _ranges.push_back(LifeTimeIntervalRange(from, to));
- }
- }
-
- _end = _ranges.last().end;
-}
-
-LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
-{
- Q_ASSERT(atPosition < newStart || newStart == InvalidPosition);
- Q_ASSERT(atPosition <= _end);
- Q_ASSERT(newStart <= _end || newStart == InvalidPosition);
-
- if (_ranges.isEmpty() || atPosition < _ranges.first().start)
- return LifeTimeInterval();
-
- LifeTimeInterval newInterval = *this;
- newInterval.setSplitFromInterval(true);
-
- // search where to split the interval
- for (int i = 0, ei = _ranges.size(); i < ei; ++i) {
- if (_ranges.at(i).start <= atPosition) {
- if (_ranges.at(i).end >= atPosition) {
- // split happens in the middle of a range. Keep this range in both the old and the
- // new interval, and correct the end/start later
- _ranges.resize(i + 1);
- newInterval._ranges.remove(0, i);
- break;
- }
- } else {
- // split happens between two ranges.
- _ranges.resize(i);
- newInterval._ranges.remove(0, i);
- break;
- }
- }
-
- if (newInterval._ranges.first().end == atPosition)
- newInterval._ranges.remove(0);
-
- if (newStart == InvalidPosition) {
- // the temp stays inactive for the rest of its lifetime
- newInterval = LifeTimeInterval();
- } else {
- // find the first range where the temp will get active again:
- while (!newInterval._ranges.isEmpty()) {
- const LifeTimeIntervalRange &range = newInterval._ranges.first();
- if (range.start > newStart) {
- // The split position is before the start of the range. Either we managed to skip
- // over the correct range, or we got an invalid split request. Either way, this
- // Should Never Happen <TM>.
- Q_ASSERT(range.start > newStart);
- return LifeTimeInterval();
- } else if (range.start <= newStart && range.end >= newStart) {
- // yay, we found the range that should be the new first range in the new interval!
- break;
- } else {
- // the temp stays inactive for this interval, so remove it.
- newInterval._ranges.remove(0);
- }
- }
- Q_ASSERT(!newInterval._ranges.isEmpty());
- newInterval._ranges.first().start = newStart;
- _end = newStart;
- }
-
- // if we're in the middle of a range, set the end to the split position
- if (_ranges.last().end > atPosition)
- _ranges.last().end = atPosition;
-
- validate();
- newInterval.validate();
-
- return newInterval;
-}
-
-void LifeTimeInterval::dump(QTextStream &out) const {
- IRPrinter(&out).print(const_cast<Temp *>(&_temp));
- out << ": ends at " << _end << " with ranges ";
- if (_ranges.isEmpty())
- out << "(none)";
- for (int i = 0; i < _ranges.size(); ++i) {
- if (i > 0) out << ", ";
- out << _ranges[i].start << " - " << _ranges[i].end;
- }
- if (_reg != InvalidRegister)
- out << " (register " << _reg << ")";
-}
-
-
-bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
-{
- return r1->temp() < r2->temp();
-}
-
-LifeTimeIntervals::LifeTimeIntervals(IR::Function *function)
- : _basicBlockPosition(function->basicBlockCount())
- , _positionForStatement(function->statementCount(), IR::Stmt::InvalidId)
- , _lastPosition(0)
-{
- _intervals.reserve(function->tempCount + 32); // we reserve a bit more space for intervals, because the register allocator will add intervals with fixed ranges for each register.
- renumber(function);
-}
-
-// Renumbering works as follows:
-// - phi statements are not numbered
-// - statement numbers start at 0 (zero) and increment get an even number (lastPosition + 2)
-// - basic blocks start at firstStatementNumber - 1, or rephrased: lastPosition + 1
-// - basic blocks end at the number of the last statement
-// And during life-time calculation the next rule is used:
-// - any temporary starts its life-time at definingStatementPosition + 1
-//
-// This numbering simulates half-open intervals. For example:
-// 0: %1 = 1
-// 2: %2 = 2
-// 4: %3 = %1 + %2
-// 6: print(%3)
-// Here the half-open life-time intervals would be:
-// %1: (0-4]
-// %2: (2-4]
-// %3: (4-6]
-// Instead, we use the even statement positions for uses of temporaries, and the odd positions for
-// their definitions:
-// %1: [1-4]
-// %2: [3-4]
-// %3: [5-6]
-// This has the nice advantage that placing %3 (for example) is really easy: the start will
-// never overlap with the end of the uses of the operands used in the defining statement.
-//
-// The reason to start a basic-block at firstStatementPosition - 1 is to have correct start
-// positions for target temporaries of phi-nodes. Those temporaries will now start before the
-// first statement. This also means that any moves that get generated when transforming out of SSA
-// form, will not interfere with (read: overlap) any defining statements in the preceding
-// basic-block.
-void LifeTimeIntervals::renumber(IR::Function *function)
-{
- for (BasicBlock *bb : function->basicBlocks()) {
- if (bb->isRemoved())
- continue;
-
- _basicBlockPosition[bb->index()].start = _lastPosition + 1;
-
- for (Stmt *s : bb->statements()) {
- if (s->asPhi())
- continue;
-
- _lastPosition += 2;
- _positionForStatement[s->id()] = _lastPosition;
- }
-
- _basicBlockPosition[bb->index()].end = _lastPosition;
- }
-}
-
-LifeTimeIntervals::~LifeTimeIntervals()
-{
- qDeleteAll(_intervals);
-}
-
-Optimizer::Optimizer(IR::Function *function)
- : function(function)
- , inSSA(false)
-{}
-
-void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool peelLoops)
-{
- showMeTheCode(function, "Before running the optimizer");
-
- cleanupBasicBlocks(function);
-
- function->removeSharedExpressions();
- int statementCount = 0;
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- statementCount += bb->statementCount();
-// showMeTheCode(function);
-
- static bool doSSA = qEnvironmentVariableIsEmpty("QV4_NO_SSA");
-
- if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) {
-// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl;
-
- mergeBasicBlocks(function, nullptr, nullptr);
-
- ConvertArgLocals(function).toTemps();
- showMeTheCode(function, "After converting arguments to locals");
-
- // Calculate the dominator tree:
- DominatorTree df(function);
-
- {
- // This is in a separate scope, because loop-peeling doesn't (yet) update the LoopInfo
- // calculated by the LoopDetection. So by putting it in a separate scope, it is not
- // available after peeling.
-
- LoopDetection loopDetection(df);
- loopDetection.run(function);
- showMeTheCode(function, "After loop detection");
-// cfg2dot(function, loopDetection.allLoops());
-
- // ### disable loop peeling for now. It doesn't give any measurable performance
- // improvements at this time, but significantly increases the size of the
- // JIT generated code
- Q_UNUSED(peelLoops);
- if (0 && peelLoops) {
- QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops();
- LoopPeeling(df).run(innerLoops);
-
-// cfg2dot(function, loopDetection.allLoops());
- showMeTheCode(function, "After loop peeling");
- if (!innerLoops.isEmpty())
- verifyImmediateDominators(df, function);
- }
- }
-
- verifyCFG(function);
- verifyNoPointerSharing(function);
-
- df.computeDF();
-
- verifyCFG(function);
- verifyImmediateDominators(df, function);
-
- DefUses defUses(function);
-
-// qout << "Converting to SSA..." << endl;
- convertToSSA(function, df, defUses);
-// showMeTheCode(function);
-// defUses.dump();
-
-// qout << "Cleaning up phi nodes..." << endl;
- cleanupPhis(defUses);
- showMeTheCode(function, "After cleaning up phi-nodes");
-
- StatementWorklist worklist(function);
-
- if (doTypeInference) {
-// qout << "Running type inference..." << endl;
- TypeInference(qmlEngine, defUses).run(worklist);
- showMeTheCode(function, "After type inference");
-
-// qout << "Doing reverse inference..." << endl;
- ReverseInference(defUses).run(function);
-// showMeTheCode(function);
-
-// qout << "Doing type propagation..." << endl;
- TypePropagation(defUses).run(function, worklist);
-// showMeTheCode(function);
- verifyNoPointerSharing(function);
- }
-
- static const bool doOpt = qEnvironmentVariableIsEmpty("QV4_NO_OPT");
- if (doOpt) {
-// qout << "Running SSA optimization..." << endl;
- worklist.reset();
- optimizeSSA(worklist, defUses, df);
- showMeTheCode(function, "After optimization");
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
- }
-
- verifyNoPointerSharing(function);
- mergeBasicBlocks(function, &defUses, &df);
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
- // Basic-block cycles that are unreachable (i.e. for loops in a then-part where the
- // condition is calculated to be always false) are not yet removed. This will choke the
- // block scheduling, so remove those now.
-// qout << "Cleaning up unreachable basic blocks..." << endl;
- cleanupBasicBlocks(function);
-// showMeTheCode(function);
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
- // Transform the CFG into edge-split SSA.
- showMeTheCode(function, "Before edge splitting");
- splitCriticalEdges(function, df, worklist, defUses);
- showMeTheCode(function, "After edge splitting");
-
- verifyImmediateDominators(df, function);
- verifyCFG(function);
-
-// qout << "Doing block scheduling..." << endl;
-// df.dumpImmediateDominators();
- startEndLoops = BlockScheduler(function, df).go();
- showMeTheCode(function, "After basic block scheduling");
-// cfg2dot(function);
-
-#ifndef QT_NO_DEBUG
- checkCriticalEdges(function->basicBlocks());
-#endif
-
- if (!function->module->debugMode) {
- RemoveLineNumbers::run(function);
- showMeTheCode(function, "After line number removal");
- }
-
-// qout << "Finished SSA." << endl;
- inSSA = true;
- } else {
- removeUnreachleBlocks(function);
- inSSA = false;
- }
-}
-
-void Optimizer::convertOutOfSSA() {
- if (!inSSA)
- return;
-
- // There should be no critical edges at this point.
-
- for (BasicBlock *bb : function->basicBlocks()) {
- MoveMapping moves;
-
- for (BasicBlock *successor : bb->out) {
- const int inIdx = successor->in.indexOf(bb);
- Q_ASSERT(inIdx >= 0);
- for (Stmt *s : successor->statements()) {
- if (Phi *phi = s->asPhi()) {
- moves.add(clone(phi->incoming[inIdx], function),
- clone(phi->targetTemp, function)->asTemp());
- } else {
- break;
- }
- }
- }
-
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- os << "Move mapping for function ";
- if (function->name)
- os << *function->name;
- else
- os << (void *) function;
- os << " on basic-block L" << bb->index() << ":" << endl;
- moves.dump();
- buf.close();
- qDebug("%s", buf.data().constData());
- }
-
- moves.order();
-
- moves.insertMoves(bb, function, true);
- }
-
- for (BasicBlock *bb : function->basicBlocks()) {
- while (!bb->isEmpty()) {
- if (bb->statements().first()->asPhi()) {
- bb->removeStatement(0);
- } else {
- break;
- }
- }
- }
-}
-
-LifeTimeIntervals::Ptr Optimizer::lifeTimeIntervals() const
-{
- Q_ASSERT(isInSSA());
-
- LifeRanges lifeRanges(function, startEndLoops);
-// lifeRanges.dump();
-// showMeTheCode(function);
- return lifeRanges.intervals();
-}
-
-static int countPhis(BasicBlock *bb)
-{
- int count = 0;
- for (Stmt *s : bb->statements()) {
- if (s->isa<Phi>())
- ++count;
- else
- break;
- }
-
- return count;
-}
-
-// Basic blocks can have only 1 terminator. This function returns a bit vector, where a 1 on a
-// certain index indicates that the terminator (jump) at the end of the basic block with that index
-// can be omitted.
-BitVector Optimizer::calculateOptionalJumps()
-{
- const int maxSize = function->basicBlockCount();
- BitVector optional(maxSize, false);
- if (maxSize < 2)
- return optional;
-
- BitVector reachableWithoutJump(maxSize, false);
-
- for (int i = maxSize - 1; i >= 0; --i) {
- BasicBlock *bb = function->basicBlock(i);
- if (bb->isRemoved())
- continue;
-
- if (Jump *jump = bb->statements().last()->asJump()) {
- if (reachableWithoutJump.at(jump->target->index())) {
- if (bb->statements().size() - countPhis(bb)> 1)
- reachableWithoutJump.clear();
- optional.setBit(bb->index());
- reachableWithoutJump.setBit(bb->index());
- continue;
- }
- }
-
- reachableWithoutJump.clear();
- reachableWithoutJump.setBit(bb->index());
- }
-
- return optional;
-}
-
-void Optimizer::showMeTheCode(IR::Function *function, const char *marker)
-{
- ::showMeTheCode(function, marker);
-}
-
-static inline bool overlappingStorage(const Temp &t1, const Temp &t2)
-{
- // This is the same as the operator==, but for one detail: memory locations are not sensitive
- // to types, and neither are general-purpose registers.
-
- if (t1.index != t2.index)
- return false; // different position, where-ever that may (physically) be.
- if (t1.kind != t2.kind)
- return false; // formal/local/(physical-)register/stack do never overlap
- if (t1.kind != Temp::PhysicalRegister) // Other than registers, ...
- return t1.kind == t2.kind; // ... everything else overlaps: any memory location can hold everything.
-
- // So now the index is the same, and we know that both stored in a register. If both are
- // floating-point registers, they are the same. Or, if both are non-floating-point registers,
- // generally called general-purpose registers, they are also the same.
- return (t1.type == DoubleType && t2.type == DoubleType)
- || (t1.type != DoubleType && t2.type != DoubleType);
-}
-
-MoveMapping::Moves MoveMapping::sourceUsages(Expr *e, const Moves &moves)
-{
- Moves usages;
-
- if (Temp *t = e->asTemp()) {
- for (int i = 0, ei = moves.size(); i != ei; ++i) {
- const Move &move = moves[i];
- if (Temp *from = move.from->asTemp())
- if (overlappingStorage(*from, *t))
- usages.append(move);
- }
- }
-
- return usages;
-}
-
-void MoveMapping::add(Expr *from, Temp *to) {
- if (Temp *t = from->asTemp()) {
- if (overlappingStorage(*t, *to)) {
- // assignments like fp1 = fp1 or var{&1} = double{&1} can safely be skipped.
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- IRPrinter printer(&os);
- os << "Skipping ";
- printer.print(to);
- os << " <- ";
- printer.print(from);
- buf.close();
- qDebug("%s", buf.data().constData());
- }
- return;
- }
- }
-
- Move m(from, to);
- if (_moves.contains(m))
- return;
- _moves.append(m);
-}
-
-// Order the moves that are generated when resolving edges during register allocation (see [Wimmer1]
-// section 6 for details). Now these moves form one or more graphs, so we have to output them in
-// such an order that values don't get overwritten:
-// r1 <- r0
-// r2 <- r1
-// That input has to be ordered as follows in order to prevent the value in r1 from being lost:
-// r2 <- r1
-// r1 <- r0
-//
-// So, the algorithm is to output the leaves first, and take them out of the input. This will result
-// in some moves to become leaves (in the above example: when leaf r2 <- r1 is generated and taken
-// away, the r1 <- r0 is now a leaf), so we can output those and take those out, and repeat until
-// there are no more leafs.
-//
-// The tricky part is that there might be cycles:
-// r4 <- r5
-// r5 <- r4
-// These have to be turned into a "register swap":
-// r4 <=> r5
-//
-// So after running the above algorithm where we progressively remove the leaves, we are left with
-// zero or more cycles. To resolve those, we break one of the edges of the cycle, and for all other
-// edges we generate swaps. Note that the swaps will always occur as the last couple of moves,
-// because otherwise they might clobber sources for moves:
-// r4 <=> r5
-// r6 <- r5
-// Here, the value of r5 is already overwritten with the one in r4, so the correct order is:
-// r6 <- r5
-// r4 <=> r5
-void MoveMapping::order()
-{
- QList<Move> output;
- output.reserve(_moves.size());
-
- while (!_moves.isEmpty()) {
- // Take out all leaf edges, because we can output them without any problems.
- int nextLeaf = findLeaf();
- if (nextLeaf == -1)
- break; // No more leafs left, we're done here.
- output.append(_moves.takeAt(nextLeaf));
- // Now there might be new leaf edges: any move that had the input of the previously found
- // leaf as an output, so loop around.
- }
-
- while (!_moves.isEmpty()) {
- // We're now left with one or more cycles.
- // Step one: break the/a cycle.
- _moves.removeFirst();
- // Step two: find the other edges of the cycle, starting with the one of that is now a leaf.
- while (!_moves.isEmpty()) {
- int nextLeaf = findLeaf();
- if (nextLeaf == -1)
- break; // We're done with this cycle.
- Move m = _moves.takeAt(nextLeaf);
- // Step three: get the edges from the cycle and turn it into a swap
- m.needsSwap = true;
- output.append(m);
- // Because we took out a leaf, find the next one.
- }
- // We're done with the cycle, let's see if there are more.
- }
-
- _moves = output;
-}
-
-int MoveMapping::findLeaf() const
-{
- for (int i = 0, e = _moves.size(); i != e; ++i) {
- // Take an edge from the list...
- const Temp *target = _moves.at(i).to;
- // ... and see if its target is used as a source...
- bool targetUsedAsSource = false;
- for (int j = 0; j != e; ++j) {
- if (i == j)
- continue;
-
- Expr *source = _moves.at(j).from;
- if (const Temp *sourceTemp = source->asTemp()) {
- if (overlappingStorage(*target, *sourceTemp)) {
- targetUsedAsSource = true;
- break;
- }
- }
- }
- // ... if not, we have a leaf edge ...
- if (!targetUsedAsSource)
- return i;
- // .. otherwise we try the next one.
- }
-
- return -1; // No leaf found
-}
-
-QList<IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, IR::Function *function, bool atEnd) const
-{
- QList<IR::Move *> newMoves;
- newMoves.reserve(_moves.size());
-
- int insertionPoint = atEnd ? bb->statements().size() - 1 : 0;
- for (const Move &m : _moves) {
- IR::Move *move = function->NewStmt<IR::Move>();
- move->init(clone(m.to, function), clone(m.from, function));
- move->swap = m.needsSwap;
- bb->insertStatementBefore(insertionPoint++, move);
- newMoves.append(move);
- }
-
- return newMoves;
-}
-
-void MoveMapping::dump() const
-{
- if (DebugMoveMapping) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- QTextStream os(&buf);
- IRPrinter printer(&os);
- os << "Move mapping has " << _moves.size() << " moves..." << endl;
- for (const Move &m : _moves) {
- os << "\t";
- printer.print(m.to);
- if (m.needsSwap)
- os << " <-> ";
- else
- os << " <-- ";
- printer.print(m.from);
- os << endl;
- }
- qDebug("%s", buf.data().constData());
- }
-}
-
-// References:
-// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of
-// CGO'10, ACM Press, 2010
-// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register
-// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual
-// Execution Environments, pages 132-141. ACM Press, 2005.
-// [Briggs] P. Briggs, K.D. Cooper, T.J. Harvey, and L.T. Simpson. Practical Improvements to the
-// Construction and Destruction of Static Single Assignment Form.
-// [Appel] A.W. Appel. Modern Compiler Implementation in Java. Second edition, Cambridge
-// University Press.
-// [Ramalingam] G. Ramalingam and T. Reps. An Incremental Algorithm for Maintaining the Dominator
-// Tree of a Reducible Flowgraph.
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
deleted file mode 100644
index 24257e99e9..0000000000
--- a/src/qml/compiler/qv4ssa_p.h
+++ /dev/null
@@ -1,472 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QV4SSA_P_H
-#define QV4SSA_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qv4jsir_p.h"
-#include "qv4isel_util_p.h"
-#include <private/qv4util_p.h>
-#include <QtCore/QSharedPointer>
-
-QT_BEGIN_NAMESPACE
-class QTextStream;
-class QQmlEnginePrivate;
-
-namespace QV4 {
-namespace IR {
-
-struct LifeTimeIntervalRange {
- int start;
- int end;
-
- LifeTimeIntervalRange(int start = -1, int end = -1)
- : start(start)
- , end(end)
- {}
-
- bool covers(int position) const { return start <= position && position <= end; }
-};
-} // IR namespace
-} // QV4 namespace
-
-Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeIntervalRange, Q_PRIMITIVE_TYPE);
-
-namespace QV4 {
-namespace IR {
-
-class Q_AUTOTEST_EXPORT LifeTimeInterval {
-public:
- typedef QVarLengthArray<LifeTimeIntervalRange, 4> Ranges;
-
-private:
- Temp _temp;
- Ranges _ranges;
- int _end;
- int _reg;
- unsigned _isFixedInterval : 1;
- unsigned _isSplitFromInterval : 1;
-
-public:
- enum { InvalidPosition = -1 };
- enum { InvalidRegister = -1 };
-
- explicit LifeTimeInterval(int rangeCapacity = 4)
- : _end(InvalidPosition)
- , _reg(InvalidRegister)
- , _isFixedInterval(0)
- , _isSplitFromInterval(0)
- { _ranges.reserve(rangeCapacity); }
-
- bool isValid() const { return _end != InvalidRegister; }
-
- void setTemp(const Temp &temp) { this->_temp = temp; }
- Temp temp() const { return _temp; }
- bool isFP() const { return _temp.type == IR::DoubleType; }
-
- void setFrom(int from);
- void addRange(int from, int to);
- const Ranges &ranges() const { return _ranges; }
-
- int start() const { return _ranges.first().start; }
- int end() const { return _end; }
- bool covers(int position) const
- {
- for (int i = 0, ei = _ranges.size(); i != ei; ++i) {
- if (_ranges.at(i).covers(position))
- return true;
- }
- return false;
- }
-
- int reg() const { return _reg; }
- void setReg(int reg) { Q_ASSERT(!_isFixedInterval); _reg = reg; }
-
- bool isFixedInterval() const { return _isFixedInterval; }
- void setFixedInterval(bool isFixedInterval) { _isFixedInterval = isFixedInterval; }
-
- LifeTimeInterval split(int atPosition, int newStart);
- bool isSplitFromInterval() const { return _isSplitFromInterval; }
- void setSplitFromInterval(bool isSplitFromInterval) { _isSplitFromInterval = isSplitFromInterval; }
-
- void dump(QTextStream &out) const;
- static bool lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2);
- static bool lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2);
-
- void validate() const {
-#if !defined(QT_NO_DEBUG)
- // Validate the new range
- if (_end != InvalidPosition) {
- Q_ASSERT(!_ranges.isEmpty());
- for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) {
- Q_ASSERT(range.start >= 0);
- Q_ASSERT(range.end >= 0);
- Q_ASSERT(range.start <= range.end);
- }
- }
-#endif
- }
-};
-
-inline bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
-{
- if (r1->_ranges.first().start == r2->_ranges.first().start) {
- if (r1->isSplitFromInterval() == r2->isSplitFromInterval())
- return r1->_ranges.last().end < r2->_ranges.last().end;
- else
- return r1->isSplitFromInterval();
- } else
- return r1->_ranges.first().start < r2->_ranges.first().start;
-}
-
-class LifeTimeIntervals
-{
- Q_DISABLE_COPY(LifeTimeIntervals)
-
- LifeTimeIntervals(IR::Function *function);
- void renumber(IR::Function *function);
-
-public:
- typedef QSharedPointer<LifeTimeIntervals> Ptr;
- static Ptr create(IR::Function *function)
- { return Ptr(new LifeTimeIntervals(function)); }
-
- ~LifeTimeIntervals();
-
- // takes ownership of the pointer
- void add(LifeTimeInterval *interval)
- { _intervals.append(interval); }
-
- // After calling Optimizer::lifeTimeIntervals() the result will have all intervals in descending order of start position.
- QVector<LifeTimeInterval *> intervals() const
- { return _intervals; }
-
- int size() const
- { return _intervals.size(); }
-
- int positionForStatement(Stmt *stmt) const
- {
- Q_ASSERT(stmt->id() >= 0);
- if (static_cast<unsigned>(stmt->id()) < _positionForStatement.size())
- return _positionForStatement[stmt->id()];
-
- return Stmt::InvalidId;
- }
-
- int startPosition(BasicBlock *bb) const
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size());
-
- return _basicBlockPosition.at(bb->index()).start;
- }
-
- int endPosition(BasicBlock *bb) const
- {
- Q_ASSERT(bb->index() >= 0);
- Q_ASSERT(static_cast<unsigned>(bb->index()) < _basicBlockPosition.size());
-
- return _basicBlockPosition.at(bb->index()).end;
- }
-
- int lastPosition() const
- {
- return _lastPosition;
- }
-
-private:
- struct BasicBlockPositions {
- int start;
- int end;
-
- BasicBlockPositions()
- : start(IR::Stmt::InvalidId)
- , end(IR::Stmt::InvalidId)
- {}
- };
-
- std::vector<BasicBlockPositions> _basicBlockPosition;
- std::vector<int> _positionForStatement;
- QVector<LifeTimeInterval *> _intervals;
- int _lastPosition;
-};
-
-class Q_QML_PRIVATE_EXPORT Optimizer
-{
- Q_DISABLE_COPY(Optimizer)
-
-public:
- Optimizer(Function *function);
-
- void run(QQmlEnginePrivate *qmlEngine, bool doTypeInference = true, bool peelLoops = true);
- void convertOutOfSSA();
-
- bool isInSSA() const
- { return inSSA; }
-
- QHash<BasicBlock *, BasicBlock *> loopStartEndBlocks() const { return startEndLoops; }
-
- LifeTimeIntervals::Ptr lifeTimeIntervals() const;
-
- BitVector calculateOptionalJumps();
-
- static void showMeTheCode(Function *function, const char *marker);
-
-private:
- Function *function;
- bool inSSA;
- QHash<BasicBlock *, BasicBlock *> startEndLoops;
-};
-
-class Q_AUTOTEST_EXPORT MoveMapping
-{
-#ifdef V4_AUTOTEST
-public:
-#endif
- struct Move {
- Expr *from;
- Temp *to;
- bool needsSwap;
-
- Move(Expr *from, Temp *to, bool needsSwap = false)
- : from(from), to(to), needsSwap(needsSwap)
- {}
-
- bool operator==(const Move &other) const
- { return from == other.from && to == other.to; }
- };
- typedef QList<Move> Moves;
-
- Moves _moves;
-
- static Moves sourceUsages(Expr *e, const Moves &moves);
-
-public:
- void add(Expr *from, Temp *to);
- void order();
- QList<IR::Move *> insertMoves(BasicBlock *bb, Function *function, bool atEnd) const;
-
- void dump() const;
-
-private:
- int findLeaf() const;
-};
-
-/*
- * stack slot allocation:
- *
- * foreach bb do
- * foreach stmt do
- * if the current statement is not a phi-node:
- * purge ranges that end before the current statement
- * check for life ranges to activate, and if they don't have a stackslot associated then allocate one
- * renumber temps to stack
- * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated
- * if it's a jump:
- * foreach phi node in the successor:
- * allocate slots for each temp (both sources and targets) if they don't have one allocated already
- * insert moves before the jump
- */
-class AllocateStackSlots: protected ConvertTemps
-{
- IR::LifeTimeIntervals::Ptr _intervals;
- QVector<IR::LifeTimeInterval *> _unhandled;
- QVector<IR::LifeTimeInterval *> _live;
- QBitArray _slotIsInUse;
- IR::Function *_function;
-
- int defPosition(IR::Stmt *s) const
- {
- return usePosition(s) + 1;
- }
-
- int usePosition(IR::Stmt *s) const
- {
- return _intervals->positionForStatement(s);
- }
-
-public:
- AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals)
- : _intervals(intervals)
- , _slotIsInUse(intervals->size(), false)
- , _function(0)
- {
- _live.reserve(8);
- _unhandled = _intervals->intervals();
- }
-
- void forFunction(IR::Function *function)
- {
- IR::Optimizer::showMeTheCode(function, "Before stack slot allocation");
- _function = function;
- toStackSlots(function);
- }
-
-protected:
- int allocateFreeSlot() override
- {
- for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) {
- if (!_slotIsInUse[i]) {
- if (_nextUnusedStackSlot <= i) {
- Q_ASSERT(_nextUnusedStackSlot == i);
- _nextUnusedStackSlot = i + 1;
- }
- _slotIsInUse[i] = true;
- return i;
- }
- }
-
- Q_UNREACHABLE();
- return -1;
- }
-
- void process(IR::Stmt *s) override
- {
-// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id);
-
- if (IR::Phi *phi = s->asPhi()) {
- visitPhi(phi);
- } else {
- // purge ranges no longer alive:
- for (int i = 0; i < _live.size(); ) {
- const IR::LifeTimeInterval *lti = _live.at(i);
- if (lti->end() < usePosition(s)) {
-// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index];
- _live.remove(i);
- Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]);
- _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false;
- continue;
- } else {
- ++i;
- }
- }
-
- // active new ranges:
- while (!_unhandled.isEmpty()) {
- IR::LifeTimeInterval *lti = _unhandled.last();
- if (lti->start() > defPosition(s))
- break; // we're done
- Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index));
- _stackSlotForTemp[lti->temp().index] = allocateFreeSlot();
-// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index];
- _live.append(lti);
- _unhandled.removeLast();
- }
-
- visit(s);
- }
-
- if (IR::Jump *jump = s->asJump()) {
- IR::MoveMapping moves;
- for (IR::Stmt *succStmt : jump->target->statements()) {
- if (IR::Phi *phi = succStmt->asPhi()) {
- forceActivation(*phi->targetTemp);
- for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) {
- IR::Expr *e = phi->incoming[i];
- if (IR::Temp *t = e->asTemp()) {
- forceActivation(*t);
- }
- if (jump->target->in[i] == _currentBasicBlock)
- moves.add(phi->incoming[i], phi->targetTemp);
- }
- } else {
- break;
- }
- }
- moves.order();
- const QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
- for (IR::Move *move : newMoves)
- visit(move);
- }
- }
-
- void forceActivation(const IR::Temp &t)
- {
- if (_stackSlotForTemp.contains(t.index))
- return;
-
- int i = _unhandled.size() - 1;
- for (; i >= 0; --i) {
- IR::LifeTimeInterval *lti = _unhandled[i];
- if (lti->temp() == t) {
- _live.append(lti);
- _unhandled.remove(i);
- break;
- }
- }
- Q_ASSERT(i >= 0); // check that we always found the entry
-
- _stackSlotForTemp[t.index] = allocateFreeSlot();
-// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index];
- }
-
- void visitPhi(IR::Phi *phi) override
- {
- Q_UNUSED(phi);
-#if !defined(QT_NO_DEBUG)
- Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index));
- Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]);
- for (IR::Expr *e : phi->incoming) {
- if (IR::Temp *t = e->asTemp())
- Q_ASSERT(_stackSlotForTemp.contains(t->index));
- }
-#endif // defined(QT_NO_DEBUG)
- }
-};
-
-} // IR namespace
-} // QV4 namespace
-
-
-Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE);
-
-QT_END_NAMESPACE
-
-#endif // QV4SSA_P_H