aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri24
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp443
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h141
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator.cpp71
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h741
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp703
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h87
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp1952
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h274
-rw-r--r--src/qml/compiler/qv4codegen.cpp7
-rw-r--r--src/qml/compiler/qv4compilationunitmapper.cpp99
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_p.h87
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp99
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp134
-rw-r--r--src/qml/compiler/qv4compileddata.cpp409
-rw-r--r--src/qml/compiler/qv4compileddata_p.h580
-rw-r--r--src/qml/compiler/qv4compiler.cpp239
-rw-r--r--src/qml/compiler/qv4compiler_p.h11
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h31
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp226
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h22
-rw-r--r--src/qml/compiler/qv4isel_p.cpp25
-rw-r--r--src/qml/compiler/qv4isel_p.h43
-rw-r--r--src/qml/compiler/qv4isel_util_p.h50
-rw-r--r--src/qml/compiler/qv4jsir.cpp335
-rw-r--r--src/qml/compiler/qv4jsir_p.h447
-rw-r--r--src/qml/compiler/qv4ssa.cpp1146
-rw-r--r--src/qml/compiler/qv4ssa_p.h19
28 files changed, 5384 insertions, 3061 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 585fef7603..1de5dfa6fa 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -25,13 +25,29 @@ SOURCES += \
HEADERS += \
$$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4isel_moth_p.h \
- $$PWD/qv4instr_moth_p.h
+ $$PWD/qqmlpropertycachecreator_p.h \
+ $$PWD/qqmlpropertyvalidator_p.h \
+ $$PWD/qv4compilationunitmapper_p.h
SOURCES += \
$$PWD/qqmltypecompiler.cpp \
- $$PWD/qv4instr_moth.cpp \
- $$PWD/qv4isel_moth.cpp
+ $$PWD/qqmlpropertycachecreator.cpp \
+ $$PWD/qqmlpropertyvalidator.cpp \
+ $$PWD/qv4compilationunitmapper.cpp
+unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
+else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
+
+qtConfig(qml-interpreter) {
+ HEADERS += \
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4isel_moth_p.h
+ SOURCES += \
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4isel_moth.cpp
+}
+
+
+qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index eaf0e72296..eb83962630 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -44,12 +44,12 @@
#include <private/qqmljsparser_p.h>
#include <private/qqmljslexer_p.h>
#include <QCoreApplication>
+#include <QCryptographicHash>
#ifndef V4_BOOTSTRAP
#include <private/qqmlglobal_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qqmlcompiler_p.h>
#endif
#ifdef CONST
@@ -72,21 +72,24 @@ using namespace QmlIR;
return false; \
}
-void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &loc)
+void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc)
{
inheritedTypeNameIndex = typeNameIndex;
location.line = loc.startLine;
location.column = loc.startColumn;
- idIndex = id;
- indexOfDefaultProperty = -1;
+ idNameIndex = idIndex;
+ id = -1;
+ indexOfDefaultPropertyOrAlias = -1;
+ defaultPropertyIsAlias = false;
+ flags = QV4::CompiledData::Object::NoFlag;
properties = pool->New<PoolList<Property> >();
+ aliases = pool->New<PoolList<Alias> >();
qmlSignals = pool->New<PoolList<Signal> >();
bindings = pool->New<PoolList<Binding> >();
functions = pool->New<PoolList<Function> >();
functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >();
- runtimeFunctionIndices = 0;
declarationsOverride = 0;
}
@@ -145,15 +148,42 @@ QString Object::appendProperty(Property *prop, const QString &propertyName, bool
const int index = target->properties->append(prop);
if (isDefaultProperty) {
- if (target->indexOfDefaultProperty != -1) {
+ if (target->indexOfDefaultPropertyOrAlias != -1) {
*errorLocation = defaultToken;
return tr("Duplicate default property");
}
- target->indexOfDefaultProperty = index;
+ target->indexOfDefaultPropertyOrAlias = index;
}
return QString(); // no error
}
+QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation)
+{
+ Object *target = declarationsOverride;
+ if (!target)
+ target = this;
+
+ for (Alias *p = target->aliases->first; p; p = p->next)
+ if (p->nameIndex == alias->nameIndex)
+ return tr("Duplicate alias name");
+
+ if (aliasName.constData()->isUpper())
+ return tr("Alias names cannot begin with an upper case letter");
+
+ const int index = target->aliases->append(alias);
+
+ if (isDefaultProperty) {
+ if (target->indexOfDefaultPropertyOrAlias != -1) {
+ *errorLocation = defaultToken;
+ return tr("Duplicate default property");
+ }
+ target->indexOfDefaultPropertyOrAlias = index;
+ target->defaultPropertyIsAlias = true;
+ }
+
+ return QString(); // no error
+}
+
void Object::appendFunction(QmlIR::Function *f)
{
Object *target = declarationsOverride;
@@ -164,7 +194,7 @@ void Object::appendFunction(QmlIR::Function *f)
QString Object::appendBinding(Binding *b, bool isListBinding)
{
- const bool bindingToDefaultProperty = (b->propertyNameIndex == 0);
+ const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0));
if (!isListBinding && !bindingToDefaultProperty
&& b->type != QV4::CompiledData::Binding::Type_GroupProperty
&& b->type != QV4::CompiledData::Binding::Type_AttachedProperty
@@ -190,7 +220,7 @@ Binding *Object::findBinding(quint32 nameIndex) const
void Object::insertSorted(Binding *b)
{
- Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::valueLocation>(b);
+ Binding *insertionPoint = bindings->findSortedInsertionPoint<quint32, Binding, &Binding::offset>(b);
bindings->insertAfter(insertionPoint, b);
}
@@ -222,32 +252,6 @@ static void replaceWithSpace(QString &str, int idx, int n)
*data++ = space;
}
-void Document::collectTypeReferences()
-{
- foreach (Object *obj, objects) {
- if (obj->inheritedTypeNameIndex != emptyStringIndex) {
- QV4::CompiledData::TypeReference &r = typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
- r.needsCreation = true;
- r.errorWhenNotFound = true;
- }
-
- for (const Property *prop = obj->firstProperty(); prop; prop = prop->next) {
- 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.
- QV4::CompiledData::TypeReference &r = typeReferences.add(prop->customTypeNameIndex, obj->location);
- r.needsCreation = true;
- r.errorWhenNotFound = true;
- }
- }
-
- for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
- typeReferences.add(binding->propertyNameIndex, binding->location);
- }
- }
-}
-
void Document::removeScriptPragmas(QString &script)
{
const QLatin1String pragma("pragma");
@@ -298,7 +302,6 @@ Document::Document(bool debugMode)
, program(0)
, indexOfRootObject(0)
, jsGenerator(&jsModule)
- , unitFlags(0)
{
}
@@ -637,7 +640,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
}
if (node->versionToken.isValid()) {
- extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion);
+ int major, minor;
+ extractVersion(textRefAt(node->versionToken), &major, &minor);
+ import->majorVersion = major;
+ import->minorVersion = minor;
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version"));
return false;
@@ -807,130 +813,87 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
}
} else {
const QStringRef &memberType = node->memberType;
- const QStringRef &name = node->name;
-
- bool typeFound = false;
- QV4::CompiledData::Property::Type type;
-
if (memberType == QLatin1String("alias")) {
- type = QV4::CompiledData::Property::Alias;
- typeFound = true;
- }
+ return appendAlias(node);
+ } else {
+ const QStringRef &name = node->name;
- for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
- const TypeNameToType *t = propTypeNameToTypes + ii;
- if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
- type = t->type;
- typeFound = true;
+ bool typeFound = false;
+ QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var;
+
+ for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
+ const TypeNameToType *t = propTypeNameToTypes + ii;
+ if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
+ type = t->type;
+ typeFound = true;
+ }
}
- }
- if (!typeFound && memberType.at(0).isUpper()) {
- const QStringRef &typeModifier = node->typeModifier;
+ if (!typeFound && memberType.at(0).isUpper()) {
+ const QStringRef &typeModifier = node->typeModifier;
- if (typeModifier.isEmpty()) {
- type = QV4::CompiledData::Property::Custom;
- } else if (typeModifier == QLatin1String("list")) {
- type = QV4::CompiledData::Property::CustomList;
- } else {
- recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
+ if (typeModifier.isEmpty()) {
+ type = QV4::CompiledData::Property::Custom;
+ } else if (typeModifier == QLatin1String("list")) {
+ type = QV4::CompiledData::Property::CustomList;
+ } else {
+ recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
+ return false;
+ }
+ typeFound = true;
+ } else if (!node->typeModifier.isNull()) {
+ recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
return false;
}
- typeFound = true;
- } else if (!node->typeModifier.isNull()) {
- recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
- return false;
- }
-
- if (!typeFound) {
- recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type"));
- return false;
- }
-
- Property *property = New<Property>();
- property->flags = 0;
- if (node->isReadonlyMember)
- property->flags |= QV4::CompiledData::Property::IsReadOnly;
- property->type = type;
- if (type >= QV4::CompiledData::Property::Custom)
- property->customTypeNameIndex = registerString(memberType.toString());
- else
- property->customTypeNameIndex = emptyStringIndex;
-
- const QString propName = name.toString();
- property->nameIndex = registerString(propName);
- QQmlJS::AST::SourceLocation loc = node->firstSourceLocation();
- property->location.line = loc.startLine;
- property->location.column = loc.startColumn;
-
- property->aliasPropertyValueIndex = emptyStringIndex;
-
- if (type == QV4::CompiledData::Property::Alias) {
- if (!node->statement && !node->binding)
- COMPILE_EXCEPTION(loc, tr("No property alias location"));
+ if (!typeFound) {
+ recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type"));
+ return false;
+ }
- QQmlJS::AST::SourceLocation rhsLoc;
- if (node->binding)
- rhsLoc = node->binding->firstSourceLocation();
- else if (node->statement)
- rhsLoc = node->statement->firstSourceLocation();
+ Property *property = New<Property>();
+ property->flags = 0;
+ if (node->isReadonlyMember)
+ property->flags |= QV4::CompiledData::Property::IsReadOnly;
+ property->type = type;
+ if (type >= QV4::CompiledData::Property::Custom)
+ property->customTypeNameIndex = registerString(memberType.toString());
else
- rhsLoc = node->semicolonToken;
- property->aliasLocation.line = rhsLoc.startLine;
- property->aliasLocation.column = rhsLoc.startColumn;
-
- QStringList alias;
-
- if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) {
- alias = astNodeToStringList(stmt->expression);
- if (alias.isEmpty()) {
- if (isStatementNodeScript(node->statement)) {
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
- } else {
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
- }
- }
- } else {
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
- }
+ property->customTypeNameIndex = emptyStringIndex;
- if (alias.count() < 1 || alias.count() > 3)
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+ const QString propName = name.toString();
+ property->nameIndex = registerString(propName);
- property->aliasIdValueIndex = registerString(alias.first());
+ QQmlJS::AST::SourceLocation loc = node->firstSourceLocation();
+ property->location.line = loc.startLine;
+ property->location.column = loc.startColumn;
- QString propertyValue = alias.value(1);
- if (alias.count() == 3) {
- propertyValue += QLatin1Char('.');
- propertyValue += alias.at(2);
- }
- property->aliasPropertyValueIndex = registerString(propertyValue);
- }
- QQmlJS::AST::SourceLocation errorLocation;
- QString error;
+ QQmlJS::AST::SourceLocation errorLocation;
+ QString error;
- if (illegalNames.contains(propName))
- error = tr("Illegal property name");
- else
- error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation);
+ if (illegalNames.contains(propName))
+ error = tr("Illegal property name");
+ else
+ error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation);
- if (!error.isEmpty()) {
- if (errorLocation.startLine == 0)
- errorLocation = node->identifierToken;
+ if (!error.isEmpty()) {
+ if (errorLocation.startLine == 0)
+ errorLocation = node->identifierToken;
- recordError(errorLocation, error);
- return false;
- }
+ recordError(errorLocation, error);
+ return false;
+ }
- qSwap(_propertyDeclaration, property);
- if (node->binding) {
- // process QML-like initializers (e.g. property Object o: Object {})
- QQmlJS::AST::Node::accept(node->binding, this);
- } else if (node->statement && type != QV4::CompiledData::Property::Alias) {
- appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ qSwap(_propertyDeclaration, property);
+ if (node->binding) {
+ // process QML-like initializers (e.g. property Object o: Object {})
+ 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);
+ }
+ qSwap(_propertyDeclaration, property);
}
- qSwap(_propertyDeclaration, property);
}
return false;
@@ -952,6 +915,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
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;
+ 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());
+
_object->appendFunction(f);
} else {
recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
@@ -1027,13 +1000,13 @@ 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->value.d = lit->value;
+ 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->value.d = -lit->value;
+ binding->setNumberValueInternal(-lit->value);
}
}
}
@@ -1085,6 +1058,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
{
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
+ binding->offset = nameLocation.offset;
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
@@ -1104,6 +1078,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
+ binding->offset = nameLocation.offset;
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
@@ -1133,6 +1108,79 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
}
}
+bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node)
+{
+ Alias *alias = New<Alias>();
+ alias->flags = 0;
+ if (node->isReadonlyMember)
+ alias->flags |= QV4::CompiledData::Alias::IsReadOnly;
+
+ const QString propName = node->name.toString();
+ alias->nameIndex = registerString(propName);
+
+ QQmlJS::AST::SourceLocation loc = node->firstSourceLocation();
+ alias->location.line = loc.startLine;
+ alias->location.column = loc.startColumn;
+
+ alias->propertyNameIndex = emptyStringIndex;
+
+ if (!node->statement && !node->binding)
+ COMPILE_EXCEPTION(loc, tr("No property alias location"));
+
+ QQmlJS::AST::SourceLocation rhsLoc;
+ if (node->binding)
+ rhsLoc = node->binding->firstSourceLocation();
+ else if (node->statement)
+ rhsLoc = node->statement->firstSourceLocation();
+ else
+ rhsLoc = node->semicolonToken;
+ alias->referenceLocation.line = rhsLoc.startLine;
+ alias->referenceLocation.column = rhsLoc.startColumn;
+
+ QStringList aliasReference;
+
+ if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) {
+ aliasReference = astNodeToStringList(stmt->expression);
+ if (aliasReference.isEmpty()) {
+ if (isStatementNodeScript(node->statement)) {
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+ } else {
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
+ }
+ }
+ } else {
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+ }
+
+ if (aliasReference.count() < 1 || aliasReference.count() > 3)
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+
+ alias->idIndex = registerString(aliasReference.first());
+
+ QString propertyValue = aliasReference.value(1);
+ if (aliasReference.count() == 3)
+ propertyValue += QLatin1Char('.') + aliasReference.at(2);
+ alias->propertyNameIndex = registerString(propertyValue);
+
+ QQmlJS::AST::SourceLocation errorLocation;
+ QString error;
+
+ if (illegalNames.contains(propName))
+ error = tr("Illegal property name");
+ else
+ error = _object->appendAlias(alias, propName, node->isDefaultMember, node->defaultToken, &errorLocation);
+
+ if (!error.isEmpty()) {
+ if (errorLocation.startLine == 0)
+ errorLocation = node->identifierToken;
+
+ recordError(errorLocation, error);
+ return false;
+ }
+
+ return false;
+}
+
Object *IRBuilder::bindingsTarget() const
{
if (_propertyDeclaration && _object->declarationsOverride)
@@ -1178,10 +1226,10 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST
if (illegalNames.contains(idQString))
COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property"));
- if (_object->idIndex != emptyStringIndex)
+ if (_object->idNameIndex != emptyStringIndex)
COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
- _object->idIndex = registerString(idQString);
+ _object->idNameIndex = registerString(idQString);
_object->locationOfIdProperty.line = idLocation.startLine;
_object->locationOfIdProperty.column = idLocation.startColumn;
@@ -1202,8 +1250,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
if (import->qualifierIndex != emptyStringIndex
&& stringAt(import->qualifierIndex) == currentName) {
qualifiedIdElement = qualifiedIdElement->next;
- currentName += QLatin1Char('.');
- currentName += qualifiedIdElement->name;
+ currentName += QLatin1Char('.') + qualifiedIdElement->name;
if (!qualifiedIdElement->name.unicode()->isUpper())
COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name"));
@@ -1229,6 +1276,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
if (!binding) {
binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
+ binding->offset = qualifiedIdElement->identifierToken.offset;
binding->location.line = qualifiedIdElement->identifierToken.startLine;
binding->location.column = qualifiedIdElement->identifierToken.startColumn;
binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine;
@@ -1300,7 +1348,18 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
return true;
}
-QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
+bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
+{
+ if (property->type != QV4::CompiledData::Property::Custom)
+ return false;
+ QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
+ if (!exprStmt)
+ return false;
+ QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
+ return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
+}
+
+QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes)
{
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output);
@@ -1309,12 +1368,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count();
const int objectOffsetTableSize = output.objects.count() * sizeof(quint32);
- QHash<Object*, quint32> objectOffsets;
+ QHash<const Object*, quint32> objectOffsets;
int objectsSize = 0;
foreach (Object *o, output.objects) {
objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize);
- objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount());
+ objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
int signalTableSize = 0;
for (const Signal *s = o->firstSignal(); s; s = s->next)
@@ -1333,7 +1392,6 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(data);
qmlUnit->unitSize = totalSize;
- qmlUnit->flags |= output.unitFlags;
qmlUnit->flags |= QV4::CompiledData::Unit::IsQml;
qmlUnit->offsetToImports = unitSize;
qmlUnit->nImports = output.imports.count();
@@ -1343,6 +1401,20 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData();
qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount();
+#ifndef V4_BOOTSTRAP
+ if (!dependentTypes.isEmpty()) {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (dependentTypes.addToHash(&hash, engine)) {
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum));
+ memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum));
+ }
+ }
+#else
+ Q_UNUSED(dependentTypes);
+ Q_UNUSED(engine);
+#endif
+
// write imports
char *importPtr = data + qmlUnit->offsetToImports;
foreach (const QV4::CompiledData::Import *imp, output.imports) {
@@ -1354,13 +1426,17 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
// write objects
quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects);
char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize;
- foreach (Object *o, output.objects) {
+ for (int i = 0; i < output.objects.count(); ++i) {
+ const Object *o = output.objects.at(i);
*objectTable++ = objectOffsets.value(o);
QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
- objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty;
- objectToWrite->idIndex = o->idIndex;
+ objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias;
+ objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias;
+ objectToWrite->flags = o->flags;
+ objectToWrite->idNameIndex = o->idNameIndex;
+ objectToWrite->id = o->id;
objectToWrite->location = o->location;
objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
@@ -1374,6 +1450,10 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
objectToWrite->offsetToProperties = nextOffset;
nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property);
+ objectToWrite->nAliases = o->aliasCount();
+ objectToWrite->offsetToAliases = nextOffset;
+ nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias);
+
objectToWrite->nSignals = o->signalCount();
objectToWrite->offsetToSignals = nextOffset;
nextOffset += objectToWrite->nSignals * sizeof(quint32);
@@ -1382,9 +1462,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
objectToWrite->offsetToBindings = nextOffset;
nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
+ objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count;
+ objectToWrite->offsetToNamedObjectsInComponent = nextOffset;
+ nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32);
+
quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions);
for (const Function *f = o->firstFunction(); f; f = f->next)
- *functionsTable++ = o->runtimeFunctionIndices->at(f->index);
+ *functionsTable++ = o->runtimeFunctionIndices.at(f->index);
char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
for (const Property *p = o->firstProperty(); p; p = p->next) {
@@ -1393,6 +1477,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
propertiesPtr += sizeof(QV4::CompiledData::Property);
}
+ char *aliasesPtr = objectPtr + objectToWrite->offsetToAliases;
+ for (const Alias *a = o->firstAlias(); a; a = a->next) {
+ QV4::CompiledData::Alias *aliasToWrite = reinterpret_cast<QV4::CompiledData::Alias*>(aliasesPtr);
+ *aliasToWrite = *a;
+ aliasesPtr += sizeof(QV4::CompiledData::Alias);
+ }
+
char *bindingPtr = objectPtr + objectToWrite->offsetToBindings;
bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingNoAlias);
bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isSignalHandler);
@@ -1421,7 +1512,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
signalPtr += size;
}
- objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount());
+ quint32 *namedObjectInComponentPtr = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent);
+ for (int i = 0; i < o->namedObjectsInComponent.count; ++i) {
+ *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
+ }
+
+ objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
objectPtr += signalTableSize;
}
@@ -1435,10 +1531,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
output.jsGenerator.stringTable.serialize(qmlUnit);
+ qmlUnit->generateChecksum();
+
return qmlUnit;
}
-char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const
+char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
{
for (const Binding *b = o->firstBinding(); b; b = b->next) {
if (!(b->*(filter))())
@@ -1446,7 +1544,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter
QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
*bindingToWrite = *b;
if (b->type == QV4::CompiledData::Binding::Type_Script)
- bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices->at(b->value.compiledScriptIndex);
+ bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex);
bindingPtr += sizeof(QV4::CompiledData::Binding);
}
return bindingPtr;
@@ -1614,7 +1712,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
if (tdata->isComplete()) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId));
+ initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId));
newResolver->flags |= AllPropertiesAreFinal;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
@@ -1627,7 +1725,11 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
member->kind = QV4::IR::Member::MemberOfSingletonObject;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
- } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
+ }
+#if 0
+ else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
+ // Right now the attached property IDs are not stable and cannot be embedded in the
+ // code that is cached on disk.
QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta);
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
@@ -1635,6 +1737,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine));
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
+#endif
return result;
}
@@ -1742,20 +1845,20 @@ static QV4::IR::DiscoveredType resolveMetaObjectProperty(
if (property->isEnum())
return QV4::IR::VarType;
- switch (property->propType) {
+ switch (property->propType()) {
case QMetaType::Bool: result = QV4::IR::BoolType; break;
case QMetaType::Int: result = QV4::IR::SInt32Type; break;
case QMetaType::Double: result = QV4::IR::DoubleType; break;
case QMetaType::QString: result = QV4::IR::StringType; break;
default:
if (property->isQObject()) {
- if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) {
+ if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, cache);
return QV4::IR::DiscoveredType(newResolver);
}
- } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType)) {
+ } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) {
if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
@@ -1822,7 +1925,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
// Look for IDs first.
foreach (const IdMapping &mapping, _idObjects)
if (name == mapping.name) {
- _function->idObjectDependencies.insert(mapping.idIndex);
+ 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);
@@ -1907,7 +2012,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
#ifndef V4_BOOTSTRAP
-QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check)
+QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const
{
if (notInRevision) *notInRevision = false;
@@ -1926,7 +2031,7 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev
}
-QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision)
+QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const
{
if (notInRevision) *notInRevision = false;
@@ -1948,7 +2053,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis
d = property(propName, notInRevision);
if (d)
- return cache->signal(d->notifyIndex);
+ return cache->signal(d->notifyIndex());
}
return 0;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 057ed1be9f..cc16dc2104 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -113,7 +113,7 @@ struct PoolList
T *insertPos = 0;
for (T *it = first; it; it = it->next) {
- if (!(it->*sortMember < item->*sortMember))
+ if (!(it->*sortMember <= item->*sortMember))
break;
insertPos = it;
}
@@ -161,6 +161,43 @@ struct PoolList
}
return result;
}
+
+ struct Iterator {
+ T *ptr;
+
+ explicit Iterator(T *p) : ptr(p) {}
+
+ T *operator->() {
+ return ptr;
+ }
+
+ const T *operator->() const {
+ return ptr;
+ }
+
+ T &operator*() {
+ return *ptr;
+ }
+
+ const T &operator*() const {
+ return *ptr;
+ }
+
+ void operator++() {
+ ptr = ptr->next;
+ }
+
+ bool operator==(const Iterator &rhs) const {
+ return ptr == rhs.ptr;
+ }
+
+ bool operator!=(const Iterator &rhs) const {
+ return ptr != rhs.ptr;
+ }
+ };
+
+ Iterator begin() { return Iterator(first); }
+ Iterator end() { return Iterator(nullptr); }
};
template <typename T>
@@ -170,7 +207,18 @@ class FixedPoolArray
public:
int count;
- void init(QQmlJS::MemoryPool *pool, const QVector<T> &vector)
+ 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)));
@@ -183,6 +231,16 @@ public:
}
}
+ 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];
@@ -200,6 +258,9 @@ public:
return i;
return -1;
}
+
+ const T *begin() const { return data; }
+ const T *end() const { return data + count; }
};
struct Object;
@@ -217,6 +278,10 @@ struct Signal
QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const;
+ int parameterCount() const { return parameters->count; }
+ PoolList<SignalParameter>::Iterator parametersBegin() const { return parameters->begin(); }
+ PoolList<SignalParameter>::Iterator parametersEnd() const { return parameters->end(); }
+
Signal *next;
};
@@ -227,16 +292,32 @@ struct Property : public QV4::CompiledData::Property
struct Binding : public QV4::CompiledData::Binding
{
+ // The offset in the source file where the binding appeared. This is used for sorting to ensure
+ // that assignments to list properties are done in the correct order. We use the offset here instead
+ // of Binding::location as the latter has limited precision.
+ quint32 offset;
// Binding's compiledScriptIndex is index in object's functionsAndExpressions
Binding *next;
};
+struct Alias : public QV4::CompiledData::Alias
+{
+ Alias *next;
+};
+
struct Function
{
QQmlJS::AST::FunctionDeclaration *functionDeclaration;
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
+ FixedPoolArray<int> formals;
+
+ // --- QQmlPropertyCacheCreator interface
+ const int *formalsBegin() const { return formals.begin(); }
+ const int *formalsEnd() const { return formals.end(); }
+ // ---
+
Function *next;
};
@@ -265,14 +346,19 @@ struct Q_QML_PRIVATE_EXPORT Object
Q_DECLARE_TR_FUNCTIONS(Object)
public:
quint32 inheritedTypeNameIndex;
- quint32 idIndex;
- int indexOfDefaultProperty;
+ quint32 idNameIndex;
+ int id;
+ int indexOfDefaultPropertyOrAlias;
+ bool defaultPropertyIsAlias;
+ quint32 flags;
QV4::CompiledData::Location location;
QV4::CompiledData::Location locationOfIdProperty;
const Property *firstProperty() const { return properties->first; }
int propertyCount() const { return properties->count; }
+ Alias *firstAlias() const { return aliases->first; }
+ int aliasCount() const { return aliases->count; }
const Signal *firstSignal() const { return qmlSignals->first; }
int signalCount() const { return qmlSignals->count; }
Binding *firstBinding() const { return bindings->first; }
@@ -280,16 +366,28 @@ public:
const Function *firstFunction() const { return functions->first; }
int functionCount() const { return functions->count; }
+ PoolList<Binding>::Iterator bindingsBegin() const { return bindings->begin(); }
+ PoolList<Binding>::Iterator bindingsEnd() const { return bindings->end(); }
+ PoolList<Property>::Iterator propertiesBegin() const { return properties->begin(); }
+ 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<Signal>::Iterator signalsBegin() const { return qmlSignals->begin(); }
+ PoolList<Signal>::Iterator signalsEnd() const { return qmlSignals->end(); }
+ PoolList<Function>::Iterator functionsBegin() const { return functions->begin(); }
+ PoolList<Function>::Iterator functionsEnd() const { return functions->end(); }
+
// If set, then declarations for this object (and init bindings for these) should go into the
// specified object. Used for declarations inside group properties.
Object *declarationsOverride;
- void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
+ 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 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);
void appendFunction(QmlIR::Function *f);
QString appendBinding(Binding *b, bool isListBinding);
@@ -299,12 +397,17 @@ public:
QString bindingAsString(Document *doc, int scriptIndex) const;
PoolList<CompiledFunctionOrExpression> *functionsAndExpressions;
- FixedPoolArray<int> *runtimeFunctionIndices;
+ FixedPoolArray<int> runtimeFunctionIndices;
+
+ FixedPoolArray<quint32> namedObjectsInComponent;
+ int namedObjectsInComponentCount() const { return namedObjectsInComponent.count; }
+ const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); }
private:
friend struct IRLoader;
PoolList<Property> *properties;
+ PoolList<Alias> *aliases;
PoolList<Signal> *qmlSignals;
PoolList<Binding> *bindings;
PoolList<Function> *functions;
@@ -330,15 +433,10 @@ struct Q_QML_PRIVATE_EXPORT Document
QList<Pragma*> pragmas;
QQmlJS::AST::UiProgram *program;
int indexOfRootObject;
- QList<Object*> objects;
+ QVector<Object*> objects;
QV4::Compiler::JSUnitGenerator jsGenerator;
- quint32 unitFlags;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit;
- QHash<int, QStringList> extraSignalParameters;
-
- QV4::CompiledData::TypeReferenceMap typeReferences;
- void collectTypeReferences();
int registerString(const QString &str) { return jsGenerator.registerString(str); }
QString stringAt(int index) const { return jsGenerator.stringForIndex(index); }
@@ -410,6 +508,8 @@ public:
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, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
+ bool appendAlias(QQmlJS::AST::UiPublicMember *node);
+
Object *bindingsTarget() const;
bool setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST::Statement *value);
@@ -426,6 +526,7 @@ public:
QString stringAt(int index) const { return jsGenerator->stringForIndex(index); }
static bool isStatementNodeScript(QQmlJS::AST::Statement *statement);
+ static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement);
QList<QQmlJS::DiagnosticMessage> errors;
@@ -433,7 +534,7 @@ public:
QList<const QV4::CompiledData::Import *> _imports;
QList<Pragma*> _pragmas;
- QList<Object*> _objects;
+ QVector<Object*> _objects;
QV4::CompiledData::TypeReferenceMap _typeReferences;
@@ -447,21 +548,21 @@ public:
struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
{
- QV4::CompiledData::Unit *generate(Document &output);
+ QV4::CompiledData::Unit *generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes);
private:
typedef bool (Binding::*BindingFilter)() const;
- char *writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const;
+ char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const;
};
#ifndef V4_BOOTSTRAP
struct Q_QML_EXPORT PropertyResolver
{
- PropertyResolver(QQmlPropertyCache *cache)
+ PropertyResolver(const QQmlPropertyCache *cache)
: cache(cache)
{}
- QQmlPropertyData *property(int index)
+ QQmlPropertyData *property(int index) const
{
return cache->property(index);
}
@@ -471,12 +572,12 @@ struct Q_QML_EXPORT PropertyResolver
IgnoreRevision
};
- QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision);
+ QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision) const;
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
- QQmlPropertyData *signal(const QString &name, bool *notInRevision);
+ QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
- QQmlPropertyCache *cache;
+ const QQmlPropertyCache *cache;
};
#endif
diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp
new file mode 100644
index 0000000000..f8d63ec634
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertycachecreator.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpropertycachecreator_p.h"
+
+#include <private/qqmlengine_p.h>
+
+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, const QQmlPropertyCache *referencingObjectPropertyCache)
+ : referencingObjectIndex(referencingObjectIndex)
+ , instantiatingBinding(instantiatingBinding)
+ , instantiatingProperty(nullptr)
+{
+ if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+ Q_ASSERT(referencingObjectIndex >= 0);
+ Q_ASSERT(referencingObjectPropertyCache);
+ Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
+
+ bool notInRevision = false;
+ instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, &notInRevision);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
new file mode 100644
index 0000000000..10bcd1dbc1
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -0,0 +1,741 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QQMLPROPERTYCACHECREATOR_P_H
+#define QQMLPROPERTYCACHECREATOR_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 "qqmltypecompiler_p.h"
+#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlengine_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;
+};
+
+struct QQmlPropertyCacheCreatorBase
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
+public:
+ static QAtomicInt classIndexCounter;
+};
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, 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);
+
+ QString stringAt(int index) const { return objectContainer->stringAt(index); }
+
+ QQmlEnginePrivate * const enginePrivate;
+ const ObjectContainer * const objectContainer;
+ const QQmlImports * const imports;
+ QQmlPropertyCacheVector *propertyCaches;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports)
+ : enginePrivate(enginePrivate)
+ , objectContainer(objectContainer)
+ , imports(imports)
+ , propertyCaches(propertyCaches)
+{
+ propertyCaches->resize(objectContainer->objectCount());
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
+{
+ QQmlBindingInstantiationContext context;
+ return buildMetaObjectRecursively(objectContainer->rootObjectIndex(), context);
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
+{
+ const CompiledObject *obj = objectContainer->objectAt(objectIndex);
+
+ bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0;
+ if (!needVMEMetaObject) {
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ // If the on assignment is inside a group property, we need to distinguish between QObject based
+ // group properties and value type group properties. For the former the base type is derived from
+ // the property that references us, for the latter we only need a meta-object on the referencing object
+ // because interceptors can't go to the shared value type instances.
+ 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);
+ Q_ASSERT(typeRef);
+ QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ if (error.isSet())
+ return error;
+ }
+ } else {
+ // On assignments are implemented using value interceptors, which require a VME meta object.
+ needVMEMetaObject = true;
+ }
+ break;
+ }
+ }
+ }
+
+ QQmlPropertyCache *baseTypeCache;
+ {
+ QQmlCompileError error;
+ baseTypeCache = propertyCacheForObject(obj, context, &error);
+ if (error.isSet())
+ return error;
+ }
+
+ if (baseTypeCache) {
+ if (needVMEMetaObject) {
+ QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache);
+ if (error.isSet())
+ return error;
+ } else {
+ propertyCaches->set(objectIndex, baseTypeCache);
+ }
+ }
+
+ if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) {
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding)
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+ QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context);
+ if (error.isSet())
+ return error;
+ }
+ }
+
+ QQmlCompileError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+inline 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);
+ }
+ } else if (obj->inheritedTypeNameIndex != 0) {
+ auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->isFullyDynamicType) {
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
+ return nullptr;
+ }
+ if (obj->signalCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
+ return nullptr;
+ }
+ if (obj->functionCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
+ return nullptr;
+ }
+ }
+
+ return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) {
+ auto *typeRef = objectContainer->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlType *qmltype = typeRef->type;
+ if (!qmltype) {
+ QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
+ if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) {
+ if (qmltype->isComposite()) {
+ 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;
+ if (!attachedMo) {
+ *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
+ return nullptr;
+ }
+ return enginePrivate->cache(attachedMo);
+ }
+ return nullptr;
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, 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()));
+
+ propertyCaches->set(objectIndex, cache);
+ propertyCaches->setNeedsVMEMetaObject(objectIndex);
+
+ struct TypeData {
+ QV4::CompiledData::Property::Type dtype;
+ int metaType;
+ } builtinTypes[] = {
+ { QV4::CompiledData::Property::Var, QMetaType::QVariant },
+ { QV4::CompiledData::Property::Variant, QMetaType::QVariant },
+ { QV4::CompiledData::Property::Int, QMetaType::Int },
+ { QV4::CompiledData::Property::Bool, QMetaType::Bool },
+ { QV4::CompiledData::Property::Real, QMetaType::Double },
+ { QV4::CompiledData::Property::String, QMetaType::QString },
+ { QV4::CompiledData::Property::Url, QMetaType::QUrl },
+ { QV4::CompiledData::Property::Color, QMetaType::QColor },
+ { QV4::CompiledData::Property::Font, QMetaType::QFont },
+ { QV4::CompiledData::Property::Time, QMetaType::QTime },
+ { QV4::CompiledData::Property::Date, QMetaType::QDate },
+ { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
+ { QV4::CompiledData::Property::Rect, QMetaType::QRectF },
+ { QV4::CompiledData::Property::Point, QMetaType::QPointF },
+ { QV4::CompiledData::Property::Size, QMetaType::QSizeF },
+ { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
+ { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
+ { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
+ { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
+ { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
+};
+ static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
+
+ QByteArray newClassName;
+
+ if (objectIndex == objectContainer->rootObjectIndex()) {
+ const QString path = objectContainer->url().path();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ if (lastSlash > -1) {
+ const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
+ if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
+ newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
+ QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+ }
+ }
+ if (newClassName.isEmpty()) {
+ newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName.append("_QML_");
+ newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
+ }
+
+ cache->_dynamicClassName = newClassName;
+
+ int varPropCount = 0;
+
+ QmlIR::PropertyResolver resolver(baseTypeCache);
+
+ for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) {
+ if (p->type == QV4::CompiledData::Property::Var)
+ varPropCount++;
+
+ bool notInRevision = false;
+ QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) {
+ bool notInRevision = false;
+ QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ int effectivePropertyIndex = cache->propertyIndexCacheStart;
+ int effectiveMethodIndex = cache->methodIndexCacheStart;
+
+ // For property change signal override detection.
+ // We prepopulate a set of signal names which already exist in the object,
+ // 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;
+ while ((parentCache = parentCache->parent())) {
+ if (int pSigCount = parentCache->signalCount()) {
+ int pSigOffset = parentCache->signalOffset();
+ for (int i = pSigOffset; i < pSigCount; ++i) {
+ QQmlPropertyData *currPSig = parentCache->signal(i);
+ // XXX TODO: find a better way to get signal name from the property data :-/
+ for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
+ iter != parentCache->stringCache.end(); ++iter) {
+ if (currPSig == (*iter).second) {
+ seenSignals.insert(iter.key());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Set up notify signals for properties - first normal, then alias
+ for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
+ seenSignals.insert(changedSigName);
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed");
+ seenSignals.insert(changedSigName);
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ // Dynamic signals
+ for (auto s = obj->signalsBegin(), end = obj->signalsEnd(); s != end; ++s) {
+ const int paramCount = s->parameterCount();
+
+ QList<QByteArray> names;
+ names.reserve(paramCount);
+ QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
+
+ if (paramCount) {
+ paramTypes[0] = paramCount;
+
+ int i = 0;
+ for (auto param = s->parametersBegin(), end = s->parametersEnd(); param != end; ++param, ++i) {
+ names.append(stringAt(param->nameIndex).toUtf8());
+ if (param->type < builtinTypeCount) {
+ // built-in type
+ paramTypes[i + 1] = builtinTypes[param->type].metaType;
+ } else {
+ // 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))
+ return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
+
+ if (qmltype->isComposite()) {
+ 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();
+ }
+ }
+ }
+ }
+
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+ if (paramCount)
+ flags.hasArguments = true;
+
+ QString signalName = stringAt(s->nameIndex);
+ if (seenSignals.contains(signalName))
+ return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
+ seenSignals.insert(signalName);
+
+ cache->appendSignal(signalName, flags, effectiveMethodIndex++,
+ paramCount?paramTypes.constData():0, names);
+ }
+
+
+ // Dynamic slots
+ for (auto function = objectContainer->objectFunctionsBegin(obj), end = objectContainer->objectFunctionsEnd(obj); function != end; ++function) {
+ auto flags = QQmlPropertyData::defaultSlotFlags();
+
+ const QString slotName = stringAt(function->nameIndex);
+ if (seenSignals.contains(slotName))
+ return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
+ // Note: we don't append slotName to the seenSignals list, since we don't
+ // protect against overriding change signals or methods with properties.
+
+ QList<QByteArray> parameterNames;
+ for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) {
+ flags.hasArguments = true;
+ parameterNames << stringAt(*formal).toUtf8();
+ }
+
+ cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
+ }
+
+
+ // Dynamic properties
+ int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
+ int propertyIdx = 0;
+ for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) {
+ int propertyType = 0;
+ QQmlPropertyData::Flags propertyFlags;
+
+ if (p->type == QV4::CompiledData::Property::Var) {
+ propertyType = QMetaType::QVariant;
+ propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
+ } else if (p->type < builtinTypeCount) {
+ propertyType = builtinTypes[p->type].metaType;
+
+ if (p->type == QV4::CompiledData::Property::Variant)
+ propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
+ } else {
+ 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)) {
+ return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
+ }
+
+ Q_ASSERT(qmltype);
+ if (qmltype->isComposite()) {
+ QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+
+ if (p->type == QV4::CompiledData::Property::Custom) {
+ propertyType = compilationUnit->metaTypeId;
+ } else {
+ propertyType = compilationUnit->listMetaTypeId;
+ }
+
+ tdata->release();
+ } else {
+ if (p->type == QV4::CompiledData::Property::Custom) {
+ propertyType = qmltype->typeId();
+ } else {
+ propertyType = qmltype->qListTypeId();
+ }
+ }
+
+ if (p->type == QV4::CompiledData::Property::Custom)
+ propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
+ else
+ propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ }
+
+ if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
+ propertyFlags.isWritable = true;
+
+
+ QString propertyName = stringAt(p->nameIndex);
+ if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
+ cache->_defaultPropertyName = propertyName;
+ cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ propertyType, effectiveSignalIndex);
+
+ effectiveSignalIndex++;
+ }
+
+ QQmlCompileError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheAliasCreator
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
+
+ void appendAliasPropertiesToMetaObjects();
+
+ void 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);
+
+ void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
+
+ int objectForId(const CompiledObject &component, int id) const;
+
+ QQmlPropertyCacheVector *propertyCaches;
+ const ObjectContainer *objectContainer;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
+ : propertyCaches(propertyCaches)
+ , objectContainer(objectContainer)
+{
+
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
+{
+ for (int i = 0; i < objectContainer->objectCount(); ++i) {
+ const CompiledObject &component = *objectContainer->objectAt(i);
+ if (!(component.flags & QV4::CompiledData::Object::IsComponent))
+ continue;
+
+ const auto rootBinding = component.bindingsBegin();
+ appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
+ }
+
+ const int rootObjectIndex = objectContainer->rootObjectIndex();
+ appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex)
+{
+ QVector<int> objectsWithAliases;
+ collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
+ if (objectsWithAliases.isEmpty())
+ return;
+
+ const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
+ for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+
+ const int targetObjectIndex = objectForId(component, alias->targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+
+ if (alias->aliasToLocalAlias)
+ continue;
+
+ if (alias->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ if (!targetProperty)
+ return false;
+ }
+ return true;
+ };
+
+ do {
+ QVector<int> pendingObjects;
+
+ for (int objectIndex: qAsConst(objectsWithAliases)) {
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+
+ if (allAliasTargetsExist(object)) {
+ appendAliasesToPropertyCache(component, objectIndex);
+ } else {
+ pendingObjects.append(objectIndex);
+ }
+
+ }
+ qSwap(objectsWithAliases, pendingObjects);
+ } while (!objectsWithAliases.isEmpty());
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (object.aliasCount() > 0)
+ objectsWithAliases->append(objectIndex);
+
+ // Stop at Component boundary
+ if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != objectContainer->rootObjectIndex())
+ return;
+
+ for (auto binding = object.bindingsBegin(), end = object.bindingsEnd(); binding != end; ++binding) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Object
+ && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
+ && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ continue;
+
+ collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ }
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type,
+ QQmlPropertyData::Flags *propertyFlags)
+{
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+
+ const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
+
+ *type = 0;
+ bool writable = false;
+ bool resettable = false;
+
+ propertyFlags->isAlias = true;
+
+ if (alias.aliasToLocalAlias) {
+ auto targetAlias = targetObject.aliasesBegin();
+ for (uint i = 0; i < alias.localAliasIndex; ++i)
+ ++targetAlias;
+ propertyDataForAlias(component, *targetAlias, type, propertyFlags);
+ return;
+ } else if (alias.encodedMetaPropertyIndex == -1) {
+ Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
+ auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->type)
+ *type = typeRef->type->typeId();
+ else
+ *type = typeRef->compilationUnit->metaTypeId;
+
+ propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
+ } else {
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
+ int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
+
+ QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ Q_ASSERT(targetProperty);
+
+ *type = targetProperty->propType();
+
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
+ if (valueTypeIndex != -1) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
+ if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
+ *type = QVariant::Int;
+ else
+ *type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ } else {
+ if (targetProperty->isEnum()) {
+ *type = QVariant::Int;
+ } else {
+ // Copy type flags
+ propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
+
+ if (targetProperty->isVarProperty())
+ propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ }
+ }
+ }
+
+ propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable;
+ propertyFlags->isResettable = resettable;
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex)
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (!object.aliasCount())
+ return;
+
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
+ Q_ASSERT(propertyCache);
+
+ int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
+ int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
+
+ int aliasIndex = 0;
+ for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias, ++aliasIndex) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+
+ int type = 0;
+ QQmlPropertyData::Flags propertyFlags;
+ propertyDataForAlias(component, *alias, &type, &propertyFlags);
+
+ const QString propertyName = objectContainer->stringAt(alias->nameIndex);
+
+ if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ propertyCache->_defaultPropertyName = propertyName;
+
+ propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ type, effectiveSignalIndex++);
+ }
+}
+
+template <typename ObjectContainer>
+inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
+{
+ for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
+ if (candidate.id == id)
+ return candidateIndex;
+ }
+ return -1;
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYCACHECREATOR_P_H
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
new file mode 100644
index 0000000000..45379d5155
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -0,0 +1,703 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlpropertyvalidator_p.h"
+
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlstringconverters_p.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit)
+ : enginePrivate(enginePrivate)
+ , imports(imports)
+ , qmlUnit(compilationUnit->data)
+ , resolvedTypes(compilationUnit->resolvedTypes)
+ , propertyCaches(compilationUnit->propertyCaches)
+ , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject)
+{
+ bindingPropertyDataPerObject->resize(qmlUnit->nObjects);
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::validate()
+{
+ return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0);
+}
+
+typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
+
+struct BindingFinder
+{
+ bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const
+ {
+ return name < binding->propertyNameIndex;
+ }
+ bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const
+ {
+ return binding->propertyNameIndex < name;
+ }
+ bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const
+ {
+ return lhs->propertyNameIndex < rhs->propertyNameIndex;
+ }
+};
+
+QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
+{
+ const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
+
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->nBindings == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
+ Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ return validateObject(componentBinding->value.objectIndex, componentBinding);
+ }
+
+ QQmlPropertyCache *propertyCache = propertyCaches.at(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();
+ }
+
+ QList<const QV4::CompiledData::Binding*> customBindings;
+
+ // Collect group properties first for sanity checking
+ // vector values are sorted by property name string index.
+ GroupPropertyVector groupProperties;
+ const QV4::CompiledData::Binding *binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ if (!binding->isGroupProperty())
+ continue;
+
+ if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ continue;
+
+ if (populatingValueTypeGroupProperty) {
+ return recordError(binding->location, tr("Property assignment expected"));
+ }
+
+ GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
+ groupProperties.insert(pos, binding);
+ }
+
+ QmlIR::PropertyResolver propertyResolver(propertyCache);
+
+ QString defaultPropertyName;
+ QQmlPropertyData *defaultProperty = 0;
+ if (obj->indexOfDefaultPropertyOrAlias != -1) {
+ QQmlPropertyCache *cache = propertyCache->parent();
+ defaultPropertyName = cache->defaultPropertyName();
+ defaultProperty = cache->defaultProperty();
+ } else {
+ defaultPropertyName = propertyCache->defaultPropertyName();
+ defaultProperty = propertyCache->defaultProperty();
+ }
+
+ QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+
+ binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ QString name = stringAt(binding->propertyNameIndex);
+
+ if (customParser) {
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
+ customBindings << binding;
+ continue;
+ }
+ } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
+ customBindings << binding;
+ continue;
+ }
+ }
+
+ bool bindingToDefaultProperty = false;
+ bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
+
+ bool notInRevision = false;
+ QQmlPropertyData *pd = 0;
+ if (!name.isEmpty()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ pd = propertyResolver.signal(name, &notInRevision);
+ else
+ pd = propertyResolver.property(name, &notInRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : 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));
+ } else {
+ return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
+ }
+ }
+ } else {
+ if (isGroupProperty)
+ return recordError(binding->location, tr("Cannot assign a value directly to a grouped property"));
+
+ pd = defaultProperty;
+ name = defaultPropertyName;
+ bindingToDefaultProperty = true;
+ }
+
+ if (pd)
+ collectedBindingPropertyData[i] = pd;
+
+ if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
+ QQmlType *type = 0;
+ QQmlImportNamespace *typeNamespace = 0;
+ imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &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()));
+ if (!subObjectValidatorErrors.isEmpty())
+ return subObjectValidatorErrors;
+ }
+
+ // Signal handlers were resolved and checked earlier in the signal handler conversion pass.
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ continue;
+
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
+ return recordError(binding->location, tr("Attached properties cannot be used here"));
+ }
+ continue;
+ }
+
+ if (pd) {
+ GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
+ const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex);
+
+ if (!pd->isWritable()
+ && !pd->isQList()
+ && !binding->isGroupProperty()
+ && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
+ ) {
+
+ if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
+ return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
+ return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
+ }
+
+ if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
+ QString error;
+ if (pd->propType() == qMetaTypeId<QQmlScriptString>())
+ error = tr( "Cannot assign multiple values to a script property");
+ else
+ error = tr( "Cannot assign multiple values to a singular property");
+ return recordError(binding->valueLocation, error);
+ }
+
+ if (!bindingToDefaultProperty
+ && !binding->isGroupProperty()
+ && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ && assigningToGroupProperty) {
+ QV4::CompiledData::Location loc = binding->valueLocation;
+ if (loc < (*assignedGroupProperty)->valueLocation)
+ loc = (*assignedGroupProperty)->valueLocation;
+
+ if (pd && QQmlValueTypeFactory::isValueType(pd->propType()))
+ return recordError(loc, tr("Property has already been assigned a value"));
+ return recordError(loc, tr("Cannot assign a value directly to a grouped property"));
+ }
+
+ if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding);
+ if (bindingError.isSet())
+ return recordError(bindingError);
+ } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ QQmlCompileError bindingError = validateObjectBinding(pd, name, binding);
+ if (bindingError.isSet())
+ return recordError(bindingError);
+ } else if (binding->isGroupProperty()) {
+ if (QQmlValueTypeFactory::isValueType(pd->propType())) {
+ if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) {
+ if (!pd->isWritable()) {
+ return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
+ }
+ } else {
+ return recordError(binding->location, tr("Invalid grouped property access"));
+ }
+ } else {
+ if (!enginePrivate->propertyCacheForType(pd->propType())) {
+ return recordError(binding->location, tr("Invalid grouped property access"));
+ }
+ }
+ }
+ } else {
+ if (customParser) {
+ customBindings << binding;
+ continue;
+ }
+ if (bindingToDefaultProperty) {
+ return recordError(binding->location, tr("Cannot assign to non-existent default property"));
+ } else {
+ return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name));
+ }
+ }
+ }
+
+ if (obj->idNameIndex) {
+ bool notInRevision = false;
+ collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
+ }
+
+ if (customParser && !customBindings.isEmpty()) {
+ customParser->clearErrors();
+ customParser->validator = this;
+ customParser->engine = enginePrivate;
+ customParser->imports = &imports;
+ customParser->verifyBindings(qmlUnit, customBindings);
+ customParser->validator = 0;
+ customParser->engine = 0;
+ customParser->imports = (QQmlImports*)0;
+ QVector<QQmlCompileError> parserErrors = customParser->errors();
+ if (!parserErrors.isEmpty())
+ return parserErrors;
+ }
+
+ (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData;
+
+ QVector<QQmlCompileError> noError;
+ return noError;
+}
+
+QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
+{
+ if (property->isQList()) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists"));
+ }
+
+ QQmlCompileError noError;
+
+ if (property->isEnum()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
+ return noError;
+
+ QString value = binding->valueAsString(qmlUnit);
+ QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
+ bool ok;
+ if (p.isFlagType()) {
+ p.enumerator().keysToValue(value.toUtf8().constData(), &ok);
+ } else
+ p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
+
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
+ }
+ return noError;
+ }
+
+ switch (property->propType()) {
+ case QMetaType::QVariant:
+ break;
+ case QVariant::String: {
+ if (!binding->evaluatesToString()) {
+ return QQmlCompileError(binding->valueLocation, 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"));
+ }
+ }
+ break;
+ case QVariant::ByteArray: {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return QQmlCompileError(binding->valueLocation, 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"));
+ }
+ }
+ break;
+ case QVariant::UInt: {
+ if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ double d = binding->valueAsNumber();
+ if (double(uint(d)) == d)
+ return noError;
+ }
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected"));
+ }
+ break;
+ case QVariant::Int: {
+ if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ double d = binding->valueAsNumber();
+ if (double(int(d)) == d)
+ return noError;
+ }
+ return QQmlCompileError(binding->valueLocation, 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"));
+ }
+ }
+ break;
+ case QVariant::Double: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected"));
+ }
+ }
+ break;
+ case QVariant::Color: {
+ bool ok = false;
+ QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected"));
+ }
+ }
+ break;
+#ifndef QT_NO_DATESTRING
+ case QVariant::Date: {
+ bool ok = false;
+ QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected"));
+ }
+ }
+ break;
+ case QVariant::Time: {
+ bool ok = false;
+ QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected"));
+ }
+ }
+ break;
+ case QVariant::DateTime: {
+ bool ok = false;
+ QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected"));
+ }
+ }
+ break;
+#endif // QT_NO_DATESTRING
+ case QVariant::Point: {
+ bool ok = false;
+ QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::PointF: {
+ bool ok = false;
+ QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::Size: {
+ bool ok = false;
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
+ }
+ }
+ break;
+ case QVariant::SizeF: {
+ bool ok = false;
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
+ }
+ }
+ break;
+ case QVariant::Rect: {
+ bool ok = false;
+ QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected"));
+ }
+ }
+ break;
+ case QVariant::RectF: {
+ bool ok = false;
+ QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, 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"));
+ }
+ }
+ break;
+ case QVariant::Vector2D: {
+ struct {
+ 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"));
+ }
+ }
+ break;
+ case QVariant::Vector3D: {
+ struct {
+ float xp;
+ 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"));
+ }
+ }
+ break;
+ case QVariant::Vector4D: {
+ struct {
+ float xp;
+ float yp;
+ 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"));
+ }
+ }
+ break;
+ case QVariant::Quaternion: {
+ struct {
+ float wp;
+ float xp;
+ 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"));
+ }
+ }
+ break;
+ case QVariant::RegExp:
+ return QQmlCompileError(binding->valueLocation, 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"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<int> >()) {
+ bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
+ if (ok) {
+ double n = binding->valueAsNumber();
+ if (double(int(n)) != n)
+ ok = false;
+ }
+ if (!ok)
+ return QQmlCompileError(binding->valueLocation, 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"));
+ }
+ 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"));
+ }
+ 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"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QJSValue>()) {
+ break;
+ } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
+ 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()))));
+ }
+ }
+ break;
+ }
+ return noError;
+}
+
+/*!
+ Returns true if from can be assigned to a (QObject) property of type
+ to.
+*/
+bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
+{
+ QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
+
+ while (fromMo) {
+ if (fromMo == toMo)
+ return true;
+ fromMo = fromMo->parent();
+ }
+ return false;
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
+{
+ QVector<QQmlCompileError> errors;
+ errors.append(QQmlCompileError(location, description));
+ return errors;
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const
+{
+ QVector<QQmlCompileError> errors;
+ errors.append(error);
+ return errors;
+}
+
+QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
+{
+ QQmlCompileError noError;
+
+ if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
+ Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
+
+ 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 QMetaObject *mo = cache->firstCppMetaObject();
+ while (mo && !qmlType) {
+ qmlType = QQmlMetaType::qmlType(mo);
+ mo = mo->superClass();
+ }
+ Q_ASSERT(qmlType);
+ }
+
+ if (qmlType) {
+ isValueSource = qmlType->propertyValueSourceCast() != -1;
+ isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1;
+ }
+
+ if (!isValueSource && !isPropertyInterceptor) {
+ return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
+ }
+
+ return noError;
+ }
+
+ if (QQmlMetaType::isInterface(property->propType())) {
+ // 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) {
+ // We can convert everything to QVariant :)
+ return noError;
+ } else if (property->isQList()) {
+ const int listType = enginePrivate->listType(property->propType());
+ if (!QQmlMetaType::isInterface(listType)) {
+ QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex);
+ if (!canCoerce(listType, source)) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
+ }
+ }
+ 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"));
+ } 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
+ // 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());
+
+ // Will be true if the assgned type inherits propertyMetaObject
+ bool isAssignable = false;
+ // Determine isAssignable value
+ if (propertyMetaObject) {
+ QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex);
+ while (c && !isAssignable) {
+ isAssignable |= c == propertyMetaObject;
+ c = c->parent();
+ }
+ }
+
+ if (!isAssignable) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property"));
+ }
+ }
+ return noError;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
new file mode 100644
index 0000000000..d0bd314461
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QQMLPROPERTYVALIDATOR_P_H
+#define QQMLPROPERTYVALIDATOR_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/qqmltypecompiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyValidator
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
+public:
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit);
+
+ QVector<QQmlCompileError> validate();
+
+private:
+ QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const;
+ QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const;
+ QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const;
+
+ bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
+
+ QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT;
+ QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT;
+ QString stringAt(int index) const { return qmlUnit->stringAt(index); }
+
+ QQmlEnginePrivate *enginePrivate;
+ const QQmlImports &imports;
+ const QV4::CompiledData::Unit *qmlUnit;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
+ const QQmlPropertyCacheVector &propertyCaches;
+
+ QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYVALIDATOR_P_H
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index caa4a55d3d..2308e66609 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,9 +44,10 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qqmlstringconverters_p.h>
#include <private/qv4ssa_p.h>
+#include "qqmlpropertycachecreator_p.h"
+
#define COMPILE_EXCEPTION(token, desc) \
{ \
recordError((token)->location, desc); \
@@ -55,95 +56,35 @@
QT_BEGIN_NAMESPACE
-QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *parsedQML)
- : engine(engine)
- , compiledData(compiledData)
+QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
+ QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &importCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
+ : resolvedTypes(resolvedTypeCache)
+ , engine(engine)
, typeData(typeData)
+ , importCache(importCache)
, document(parsedQML)
{
}
-bool QQmlTypeCompiler::compile()
+QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
{
- compiledData->importCache = new QQmlTypeNameCache;
-
- foreach (const QString &ns, typeData->namespaces())
- compiledData->importCache->add(ns);
-
- // Add any Composite Singletons that were used to the import cache
- foreach (const QQmlTypeData::TypeReference &singleton, typeData->compositeSingletons())
- compiledData->importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix);
-
- typeData->imports().populateCache(compiledData->importCache);
-
- const QHash<int, QQmlTypeData::TypeReference> &resolvedTypes = typeData->resolvedTypeRefs();
- for (QHash<int, QQmlTypeData::TypeReference>::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd();
- resolvedType != end; ++resolvedType) {
- QScopedPointer<QQmlCompiledData::TypeReference> ref(new QQmlCompiledData::TypeReference);
- QQmlType *qmlType = resolvedType->type;
- if (resolvedType->typeData) {
- if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) {
- QQmlError error;
- QString reason = tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName());
- error.setDescription(reason);
- error.setColumn(resolvedType->location.column);
- error.setLine(resolvedType->location.line);
- recordError(error);
- return false;
- }
- ref->component = resolvedType->typeData->compiledData();
- ref->component->addref();
- } else if (qmlType) {
- ref->type = qmlType;
- Q_ASSERT(ref->type);
-
- if (resolvedType->needsCreation && !ref->type->isCreatable()) {
- QQmlError error;
- QString reason = ref->type->noCreationReason();
- if (reason.isEmpty())
- reason = tr("Element is not creatable.");
- error.setDescription(reason);
- error.setColumn(resolvedType->location.column);
- error.setLine(resolvedType->location.line);
- recordError(error);
- return false;
- }
-
- if (ref->type->containsRevisionedAttributes()) {
- ref->typePropertyCache = engine->cache(ref->type,
- resolvedType->minorVersion);
- if (!ref->typePropertyCache) {
- QQmlError cacheError;
- cacheError.setColumn(resolvedType->location.column);
- cacheError.setLine(resolvedType->location.line);
- recordError(cacheError);
- return false;
- }
- ref->typePropertyCache->addref();
- }
- }
- ref->majorVersion = resolvedType->majorVersion;
- ref->minorVersion = resolvedType->minorVersion;
- ref->doDynamicTypeCheck();
- compiledData->resolvedTypes.insert(resolvedType.key(), ref.take());
- }
-
// Build property caches and VME meta object data
- for (QHash<int, QQmlCompiledData::TypeReference*>::ConstIterator it = compiledData->resolvedTypes.constBegin(), end = compiledData->resolvedTypes.constEnd();
+ for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd();
it != end; ++it) {
QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0;
if (customParser)
customParsers.insert(it.key(), customParser);
}
- compiledData->metaObjects.reserve(document->objects.count());
- compiledData->propertyCaches.reserve(document->objects.count());
-
{
- QQmlPropertyCacheCreator propertyCacheBuilder(this);
- if (!propertyCacheBuilder.buildMetaObjects())
- return false;
+ QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, engine, this, imports());
+ QQmlCompileError error = propertyCacheBuilder.buildMetaObjects();
+ if (error.isSet()) {
+ recordError(error);
+ return nullptr;
+ }
}
{
@@ -154,13 +95,13 @@ bool QQmlTypeCompiler::compile()
{
SignalHandlerConverter converter(this);
if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations())
- return false;
+ return nullptr;
}
{
QQmlEnumTypeResolver enumResolver(this);
if (!enumResolver.resolveEnumBindings())
- return false;
+ return nullptr;
}
{
@@ -173,34 +114,19 @@ bool QQmlTypeCompiler::compile()
annotator.annotateBindingsToAliases();
}
- // Collect imported scripts
- const QList<QQmlTypeData::ScriptReference> &scripts = typeData->resolvedScripts();
- compiledData->scripts.reserve(scripts.count());
- for (int scriptIndex = 0; scriptIndex < scripts.count(); ++scriptIndex) {
- const QQmlTypeData::ScriptReference &script = scripts.at(scriptIndex);
-
- QStringRef qualifier(&script.qualifier);
- QString enclosingNamespace;
-
- const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
- if (lastDotIndex != -1) {
- enclosingNamespace = qualifier.left(lastDotIndex).toString();
- qualifier = qualifier.mid(lastDotIndex+1);
- }
-
- compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
- QQmlScriptData *scriptData = script.script->scriptData();
- scriptData->addref();
- compiledData->scripts << scriptData;
- }
-
// Resolve component boundaries and aliases
{
// Scan for components, determine their scopes and resolve aliases within the scope.
QQmlComponentAndAliasResolver resolver(this);
if (!resolver.resolve())
- return false;
+ return nullptr;
+ }
+
+ {
+ QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this);
+ if (!deferredAndCustomParserBindingScanner.scanObject())
+ return nullptr;
}
// Compile JS binding expressions and signal handlers
@@ -212,10 +138,10 @@ bool QQmlTypeCompiler::compile()
sss.scan();
}
- QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, compiledData->importCache, &document->jsGenerator.stringTable);
+ QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
- return false;
+ return nullptr;
QQmlJavaScriptBindingExpressionSimplificationPass pass(this);
pass.reduceTranslationBindings();
@@ -230,70 +156,45 @@ bool QQmlTypeCompiler::compile()
// Generate QML compiled type data structures
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document);
+ QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, QQmlEnginePrivate::get(engine), resolvedTypes);
Q_ASSERT(document->javaScriptCompilationUnit);
// The js unit owns the data and will free the qml unit.
document->javaScriptCompilationUnit->data = qmlUnit;
- compiledData->compilationUnit = document->javaScriptCompilationUnit;
-
- // Add to type registry of composites
- if (compiledData->isCompositeType())
- engine->registerInternalCompositeType(compiledData);
- else {
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject);
- QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
- if (typeRef->component) {
- compiledData->metaTypeId = typeRef->component->metaTypeId;
- compiledData->listMetaTypeId = typeRef->component->listMetaTypeId;
- } else {
- compiledData->metaTypeId = typeRef->type->typeId();
- compiledData->listMetaTypeId = typeRef->type->qListTypeId();
- }
- }
-
- // Sanity check property bindings
- QQmlPropertyValidator validator(this);
- if (!validator.validate())
- return false;
+ QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit;
+ compilationUnit = document->javaScriptCompilationUnit;
+ compilationUnit->importCache = importCache;
+ compilationUnit->resolvedTypes = resolvedTypes;
+ compilationUnit->propertyCaches = std::move(m_propertyCaches);
+ Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects));
- // Collect some data for instantiation later.
- int bindingCount = 0;
- int parserStatusCount = 0;
- int objectCount = 0;
- for (quint32 i = 0; i < qmlUnit->nObjects; ++i) {
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i);
- bindingCount += obj->nBindings;
- if (QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex)) {
- if (QQmlType *qmlType = typeRef->type) {
- if (qmlType->parserStatusCast() != -1)
- ++parserStatusCount;
- }
- ++objectCount;
- if (typeRef->component) {
- bindingCount += typeRef->component->totalBindingsCount;
- parserStatusCount += typeRef->component->totalParserStatusCount;
- objectCount += typeRef->component->totalObjectCount;
- }
- }
- }
- compiledData->totalBindingsCount = bindingCount;
- compiledData->totalParserStatusCount = parserStatusCount;
- compiledData->totalObjectCount = objectCount;
+ if (errors.isEmpty())
+ return compilationUnit;
+ else
+ return nullptr;
+}
- Q_ASSERT(compiledData->propertyCaches.count() == static_cast<int>(compiledData->compilationUnit->data->nObjects));
+void QQmlTypeCompiler::recordError(QQmlError error)
+{
+ error.setUrl(url());
+ errors << error;
+}
- return errors.isEmpty();
+void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
+{
+ QQmlError error;
+ error.setLine(location.line);
+ error.setColumn(location.column);
+ error.setDescription(description);
+ error.setUrl(url());
+ errors << error;
}
-void QQmlTypeCompiler::recordError(const QQmlError &error)
+void QQmlTypeCompiler::recordError(const QQmlCompileError &error)
{
- QQmlError e = error;
- e.setUrl(url());
- errors << e;
+ recordError(error.location, error.description);
}
QString QQmlTypeCompiler::stringAt(int idx) const
@@ -313,7 +214,7 @@ QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return compiledData->compilationUnit->data;
+ return document->javaScriptCompilationUnit->data;
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -321,12 +222,7 @@ const QQmlImports *QQmlTypeCompiler::imports() const
return &typeData->imports();
}
-QHash<int, QQmlCompiledData::TypeReference*> *QQmlTypeCompiler::resolvedTypes()
-{
- return &compiledData->resolvedTypes;
-}
-
-QList<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects()
+QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
{
return &document->objects;
}
@@ -336,45 +232,20 @@ int QQmlTypeCompiler::rootObjectIndex() const
return document->indexOfRootObject;
}
-void QQmlTypeCompiler::setPropertyCaches(const QVector<QQmlPropertyCache *> &caches)
+void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
{
- compiledData->propertyCaches = caches;
- Q_ASSERT(caches.count() >= document->indexOfRootObject);
- if (compiledData->rootPropertyCache)
- compiledData->rootPropertyCache->release();
- compiledData->rootPropertyCache = caches.at(document->indexOfRootObject);
- compiledData->rootPropertyCache->addref();
+ m_propertyCaches = std::move(caches);
+ Q_ASSERT(m_propertyCaches.count() >= document->indexOfRootObject);
}
-const QVector<QQmlPropertyCache *> &QQmlTypeCompiler::propertyCaches() const
+const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
{
- return compiledData->propertyCaches;
+ return &m_propertyCaches;
}
-void QQmlTypeCompiler::setVMEMetaObjects(const QVector<QByteArray> &metaObjects)
+QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
{
- Q_ASSERT(compiledData->metaObjects.isEmpty());
- compiledData->metaObjects = metaObjects;
-}
-
-QVector<QByteArray> *QQmlTypeCompiler::vmeMetaObjects() const
-{
- return &compiledData->metaObjects;
-}
-
-QHash<int, int> *QQmlTypeCompiler::objectIndexToIdForRoot()
-{
- return &compiledData->objectIndexToIdForRoot;
-}
-
-QHash<int, QHash<int, int> > *QQmlTypeCompiler::objectIndexToIdPerComponent()
-{
- return &compiledData->objectIndexToIdPerComponent;
-}
-
-QHash<int, QBitArray> *QQmlTypeCompiler::customParserBindings()
-{
- return &compiledData->customParserBindings;
+ return std::move(m_propertyCaches);
}
QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
@@ -392,14 +263,9 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const
return &document->jsGenerator.stringTable;
}
-void QQmlTypeCompiler::setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject)
-{
- compiledData->deferredBindingsPerObject = deferredBindingsPerObject;
-}
-
void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData)
{
- compiledData->compilationUnit->bindingPropertyDataPerObject = propertyData;
+ document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData;
}
QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
@@ -407,499 +273,34 @@ QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scrip
return object->bindingAsString(document, scriptIndex);
}
-QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
- : compiler(typeCompiler)
-{
-}
-
-void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) const
-{
- QQmlError error;
- error.setLine(location.line);
- error.setColumn(location.column);
- error.setDescription(description);
- compiler->recordError(error);
-}
-
-static QAtomicInt classIndexCounter(0);
-
-QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler)
- : QQmlCompilePass(typeCompiler)
- , enginePrivate(typeCompiler->enginePrivate())
- , qmlObjects(*typeCompiler->qmlObjects())
- , imports(typeCompiler->imports())
- , resolvedTypes(typeCompiler->resolvedTypes())
-{
-}
-
-QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator()
-{
- for (int i = 0; i < propertyCaches.count(); ++i)
- if (QQmlPropertyCache *cache = propertyCaches.at(i))
- cache->release();
- propertyCaches.clear();
-}
-
-bool QQmlPropertyCacheCreator::buildMetaObjects()
-{
- propertyCaches.resize(qmlObjects.count());
- vmeMetaObjects.resize(qmlObjects.count());
-
- if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0))
- return false;
-
- compiler->setVMEMetaObjects(vmeMetaObjects);
- compiler->setPropertyCaches(propertyCaches);
- propertyCaches.clear();
-
- return true;
-}
-
-bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding)
+void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion)
{
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
-
- QQmlPropertyCache *baseTypeCache = 0;
- QQmlPropertyData *instantiatingProperty = 0;
- if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
- Q_ASSERT(referencingObjectIndex >= 0);
- QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex);
- Q_ASSERT(parentCache);
- Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
-
- bool notInRevision = false;
- instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), &notInRevision);
- if (instantiatingProperty) {
- if (instantiatingProperty->isQObject()) {
- baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType);
- Q_ASSERT(baseTypeCache);
- } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) {
- baseTypeCache = enginePrivate->cache(vtmo);
- Q_ASSERT(baseTypeCache);
- }
- }
- }
-
- bool needVMEMetaObject = obj->propertyCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0;
- if (!needVMEMetaObject) {
- for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
-
- // On assignments are implemented using value interceptors, which require a VME meta object.
- needVMEMetaObject = true;
-
- // If the on assignment is inside a group property, we need to distinguish between QObject based
- // group properties and value type group properties. For the former the base type is derived from
- // the property that references us, for the latter we only need a meta-object on the referencing object
- // because interceptors can't go to the shared value type instances.
- if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) {
- needVMEMetaObject = false;
- if (!ensureMetaObject(referencingObjectIndex))
- return false;
- }
- break;
- }
- }
- }
-
- if (obj->inheritedTypeNameIndex != 0) {
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
-
- if (typeRef->isFullyDynamicType) {
- if (obj->propertyCount() > 0) {
- recordError(obj->location, tr("Fully dynamic types cannot declare new properties."));
- return false;
- }
- if (obj->signalCount() > 0) {
- recordError(obj->location, tr("Fully dynamic types cannot declare new signals."));
- return false;
- }
- if (obj->functionCount() > 0) {
- recordError(obj->location, tr("Fully Dynamic types cannot declare new functions."));
- return false;
- }
- }
-
- baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- Q_ASSERT(baseTypeCache);
- } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) {
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex);
- Q_ASSERT(typeRef);
- QQmlType *qmltype = typeRef->type;
- if (!qmltype) {
- QString propertyName = stringAt(instantiatingBinding->propertyNameIndex);
- if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) {
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- QQmlCompiledData *data = tdata->compiledData();
- qmltype = QQmlMetaType::qmlType(data->metaTypeId);
-
- tdata->release();
- }
- }
- }
+ const quint32 moduleIdx = registerString(module);
+ const quint32 qualifierIdx = registerString(qualifier);
- const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0;
- if (!attachedMo) {
- recordError(instantiatingBinding->location, tr("Non-existent attached object"));
- return false;
- }
- baseTypeCache = enginePrivate->cache(attachedMo);
- Q_ASSERT(baseTypeCache);
- }
-
- if (baseTypeCache) {
- if (needVMEMetaObject) {
- if (!createMetaObject(objectIndex, obj, baseTypeCache))
- return false;
- } else {
- propertyCaches[objectIndex] = baseTypeCache;
- baseTypeCache->addref();
- }
- }
-
- if (propertyCaches.at(objectIndex)) {
- for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next)
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
- if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding))
- return false;
- }
+ for (int i = 0, count = document->imports.count(); i < count; ++i) {
+ const QV4::CompiledData::Import *existingImport = document->imports.at(i);
+ if (existingImport->type == QV4::CompiledData::Import::ImportLibrary
+ && existingImport->uriIndex == moduleIdx
+ && existingImport->qualifierIndex == qualifierIdx)
+ return;
}
-
- return true;
+ auto pool = memoryPool();
+ QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
+ import->type = QV4::CompiledData::Import::ImportLibrary;
+ import->majorVersion = majorVersion;
+ import->minorVersion = minorVersion;
+ import->uriIndex = moduleIdx;
+ import->qualifierIndex = qualifierIdx;
+ document->imports.append(import);
}
-bool QQmlPropertyCacheCreator::ensureMetaObject(int objectIndex)
+QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
+ : compiler(typeCompiler)
{
- if (!vmeMetaObjects.at(objectIndex).isEmpty())
- return true;
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
- QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- return createMetaObject(objectIndex, obj, baseTypeCache);
-}
-
-bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache)
-{
- QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount(),
- obj->functionCount() + obj->propertyCount() + obj->signalCount(),
- obj->signalCount() + obj->propertyCount());
- propertyCaches[objectIndex] = cache;
-
- struct TypeData {
- QV4::CompiledData::Property::Type dtype;
- int metaType;
- } builtinTypes[] = {
- { QV4::CompiledData::Property::Var, QMetaType::QVariant },
- { QV4::CompiledData::Property::Variant, QMetaType::QVariant },
- { QV4::CompiledData::Property::Int, QMetaType::Int },
- { QV4::CompiledData::Property::Bool, QMetaType::Bool },
- { QV4::CompiledData::Property::Real, QMetaType::Double },
- { QV4::CompiledData::Property::String, QMetaType::QString },
- { QV4::CompiledData::Property::Url, QMetaType::QUrl },
- { QV4::CompiledData::Property::Color, QMetaType::QColor },
- { QV4::CompiledData::Property::Font, QMetaType::QFont },
- { QV4::CompiledData::Property::Time, QMetaType::QTime },
- { QV4::CompiledData::Property::Date, QMetaType::QDate },
- { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
- { QV4::CompiledData::Property::Rect, QMetaType::QRectF },
- { QV4::CompiledData::Property::Point, QMetaType::QPointF },
- { QV4::CompiledData::Property::Size, QMetaType::QSizeF },
- { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
- { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
- { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
- { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
- { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
- };
- static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
-
- QByteArray newClassName;
-
- if (objectIndex == compiler->rootObjectIndex()) {
- QString path = compiler->url().path();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- if (lastSlash > -1) {
- const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
- if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
- newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
- QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
- }
- }
- if (newClassName.isEmpty()) {
- newClassName = QQmlMetaObject(baseTypeCache).className();
- newClassName.append("_QML_");
- newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
- }
-
- cache->_dynamicClassName = newClassName;
-
- int aliasCount = 0;
- int varPropCount = 0;
-
- QmlIR::PropertyResolver resolver(baseTypeCache);
-
- for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) {
- if (p->type == QV4::CompiledData::Property::Alias)
- aliasCount++;
- else if (p->type == QV4::CompiledData::Property::Var)
- varPropCount++;
-
- // No point doing this for both the alias and non alias cases
- bool notInRevision = false;
- QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
- if (d && d->isFinal())
- COMPILE_EXCEPTION(p, tr("Cannot override FINAL property"));
- }
-
- typedef QQmlVMEMetaData VMD;
-
- QByteArray &dynamicData = vmeMetaObjects[objectIndex] = QByteArray(sizeof(QQmlVMEMetaData)
- + obj->propertyCount() * sizeof(VMD::PropertyData)
- + obj->functionCount() * sizeof(VMD::MethodData)
- + aliasCount * sizeof(VMD::AliasData), 0);
-
- int effectivePropertyIndex = cache->propertyIndexCacheStart;
- int effectiveMethodIndex = cache->methodIndexCacheStart;
-
- // For property change signal override detection.
- // We prepopulate a set of signal names which already exist in the object,
- // 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;
- while ((parentCache = parentCache->parent())) {
- if (int pSigCount = parentCache->signalCount()) {
- int pSigOffset = parentCache->signalOffset();
- for (int i = pSigOffset; i < pSigCount; ++i) {
- QQmlPropertyData *currPSig = parentCache->signal(i);
- // XXX TODO: find a better way to get signal name from the property data :-/
- for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
- iter != parentCache->stringCache.end(); ++iter) {
- if (currPSig == (*iter).second) {
- seenSignals.insert(iter.key());
- break;
- }
- }
- }
- }
- }
-
- // First set up notify signals for properties - first normal, then var, then alias
- enum { NSS_Normal = 0, NSS_Alias = 1 };
- for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
-
- if (ii == NSS_Alias && aliasCount == 0) continue;
-
- for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) {
- if ((ii == NSS_Normal && p->type == QV4::CompiledData::Property::Alias) ||
- (ii == NSS_Alias && p->type != QV4::CompiledData::Property::Alias))
- continue;
-
- quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
- QQmlPropertyData::IsVMESignal;
-
- QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
- seenSignals.insert(changedSigName);
-
- cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
- }
- }
-
- // Dynamic signals
- for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) {
- const int paramCount = s->parameters->count;
-
- QList<QByteArray> names;
- names.reserve(paramCount);
- QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
-
- if (paramCount) {
- paramTypes[0] = paramCount;
-
- QmlIR::SignalParameter *param = s->parameters->first;
- for (int i = 0; i < paramCount; ++i, param = param->next) {
- names.append(stringAt(param->nameIndex).toUtf8());
- if (param->type < builtinTypeCount) {
- // built-in type
- paramTypes[i + 1] = builtinTypes[param->type].metaType;
- } else {
- // 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))
- COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName));
-
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- QQmlCompiledData *data = tdata->compiledData();
-
- paramTypes[i + 1] = data->metaTypeId;
-
- tdata->release();
- } else {
- paramTypes[i + 1] = qmltype->typeId();
- }
- }
- }
- }
-
- ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
-
- quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
- QQmlPropertyData::IsVMESignal;
- if (paramCount)
- flags |= QQmlPropertyData::HasArguments;
-
- QString signalName = stringAt(s->nameIndex);
- if (seenSignals.contains(signalName))
- COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
- seenSignals.insert(signalName);
-
- cache->appendSignal(signalName, flags, effectiveMethodIndex++,
- paramCount?paramTypes.constData():0, names);
- }
-
-
- // Dynamic slots
- for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) {
- QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration;
-
- quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
-
- if (astFunction->formals)
- flags |= QQmlPropertyData::HasArguments;
-
- QString slotName = astFunction->name.toString();
- if (seenSignals.contains(slotName))
- COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal"));
- // Note: we don't append slotName to the seenSignals list, since we don't
- // protect against overriding change signals or methods with properties.
-
- QList<QByteArray> parameterNames;
- QQmlJS::AST::FormalParameterList *param = astFunction->formals;
- while (param) {
- parameterNames << param->name.toUtf8();
- param = param->next;
- }
-
- cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
- }
-
-
- // Dynamic properties (except var and aliases)
- int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
- int propertyIdx = 0;
- for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) {
-
- if (p->type == QV4::CompiledData::Property::Alias)
- continue;
-
- int propertyType = 0;
- int vmePropertyType = 0;
- quint32 propertyFlags = 0;
-
- if (p->type == QV4::CompiledData::Property::Var) {
- propertyType = QMetaType::QVariant;
- vmePropertyType = QQmlVMEMetaData::VarPropertyType;
- propertyFlags = QQmlPropertyData::IsVarProperty;
- } else if (p->type < builtinTypeCount) {
- propertyType = builtinTypes[p->type].metaType;
- vmePropertyType = propertyType;
-
- if (p->type == QV4::CompiledData::Property::Variant)
- propertyFlags |= QQmlPropertyData::IsQVariant;
- } else {
- 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)) {
- COMPILE_EXCEPTION(p, tr("Invalid property type"));
- }
-
- Q_ASSERT(qmltype);
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- QQmlCompiledData *data = tdata->compiledData();
-
- if (p->type == QV4::CompiledData::Property::Custom) {
- propertyType = data->metaTypeId;
- vmePropertyType = QMetaType::QObjectStar;
- } else {
- propertyType = data->listMetaTypeId;
- vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
- }
-
- tdata->release();
- } else {
- if (p->type == QV4::CompiledData::Property::Custom) {
- propertyType = qmltype->typeId();
- vmePropertyType = QMetaType::QObjectStar;
- } else {
- propertyType = qmltype->qListTypeId();
- vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
- }
- }
-
- if (p->type == QV4::CompiledData::Property::Custom)
- propertyFlags |= QQmlPropertyData::IsQObjectDerived;
- else
- propertyFlags |= QQmlPropertyData::IsQList;
- }
-
- if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
- propertyFlags |= QQmlPropertyData::IsWritable;
-
-
- QString propertyName = stringAt(p->nameIndex);
- if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName;
- cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- propertyType, effectiveSignalIndex);
-
- effectiveSignalIndex++;
-
- VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
- (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
- vmd->propertyCount++;
- }
-
- // Alias property count. Actual data is setup in buildDynamicMetaAliases
- ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
-
- // Dynamic slot data - comes after the property data
- for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) {
- QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration;
- int formalsCount = 0;
- QQmlJS::AST::FormalParameterList *param = astFunction->formals;
- while (param) {
- formalsCount++;
- param = param->next;
- }
-
- VMD::MethodData methodData = { /* runtimeFunctionIndex*/ 0, // ###
- formalsCount,
- /* s->location.start.line */0 }; // ###
+}
- VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
- VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
- vmd->methodCount++;
- md = methodData;
- }
- return true;
-}
SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
@@ -907,7 +308,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, imports(typeCompiler->imports())
, customParsers(typeCompiler->customParserCache())
- , resolvedTypes(*typeCompiler->resolvedTypes())
+ , resolvedTypes(typeCompiler->resolvedTypes)
, illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames())
, propertyCaches(typeCompiler->propertyCaches())
{
@@ -917,7 +318,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
{
for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) {
const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
- QQmlPropertyCache *cache = propertyCaches.at(objectIndex);
+ QQmlPropertyCache *cache = propertyCaches->at(objectIndex);
if (!cache)
continue;
if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) {
@@ -941,7 +342,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
// Attached property?
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(binding->propertyNameIndex);
+ auto *typeRef = resolvedTypes.value(binding->propertyNameIndex);
QQmlType *type = typeRef ? typeRef->type : 0;
if (!type) {
if (imports->resolveType(propertyName, &type, 0, 0, 0)) {
@@ -950,8 +351,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
- QQmlCompiledData *data = tdata->compiledData();
- type = QQmlMetaType::qmlType(data->metaTypeId);
+ auto compilationUnit = tdata->compilationUnit();
+ type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
tdata->release();
}
@@ -989,7 +390,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
bool notInRevision = false;
QQmlPropertyData *signal = resolver.signal(propertyName, &notInRevision);
if (signal) {
- int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex);
+ int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
sigIndex = propertyCache->originalClone(sigIndex);
bool unnamedParameter = false;
@@ -1014,7 +415,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ 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()));
@@ -1118,14 +519,14 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
, imports(typeCompiler->imports())
- , resolvedTypes(typeCompiler->resolvedTypes())
+ , resolvedTypes(&typeCompiler->resolvedTypes)
{
}
bool QQmlEnumTypeResolver::resolveEnumBindings()
{
for (int i = 0; i < qmlObjects.count(); ++i) {
- QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
if (!propertyCache)
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
@@ -1146,7 +547,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
if (!pd)
continue;
- if (!pd->isEnum() && pd->propType != QMetaType::Int)
+ if (!pd->isEnum() && pd->propType() != QMetaType::Int)
continue;
if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding))
@@ -1169,14 +570,14 @@ 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->value.d = (double)enumValue;
+ binding->setNumberValueInternal((double)enumValue);
binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
return true;
}
bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding)
{
- bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum();
+ bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum();
if (!prop->isEnum() && !isIntProp)
return true;
@@ -1218,9 +619,9 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
int value = 0;
bool ok = false;
- QQmlCompiledData::TypeReference *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
if (type && tr && tr->type == type) {
- QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex);
+ QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
// When these two match, we can short cut the search
if (mprop.isFlagType()) {
@@ -1311,20 +712,20 @@ QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
void QQmlAliasAnnotator::annotateBindingsToAliases()
{
for (int i = 0; i < qmlObjects.count(); ++i) {
- QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
if (!propertyCache)
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
QmlIR::PropertyResolver resolver(propertyCache);
- QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (!binding->isValueBinding())
continue;
bool notInRevision = false;
- QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
+ QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
if (pd && pd->isAlias())
binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
}
@@ -1343,21 +744,21 @@ void QQmlScriptStringScanner::scan()
{
const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>();
for (int i = 0; i < qmlObjects.count(); ++i) {
- QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
if (!propertyCache)
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
QmlIR::PropertyResolver resolver(propertyCache);
- QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Script)
continue;
bool notInRevision = false;
- QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
- if (!pd || pd->propType != scriptStringMetaType)
+ QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
+ if (!pd || pd->propType() != scriptStringMetaType)
continue;
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
@@ -1376,13 +777,8 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t
, pool(typeCompiler->memoryPool())
, qmlObjects(typeCompiler->qmlObjects())
, indexOfRootObject(typeCompiler->rootObjectIndex())
- , _componentIndex(-1)
- , _objectIndexToIdInScope(0)
- , resolvedTypes(typeCompiler->resolvedTypes())
- , propertyCaches(typeCompiler->propertyCaches())
- , vmeMetaObjectData(typeCompiler->vmeMetaObjects())
- , objectIndexToIdForRoot(typeCompiler->objectIndexToIdForRoot())
- , objectIndexToIdPerComponent(typeCompiler->objectIndexToIdPerComponent())
+ , resolvedTypes(&typeCompiler->resolvedTypes)
+ , propertyCaches(std::move(typeCompiler->takePropertyCaches()))
{
}
@@ -1390,7 +786,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
{
QmlIR::PropertyResolver propertyResolver(propertyCache);
- QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Object)
@@ -1399,18 +795,18 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
continue;
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
- QQmlCompiledData::TypeReference *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
+ auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
Q_ASSERT(tr);
if (QQmlType *targetType = tr->type) {
if (targetType->metaObject() == &QQmlComponent::staticMetaObject)
continue;
- } else if (tr->component) {
- if (tr->component->rootPropertyCache->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
+ } else if (tr->compilationUnit) {
+ if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
continue;
}
QQmlPropertyData *pd = 0;
- if (binding->propertyNameIndex != 0) {
+ if (binding->propertyNameIndex != quint32(0)) {
bool notInRevision = false;
pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
} else {
@@ -1419,7 +815,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
if (!pd || !pd->isQObject())
continue;
- QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType);
+ QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType());
const QMetaObject *mo = pc ? pc->firstCppMetaObject() : 0;
while (mo) {
if (mo == &QQmlComponent::staticMetaObject)
@@ -1430,15 +826,20 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
if (!mo)
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);
+ const QString qualifier = QStringLiteral("QmlInternals");
+
+ compiler->addImport(componentType->module(), qualifier, componentType->majorVersion(), componentType->minorVersion());
QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
- syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), 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)) {
- QQmlCompiledData::TypeReference *typeRef = new QQmlCompiledData::TypeReference;
+ auto typeRef = new QV4::CompiledData::ResolvedTypeReference;
typeRef->type = componentType;
typeRef->majorVersion = componentType->majorVersion();
typeRef->minorVersion = componentType->minorVersion();
@@ -1449,7 +850,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
const int componentIndex = qmlObjects->count() - 1;
// Keep property caches symmetric
QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject);
- componentCache->addref();
propertyCaches.append(componentCache);
QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
@@ -1462,7 +862,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
binding->value.objectIndex = componentIndex;
componentRoots.append(componentIndex);
- componentBoundaries.append(syntheticBinding->value.objectIndex);
}
}
@@ -1474,7 +873,7 @@ bool QQmlComponentAndAliasResolver::resolve()
// on the left hand side is of QQmlComponent type.
const int objCountWithoutSynthesizedComponents = qmlObjects->count();
for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) {
- const QmlIR::Object *obj = qmlObjects->at(i);
+ QmlIR::Object *obj = qmlObjects->at(i);
QQmlPropertyCache *cache = propertyCaches.at(i);
if (obj->inheritedTypeNameIndex == 0 && !cache)
continue;
@@ -1482,7 +881,7 @@ bool QQmlComponentAndAliasResolver::resolve()
bool isExplicitComponent = false;
if (obj->inheritedTypeNameIndex) {
- QQmlCompiledData::TypeReference *tref = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ auto *tref = resolvedTypes->value(obj->inheritedTypeNameIndex);
Q_ASSERT(tref);
if (tref->type && tref->type->metaObject() == &QQmlComponent::staticMetaObject)
isExplicitComponent = true;
@@ -1493,11 +892,11 @@ bool QQmlComponentAndAliasResolver::resolve()
continue;
}
- componentRoots.append(i);
+ obj->flags |= QV4::CompiledData::Object::IsComponent;
if (obj->functionCount() > 0)
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
- if (obj->propertyCount() > 0)
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
if (obj->signalCount() > 0)
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
@@ -1515,65 +914,69 @@ bool QQmlComponentAndAliasResolver::resolve()
if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
- componentBoundaries.append(rootBinding->value.objectIndex);
- }
+ // We are going to collect ids/aliases and resolve them for the root object as a separate
+ // last pass.
+ if (i != indexOfRootObject)
+ componentRoots.append(i);
- std::sort(componentBoundaries.begin(), componentBoundaries.end());
+ }
for (int i = 0; i < componentRoots.count(); ++i) {
- const QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
+ QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
const QmlIR::Binding *rootBinding = component->firstBinding();
- _componentIndex = i;
_idToObjectIndex.clear();
- _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)];
-
_objectsWithAliases.clear();
if (!collectIdsAndAliases(rootBinding->value.objectIndex))
return false;
- if (!resolveAliases())
+ component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
+
+ if (!resolveAliases(componentRoots.at(i)))
return false;
}
// Collect ids and aliases for root
- _componentIndex = -1;
_idToObjectIndex.clear();
- _objectIndexToIdInScope = objectIndexToIdForRoot;
_objectsWithAliases.clear();
collectIdsAndAliases(indexOfRootObject);
- resolveAliases();
+ QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject);
+ rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
+
+ if (!resolveAliases(indexOfRootObject))
+ return false;
// Implicit component insertion may have added objects and thus we also need
// to extend the symmetric propertyCaches.
- compiler->setPropertyCaches(propertyCaches);
+ compiler->setPropertyCaches(std::move(propertyCaches));
+ compiler->setComponentRoots(componentRoots);
return true;
}
bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
{
- const QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ QmlIR::Object *obj = qmlObjects->at(objectIndex);
- if (obj->idIndex != 0) {
- if (_idToObjectIndex.contains(obj->idIndex)) {
+ if (obj->idNameIndex != 0) {
+ if (_idToObjectIndex.contains(obj->idNameIndex)) {
recordError(obj->locationOfIdProperty, tr("id is not unique"));
return false;
}
- _idToObjectIndex.insert(obj->idIndex, objectIndex);
- _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count());
+ obj->id = _idToObjectIndex.count();
+ _idToObjectIndex.insert(obj->idNameIndex, objectIndex);
}
- for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) {
- if (property->type == QV4::CompiledData::Property::Alias) {
- _objectsWithAliases.append(objectIndex);
- break;
- }
- }
+ if (obj->aliasCount() > 0)
+ _objectsWithAliases.append(objectIndex);
+
+ // Stop at Component boundary
+ if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != compiler->rootObjectIndex())
+ return true;
for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Object
@@ -1581,10 +984,6 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
&& binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
continue;
- // Stop at Component boundary
- if (std::binary_search(componentBoundaries.constBegin(), componentBoundaries.constEnd(), binding->value.objectIndex))
- continue;
-
if (!collectIdsAndAliases(binding->value.objectIndex))
return false;
}
@@ -1592,257 +991,205 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
return true;
}
-bool QQmlComponentAndAliasResolver::resolveAliases()
+bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
{
- foreach (int objectIndex, _objectsWithAliases) {
- const QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ if (_objectsWithAliases.isEmpty())
+ return true;
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
- Q_ASSERT(propertyCache);
+ QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
- int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
- int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
- int effectiveAliasIndex = 0;
+ bool atLeastOneAliasResolved;
+ do {
+ atLeastOneAliasResolved = false;
+ QVector<int> pendingObjects;
- const QmlIR::Property *p = obj->firstProperty();
- for (int propertyIndex = 0; propertyIndex < obj->propertyCount(); ++propertyIndex, p = p->next) {
- if (p->type != QV4::CompiledData::Property::Alias)
- continue;
+ for (int objectIndex: qAsConst(_objectsWithAliases)) {
- const int idIndex = p->aliasIdValueIndex;
- const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
- if (targetObjectIndex == -1) {
- recordError(p->aliasLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ QQmlCompileError error;
+ const auto result = resolveAliasesInObject(objectIndex, &error);
+
+ if (error.isSet()) {
+ recordError(error);
return false;
}
- const int targetId = _objectIndexToIdInScope->value(targetObjectIndex, -1);
- Q_ASSERT(targetId != -1);
-
- const QString aliasPropertyValue = stringAt(p->aliasPropertyValueIndex);
-
- QStringRef property;
- QStringRef subProperty;
-
- const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
- if (propertySeparator != -1) {
- property = aliasPropertyValue.leftRef(propertySeparator);
- subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
- } else
- property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
-
- int propIdx = -1;
- int propType = 0;
- int notifySignal = -1;
- int flags = 0;
- int type = 0;
- bool writable = false;
- bool resettable = false;
-
- quint32 propertyFlags = QQmlPropertyData::IsAlias;
-
- if (property.isEmpty()) {
- const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
-
- if (typeRef->type)
- type = typeRef->type->typeId();
- else
- type = typeRef->component->metaTypeId;
-
- flags |= QML_ALIAS_FLAG_PTR;
- propertyFlags |= QQmlPropertyData::IsQObjectDerived;
+
+ if (result == AllAliasesResolved) {
+ aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
+ atLeastOneAliasResolved = true;
+ } else if (result == SomeAliasesResolved) {
+ atLeastOneAliasResolved = true;
+ pendingObjects.append(objectIndex);
} else {
- QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
- Q_ASSERT(targetCache);
- QmlIR::PropertyResolver resolver(targetCache);
+ pendingObjects.append(objectIndex);
+ }
+ }
+ qSwap(_objectsWithAliases, pendingObjects);
+ } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
- QQmlPropertyData *targetProperty = resolver.property(property.toString());
- if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) {
- recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(property.toString()));
- return false;
- }
+ if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
+ const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) {
+ recordError(alias->location, tr("Circular alias reference detected"));
+ return false;
+ }
+ }
+ }
- propIdx = targetProperty->coreIndex;
- type = targetProperty->propType;
+ return true;
+}
- writable = targetProperty->isWritable();
- resettable = targetProperty->isResettable();
- notifySignal = targetProperty->notifyIndex;
+QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error)
+{
+ const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ if (!obj->aliasCount())
+ return AllAliasesResolved;
- if (!subProperty.isEmpty()) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type);
- if (!valueTypeMetaObject) {
- recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- return false;
- }
+ int numResolvedAliases = 0;
+ bool seenUnresolvedAlias = false;
- propType = type;
+ for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
+ if (alias->flags & QV4::CompiledData::Alias::Resolved)
+ continue;
- int valueTypeIndex =
- valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
- if (valueTypeIndex == -1) {
- recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- return false;
- }
- Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+ seenUnresolvedAlias = true;
- propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex);
- if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- type = QVariant::Int;
- else
- type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ const int idIndex = alias->idIndex;
+ const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
+ if (targetObjectIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ break;
+ }
- } else {
- if (targetProperty->isEnum()) {
- type = QVariant::Int;
- } else {
- // Copy type flags
- propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask;
+ const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+ Q_ASSERT(targetObject->id >= 0);
+ alias->targetObjectId = targetObject->id;
+ alias->aliasToLocalAlias = false;
- if (targetProperty->isVarProperty())
- propertyFlags |= QQmlPropertyData::IsQVariant;
+ const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
- if (targetProperty->isQObject())
- flags |= QML_ALIAS_FLAG_PTR;
+ QStringRef property;
+ QStringRef subProperty;
+
+ const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
+ if (propertySeparator != -1) {
+ property = aliasPropertyValue.leftRef(propertySeparator);
+ subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
+ } else
+ property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
+
+ QQmlPropertyIndex propIdx;
+
+ if (property.isEmpty()) {
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ } else {
+ QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QmlIR::PropertyResolver resolver(targetCache);
+
+ QQmlPropertyData *targetProperty = resolver.property(property.toString());
+
+ // If it's an alias that we haven't resolved yet, try again later.
+ if (!targetProperty) {
+ bool aliasPointsToOtherAlias = false;
+ int localAliasIndex = 0;
+ for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) {
+ if (stringAt(targetAlias->nameIndex) == property) {
+ aliasPointsToOtherAlias = true;
+ break;
}
}
- }
-
- QQmlVMEMetaData::AliasData aliasData = { targetId, propIdx, propType, flags, notifySignal };
+ if (aliasPointsToOtherAlias) {
+ if (targetObjectIndex == objectIndex) {
+ alias->localAliasIndex = localAliasIndex;
+ alias->aliasToLocalAlias = true;
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ ++numResolvedAliases;
+ continue;
+ }
- typedef QQmlVMEMetaData VMD;
- QByteArray &dynamicData = (*vmeMetaObjectData)[objectIndex];
- Q_ASSERT(!dynamicData.isEmpty());
- VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
- *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
+ // Try again later and resolve the target alias first.
+ _objectsWithAliases.append(objectIndex);
+ // restore
+ alias->idIndex = idIndex;
+ break;
+ }
+ }
- Q_ASSERT(dynamicData.isDetached());
+ if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ break;
+ }
- if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && writable)
- propertyFlags |= QQmlPropertyData::IsWritable;
- else
- propertyFlags &= ~QQmlPropertyData::IsWritable;
+ propIdx = QQmlPropertyIndex(targetProperty->coreIndex());
- if (resettable)
- propertyFlags |= QQmlPropertyData::IsResettable;
- else
- propertyFlags &= ~QQmlPropertyData::IsResettable;
+ if (!subProperty.isEmpty()) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType());
+ if (!valueTypeMetaObject) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
- QString propertyName = stringAt(p->nameIndex);
- if (propertyIndex == obj->indexOfDefaultProperty) propertyCache->_defaultPropertyName = propertyName;
- propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- type, effectiveSignalIndex++);
+ int valueTypeIndex =
+ valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ } else {
+ if (targetProperty->isQObject())
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ }
}
+
+ alias->encodedMetaPropertyIndex = propIdx.toEncoded();
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ numResolvedAliases++;
}
- return true;
+
+ if (numResolvedAliases == 0)
+ return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved;
+
+ return SomeAliasesResolved;
}
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler)
+QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
- , enginePrivate(typeCompiler->enginePrivate())
- , qmlUnit(typeCompiler->qmlUnit())
- , resolvedTypes(*typeCompiler->resolvedTypes())
- , customParsers(typeCompiler->customParserCache())
+ , qmlObjects(typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
- , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent())
- , customParserBindingsPerObject(typeCompiler->customParserBindings())
+ , customParsers(typeCompiler->customParserCache())
, _seenObjectWithId(false)
{
}
-bool QQmlPropertyValidator::validate()
-{
- _bindingPropertyDataPerObject.resize(qmlUnit->nObjects);
- if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0))
- return false;
- compiler->setDeferredBindingsPerObject(_deferredBindingsPerObject);
- compiler->setBindingPropertyDataPerObject(_bindingPropertyDataPerObject);
- return true;
-}
-
-const QQmlImports &QQmlPropertyValidator::imports() const
+bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
{
- return *compiler->imports();
+ return scanObject(compiler->rootObjectIndex());
}
-typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
-
-struct BindingFinder
-{
- bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const
- {
- return name < binding->propertyNameIndex;
- }
- bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const
- {
- return binding->propertyNameIndex < name;
- }
- bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const
- {
- return lhs->propertyNameIndex < rhs->propertyNameIndex;
- }
-};
-
-bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
+bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
{
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
- if (obj->idIndex != 0)
+ QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ if (obj->idNameIndex != 0)
_seenObjectWithId = true;
- if (isComponent(objectIndex)) {
- Q_ASSERT(obj->nBindings == 1);
- const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->bindingCount() == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
- return validateObject(componentBinding->value.objectIndex, componentBinding);
+ return scanObject(componentBinding->value.objectIndex);
}
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
if (!propertyCache)
return true;
- 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 = customParsers.value(obj->inheritedTypeNameIndex);
- QList<const QV4::CompiledData::Binding*> customBindings;
-
- // Collect group properties first for sanity checking
- // vector values are sorted by property name string index.
- GroupPropertyVector groupProperties;
- const QV4::CompiledData::Binding *binding = obj->bindingTable();
- for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
- if (!binding->isGroupProperty())
- continue;
-
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
- continue;
-
- if (populatingValueTypeGroupProperty) {
- recordError(binding->location, tr("Property assignment expected"));
- return false;
- }
-
- GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
- groupProperties.insert(pos, binding);
- }
-
- QBitArray customParserBindings(obj->nBindings);
- QBitArray deferredBindings;
-
- QmlIR::PropertyResolver propertyResolver(propertyCache);
-
QString defaultPropertyName;
QQmlPropertyData *defaultProperty = 0;
- if (obj->indexOfDefaultProperty != -1) {
+ if (obj->indexOfDefaultPropertyOrAlias != -1) {
QQmlPropertyCache *cache = propertyCache->parent();
defaultPropertyName = cache->defaultPropertyName();
defaultProperty = cache->defaultProperty();
@@ -1851,76 +1198,55 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
defaultProperty = propertyCache->defaultProperty();
}
- QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+ QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
+
+ QmlIR::PropertyResolver propertyResolver(propertyCache);
+
+ 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(','));
+ }
+ }
- binding = obj->bindingTable();
- for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ QQmlPropertyData *pd = 0;
QString name = stringAt(binding->propertyNameIndex);
if (customParser) {
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
- customBindings << binding;
- customParserBindings.setBit(i);
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
continue;
}
} else if (QmlIR::IRBuilder::isSignalPropertyName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
- customBindings << binding;
- customParserBindings.setBit(i);
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
continue;
}
}
- bool bindingToDefaultProperty = false;
- bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
-
- bool notInRevision = false;
- QQmlPropertyData *pd = 0;
- if (!name.isEmpty()) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
- pd = propertyResolver.signal(name, &notInRevision);
- else
- pd = propertyResolver.property(name, &notInRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision);
-
- if (notInRevision) {
- QString typeName = stringAt(obj->inheritedTypeNameIndex);
- QQmlCompiledData::TypeReference *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex);
- if (objectType && objectType->type) {
- COMPILE_EXCEPTION(binding, 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 {
- COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
- }
- }
+ if (name.isEmpty()) {
+ pd = defaultProperty;
+ name = defaultPropertyName;
} else {
- if (isGroupProperty)
- COMPILE_EXCEPTION(binding, tr("Cannot assign a value directly to a grouped property"));
-
- pd = defaultProperty;
- name = defaultPropertyName;
- bindingToDefaultProperty = true;
- }
-
- if (pd)
- collectedBindingPropertyData[i] = pd;
+ if (name.constData()->isUpper())
+ continue;
- if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
- QQmlType *type = 0;
- QQmlImportNamespace *typeNamespace = 0;
- compiler->imports()->resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace);
- if (typeNamespace)
- recordError(binding->location, tr("Invalid use of namespace"));
- else
- recordError(binding->location, tr("Invalid attached object assignment"));
- return false;
+ bool notInRevision = false;
+ pd = propertyResolver.property(name, &notInRevision, QmlIR::PropertyResolver::CheckRevision);
}
bool seenSubObjectWithId = false;
if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
- const bool subObjectValid = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType));
+ const bool subObjectValid = scanObject(binding->value.objectIndex);
qSwap(_seenObjectWithId, seenSubObjectWithId);
if (!subObjectValid)
return false;
@@ -1930,538 +1256,28 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
if (!seenSubObjectWithId
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
- if (deferredBindings.isEmpty())
- deferredBindings.resize(obj->nBindings);
-
- deferredBindings.setBit(i);
+ binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
}
- // Signal handlers were resolved and checked earlier in the signal handler conversion pass.
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
|| binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
continue;
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
- if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
- recordError(binding->location, tr("Attached properties cannot be used here"));
- return false;
- }
- continue;
- }
-
- if (pd) {
- GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
- const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex);
-
- if (!pd->isWritable()
- && !pd->isQList()
- && !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
- ) {
-
- if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
- recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
- else
- recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
- return false;
- }
-
- if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
- QString error;
- if (pd->propType == qMetaTypeId<QQmlScriptString>())
- error = tr( "Cannot assign multiple values to a script property");
- else
- error = tr( "Cannot assign multiple values to a singular property");
- recordError(binding->valueLocation, error);
- return false;
- }
-
- if (!bindingToDefaultProperty
- && !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
- && assigningToGroupProperty) {
- QV4::CompiledData::Location loc = binding->valueLocation;
- if (loc < (*assignedGroupProperty)->valueLocation)
- loc = (*assignedGroupProperty)->valueLocation;
-
- if (pd && QQmlValueTypeFactory::isValueType(pd->propType))
- recordError(loc, tr("Property has already been assigned a value"));
- else
- recordError(loc, tr("Cannot assign a value directly to a grouped property"));
- return false;
- }
-
- if (binding->type < QV4::CompiledData::Binding::Type_Script) {
- if (!validateLiteralBinding(propertyCache, pd, binding))
- return false;
- } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- if (!validateObjectBinding(pd, name, binding))
- return false;
- } else if (binding->isGroupProperty()) {
- if (QQmlValueTypeFactory::isValueType(pd->propType)) {
- if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) {
- if (!pd->isWritable()) {
- recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
- return false;
- }
- } else {
- recordError(binding->location, tr("Invalid grouped property access"));
- return false;
- }
- } else {
- if (!enginePrivate->propertyCacheForType(pd->propType)) {
- recordError(binding->location, tr("Invalid grouped property access"));
- return false;
- }
- }
- }
- } else {
+ if (!pd) {
if (customParser) {
- customBindings << binding;
- customParserBindings.setBit(i);
- continue;
- }
- if (bindingToDefaultProperty) {
- COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent default property"));
- } else {
- COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent property \"%1\"").arg(name));
- }
- }
- }
-
- if (obj->idIndex) {
- bool notInRevision = false;
- collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
- }
-
- if (customParser && !customBindings.isEmpty()) {
- customParser->clearErrors();
- customParser->validator = this;
- customParser->engine = enginePrivate;
- customParser->imports = compiler->imports();
- customParser->verifyBindings(qmlUnit, customBindings);
- customParser->validator = 0;
- customParser->engine = 0;
- customParser->imports = (QQmlImports*)0;
- customParserBindingsPerObject->insert(objectIndex, customParserBindings);
- const QList<QQmlError> parserErrors = customParser->errors();
- if (!parserErrors.isEmpty()) {
- foreach (const QQmlError &error, parserErrors)
- compiler->recordError(error);
- return false;
- }
- }
-
- if (!deferredBindings.isEmpty())
- _deferredBindingsPerObject.insert(objectIndex, deferredBindings);
-
- _bindingPropertyDataPerObject[objectIndex] = collectedBindingPropertyData;
-
- return true;
-}
-
-bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
-{
- if (property->isQList()) {
- recordError(binding->valueLocation, tr("Cannot assign primitives to lists"));
- return false;
- }
-
- if (property->isEnum()) {
- if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
- return true;
-
- QString value = binding->valueAsString(qmlUnit);
- QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex);
- bool ok;
- if (p.isFlagType()) {
- p.enumerator().keysToValue(value.toUtf8().constData(), &ok);
- } else
- p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
-
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
- return false;
- }
- return true;
- }
-
- switch (property->propType) {
- case QMetaType::QVariant:
- break;
- case QVariant::String: {
- if (!binding->evaluatesToString()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: string expected"));
- return false;
- }
- }
- break;
- case QVariant::StringList: {
- if (!binding->evaluatesToString()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: string or string list expected"));
- return false;
- }
- }
- break;
- case QVariant::ByteArray: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
- recordError(binding->valueLocation, tr("Invalid property assignment: byte array expected"));
- return false;
- }
- }
- break;
- case QVariant::Url: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
- recordError(binding->valueLocation, tr("Invalid property assignment: url expected"));
- return false;
- }
- }
- break;
- case QVariant::UInt: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
- if (double(uint(d)) == d)
- return true;
- }
- recordError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected"));
- return false;
- }
- break;
- case QVariant::Int: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
- if (double(int(d)) == d)
- return true;
- }
- recordError(binding->valueLocation, tr("Invalid property assignment: int expected"));
- return false;
- }
- break;
- case QMetaType::Float: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- recordError(binding->valueLocation, tr("Invalid property assignment: number expected"));
- return false;
- }
- }
- break;
- case QVariant::Double: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- recordError(binding->valueLocation, tr("Invalid property assignment: number expected"));
- return false;
- }
- }
- break;
- case QVariant::Color: {
- bool ok = false;
- QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: color expected"));
- return false;
- }
- }
- break;
-#ifndef QT_NO_DATESTRING
- case QVariant::Date: {
- bool ok = false;
- QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: date expected"));
- return false;
- }
- }
- break;
- case QVariant::Time: {
- bool ok = false;
- QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: time expected"));
- return false;
- }
- }
- break;
- case QVariant::DateTime: {
- bool ok = false;
- QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: datetime expected"));
- return false;
- }
- }
- break;
-#endif // QT_NO_DATESTRING
- case QVariant::Point: {
- bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: point expected"));
- return false;
- }
- }
- break;
- case QVariant::PointF: {
- bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: point expected"));
- return false;
- }
- }
- break;
- case QVariant::Size: {
- bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: size expected"));
- return false;
- }
- }
- break;
- case QVariant::SizeF: {
- bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: size expected"));
- return false;
- }
- }
- break;
- case QVariant::Rect: {
- bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: rect expected"));
- return false;
- }
- }
- break;
- case QVariant::RectF: {
- bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: point expected"));
- return false;
- }
- }
- break;
- case QVariant::Bool: {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
- recordError(binding->valueLocation, tr("Invalid property assignment: boolean expected"));
- return false;
- }
- }
- break;
- case QVariant::Vector2D: {
- struct {
- float xp;
- float yp;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected"));
- return false;
- }
- }
- break;
- case QVariant::Vector3D: {
- struct {
- float xp;
- float yp;
- float zy;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected"));
- return false;
- }
- }
- break;
- case QVariant::Vector4D: {
- struct {
- float xp;
- float yp;
- float zy;
- float wp;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected"));
- return false;
- }
- }
- break;
- case QVariant::Quaternion: {
- struct {
- float wp;
- float xp;
- float yp;
- float zp;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: quaternion expected"));
- return false;
- }
- }
- break;
- case QVariant::RegExp:
- recordError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
- return false;
- 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) {
- recordError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected"));
- return false;
- }
- break;
- } else if (property->propType == qMetaTypeId<QList<int> >()) {
- bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
- if (ok) {
- double n = binding->valueAsNumber();
- if (double(int(n)) != n)
- ok = false;
- }
- if (!ok)
- recordError(binding->valueLocation, 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) {
- recordError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected"));
- return false;
- }
- break;
- } else if (property->propType == qMetaTypeId<QList<QUrl> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
- recordError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected"));
- return false;
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
}
- break;
- } else if (property->propType == qMetaTypeId<QList<QString> >()) {
- if (!binding->evaluatesToString()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected"));
- return false;
- }
- break;
- } else if (property->propType == qMetaTypeId<QJSValue>()) {
- break;
- } else if (property->propType == qMetaTypeId<QQmlScriptString>()) {
- break;
- }
-
- // otherwise, try a custom type assignment
- QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType);
- if (!converter) {
- recordError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType))));
- return false;
}
}
- break;
- }
- return true;
-}
-
-/*!
- Returns true if from can be assigned to a (QObject) property of type
- to.
-*/
-bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
-{
- QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
-
- while (fromMo) {
- if (fromMo == toMo)
- return true;
- fromMo = fromMo->parent();
- }
- return false;
-}
-bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
-{
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
-
- bool isValueSource = false;
- bool isPropertyInterceptor = false;
-
- QQmlType *qmlType = 0;
- const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex);
- if (typeRef) {
- QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- const QMetaObject *mo = cache->firstCppMetaObject();
- while (mo && !qmlType) {
- qmlType = QQmlMetaType::qmlType(mo);
- mo = mo->superClass();
- }
- Q_ASSERT(qmlType);
- }
-
- if (qmlType) {
- isValueSource = qmlType->propertyValueSourceCast() != -1;
- isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1;
- }
-
- if (!isValueSource && !isPropertyInterceptor) {
- recordError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
- return false;
- }
-
- return true;
- }
-
- if (QQmlMetaType::isInterface(property->propType)) {
- // Can only check at instantiation time if the created sub-object successfully casts to the
- // target interface.
- return true;
- } else if (property->propType == QMetaType::QVariant) {
- // We can convert everything to QVariant :)
- return true;
- } else if (property->isQList()) {
- const int listType = enginePrivate->listType(property->propType);
- if (!QQmlMetaType::isInterface(listType)) {
- QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex);
- if (!canCoerce(listType, source)) {
- recordError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
- return false;
- }
- }
- return true;
- } else if (isComponent(binding->value.objectIndex)) {
- return true;
- } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
- return true;
- } else if (QQmlValueTypeFactory::isValueType(property->propType)) {
- recordError(binding->location, tr("Unexpected object assignment"));
- return false;
- } else if (property->propType == qMetaTypeId<QQmlScriptString>()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: script expected"));
- return false;
- } else {
- // We want to 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);
-
- // Will be true if the assgned type inherits propertyMetaObject
- bool isAssignable = false;
- // Determine isAssignable value
- if (propertyMetaObject) {
- QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex);
- while (c && !isAssignable) {
- isAssignable |= c == propertyMetaObject;
- c = c->parent();
- }
- }
-
- if (!isAssignable) {
- recordError(binding->valueLocation, tr("Cannot assign object to property"));
- return false;
- }
- }
return true;
}
QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen)
: QQmlCompilePass(typeCompiler)
- , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent())
- , resolvedTypes(*typeCompiler->resolvedTypes())
+ , resolvedTypes(typeCompiler->resolvedTypes)
, customParsers(typeCompiler->customParserCache())
, qmlObjects(*typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
@@ -2471,48 +1287,42 @@ QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::
bool QQmlJSCodeGenerator::generateCodeForComponents()
{
- const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent = *compiler->objectIndexToIdPerComponent();
- for (QHash<int, QHash<int, int> >::ConstIterator component = objectIndexToIdPerComponent.constBegin(), end = objectIndexToIdPerComponent.constEnd();
- component != end; ++component) {
- if (!compileComponent(component.key(), component.value()))
+ const QVector<quint32> &componentRoots = compiler->componentRoots();
+ for (int i = 0; i < componentRoots.count(); ++i) {
+ if (!compileComponent(componentRoots.at(i)))
return false;
}
- return compileComponent(compiler->rootObjectIndex(), *compiler->objectIndexToIdForRoot());
+ return compileComponent(compiler->rootObjectIndex());
}
-bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, int> &objectIndexToId)
+bool QQmlJSCodeGenerator::compileComponent(int contextObject)
{
- if (isComponent(contextObject)) {
- const QmlIR::Object *component = qmlObjects.at(contextObject);
- Q_ASSERT(component->bindingCount() == 1);
- const QV4::CompiledData::Binding *componentBinding = component->firstBinding();
+ const QmlIR::Object *obj = qmlObjects.at(contextObject);
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->bindingCount() == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
contextObject = componentBinding->value.objectIndex;
}
QmlIR::JSCodeGen::ObjectIdMapping idMapping;
- if (!objectIndexToId.isEmpty()) {
- idMapping.reserve(objectIndexToId.count());
+ idMapping.reserve(obj->namedObjectsInComponent.count);
+ for (int i = 0; i < obj->namedObjectsInComponent.count; ++i) {
+ const int objectIndex = obj->namedObjectsInComponent.at(i);
+ QmlIR::JSCodeGen::IdMapping m;
+ const QmlIR::Object *obj = qmlObjects.at(objectIndex);
+ m.name = stringAt(obj->idNameIndex);
+ m.idIndex = obj->id;
+ m.type = propertyCaches->at(objectIndex);
- for (QHash<int, int>::ConstIterator idIt = objectIndexToId.constBegin(), end = objectIndexToId.constEnd();
- idIt != end; ++idIt) {
+ auto *tref = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ if (tref && tref->isFullyDynamicType)
+ m.type = 0;
- const int objectIndex = idIt.key();
- QmlIR::JSCodeGen::IdMapping m;
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
- m.name = stringAt(obj->idIndex);
- m.idIndex = idIt.value();
- m.type = propertyCaches.at(objectIndex);
-
- QQmlCompiledData::TypeReference *tref = resolvedTypes.value(obj->inheritedTypeNameIndex);
- if (tref && tref->isFullyDynamicType)
- m.type = 0;
-
- idMapping << m;
- }
+ idMapping << m;
}
- v4CodeGen->beginContextScope(idMapping, propertyCaches.at(contextObject));
+ v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject));
if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
return false;
@@ -2522,12 +1332,12 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, i
bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex)
{
- if (isComponent(objectIndex))
+ QmlIR::Object *object = qmlObjects.at(objectIndex);
+ if (object->flags & QV4::CompiledData::Object::IsComponent)
return true;
- QmlIR::Object *object = qmlObjects.at(objectIndex);
if (object->functionsAndExpressions->count > 0) {
- QQmlPropertyCache *scopeObject = propertyCaches.at(scopeObjectIndex);
+ QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex);
v4CodeGen->beginObjectScope(scopeObject);
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
@@ -2546,8 +1356,7 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn
}
QQmlJS::MemoryPool *pool = compiler->memoryPool();
- object->runtimeFunctionIndices = pool->New<QmlIR::FixedPoolArray<int> >();
- object->runtimeFunctionIndices->init(pool, runtimeFunctionIndices);
+ object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices);
}
for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
@@ -2580,20 +1389,20 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties()
void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
{
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
if (!propertyCache)
return;
QmlIR::Object *object = qmlObjects.at(objectIndex);
- QString defaultProperty = object->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
+ QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
QmlIR::Binding *bindingsToReinsert = 0;
QmlIR::Binding *tail = 0;
QmlIR::Binding *previousBinding = 0;
QmlIR::Binding *binding = object->firstBinding();
while (binding) {
- if (binding->propertyNameIndex == 0 || stringAt(binding->propertyNameIndex) != defaultProperty) {
+ if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) {
previousBinding = binding;
binding = binding->next;
continue;
@@ -2646,7 +1455,7 @@ void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBinding
if (binding->type != QV4::CompiledData::Binding::Type_Script)
continue;
- const int irFunctionIndex = obj->runtimeFunctionIndices->at(binding->value.compiledScriptIndex);
+ const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex);
QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex);
if (simplifyBinding(irFunction, binding)) {
irFunctionsToRemove.append(irFunctionIndex);
@@ -2757,7 +1566,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR:
for (QV4::IR::BasicBlock *bb : function->basicBlocks()) {
for (QV4::IR::Stmt *s : bb->statements()) {
- s->accept(this);
+ visit(s);
if (!_canSimplify)
return false;
}
@@ -2927,22 +1736,41 @@ void QQmlIRFunctionCleanser::clean()
foreach (QV4::IR::Function *function, module->functions) {
for (QV4::IR::BasicBlock *block : function->basicBlocks()) {
for (QV4::IR::Stmt *s : block->statements()) {
- s->accept(this);
+ visit(s);
}
}
}
foreach (QmlIR::Object *obj, *compiler->qmlObjects()) {
- if (!obj->runtimeFunctionIndices)
- continue;
- for (int i = 0; i < obj->runtimeFunctionIndices->count; ++i)
- (*obj->runtimeFunctionIndices)[i] = newFunctionIndices[obj->runtimeFunctionIndices->at(i)];
+ 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::visitClosure(QV4::IR::Closure *closure)
+void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e)
{
- closure->value = newFunctionIndices.at(closure->value);
+ 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/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 273ba01a88..de6abb4ced 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -53,13 +53,12 @@
#include <qglobal.h>
#include <qqmlerror.h>
#include <qhash.h>
-#include <private/qqmlcompiler_p.h>
+#include <private/qqmltypeloader_p.h>
#include <private/qqmlirbuilder_p.h>
QT_BEGIN_NAMESPACE
class QQmlEnginePrivate;
-class QQmlCompiledData;
class QQmlError;
class QQmlTypeData;
class QQmlImports;
@@ -75,18 +74,40 @@ 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, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *document);
+ QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
+
+ // --- interface used by QQmlPropertyCacheCreator
+ typedef QmlIR::Object CompiledObject;
+ const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); }
+ int objectCount() const { return document->objects.count(); }
+ 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;
+ // ---
- bool compile();
+ QV4::CompiledData::CompilationUnit *compile();
QList<QQmlError> compilationErrors() const { return errors; }
- void recordError(const QQmlError &error);
+ void recordError(QQmlError error);
+ void recordError(const QV4::CompiledData::Location &location, const QString &description);
+ void recordError(const QQmlCompileError &error);
- QString stringAt(int idx) const;
int registerString(const QString &str);
QV4::IR::Module *jsIRModule() const;
@@ -96,70 +117,52 @@ public:
QUrl url() const { return typeData->finalUrl(); }
QQmlEnginePrivate *enginePrivate() const { return engine; }
const QQmlImports *imports() const;
- QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes();
- QList<QmlIR::Object*> *qmlObjects();
+ QVector<QmlIR::Object *> *qmlObjects() const;
int rootObjectIndex() const;
- void setPropertyCaches(const QVector<QQmlPropertyCache *> &caches);
- const QVector<QQmlPropertyCache *> &propertyCaches() const;
- void setVMEMetaObjects(const QVector<QByteArray> &metaObjects);
- QVector<QByteArray> *vmeMetaObjects() const;
- QHash<int, int> *objectIndexToIdForRoot();
- QHash<int, QHash<int, int> > *objectIndexToIdPerComponent();
- QHash<int, QBitArray> *customParserBindings();
+ void setPropertyCaches(QQmlPropertyCacheVector &&caches);
+ const QQmlPropertyCacheVector *propertyCaches() const;
+ QQmlPropertyCacheVector &&takePropertyCaches();
+ void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; }
+ const QVector<quint32> &componentRoots() const { return m_componentRoots; }
QQmlJS::MemoryPool *memoryPool();
QStringRef newStringRef(const QString &string);
const QV4::Compiler::StringTableGenerator *stringPool() const;
- void setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject);
void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData);
const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; }
QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const;
+ void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion);
+
private:
QList<QQmlError> errors;
QQmlEnginePrivate *engine;
- QQmlCompiledData *compiledData;
QQmlTypeData *typeData;
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
QmlIR::Document *document;
// index is string index of type name (use obj->inheritedTypeNameIndex)
QHash<int, QQmlCustomParser*> customParsers;
+
+ // index in first hash is component index, vector inside contains object indices of objects with id property
+ QVector<quint32> m_componentRoots;
+ QQmlPropertyCacheVector m_propertyCaches;
};
struct QQmlCompilePass
{
- virtual ~QQmlCompilePass() {}
-
QQmlCompilePass(QQmlTypeCompiler *typeCompiler);
QString stringAt(int idx) const { return compiler->stringAt(idx); }
protected:
- void recordError(const QV4::CompiledData::Location &location, const QString &description) const;
+ void recordError(const QV4::CompiledData::Location &location, const QString &description) const
+ { compiler->recordError(location, description); }
+ void recordError(const QQmlCompileError &error)
+ { compiler->recordError(error); }
QQmlTypeCompiler *compiler;
};
-class QQmlPropertyCacheCreator : public QQmlCompilePass
-{
- Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator)
-public:
- QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler);
- ~QQmlPropertyCacheCreator();
-
- bool buildMetaObjects();
-protected:
- bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding);
- bool ensureMetaObject(int objectIndex);
- bool createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache);
-
- QQmlEnginePrivate *enginePrivate;
- const QList<QmlIR::Object*> &qmlObjects;
- const QQmlImports *imports;
- QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes;
- QVector<QByteArray> vmeMetaObjects;
- QVector<QQmlPropertyCache*> propertyCaches;
-};
-
// "Converts" signal expressions to full-fleged function declarations with
// parameters taken from the signal declarations
// It also updates the QV4::CompiledData::Binding objects to set the property name
@@ -176,12 +179,12 @@ private:
bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache);
QQmlEnginePrivate *enginePrivate;
- const QList<QmlIR::Object*> &qmlObjects;
+ const QVector<QmlIR::Object*> &qmlObjects;
const QQmlImports *imports;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QSet<QString> &illegalNames;
- const QVector<QQmlPropertyCache*> &propertyCaches;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
// ### This will go away when the codegen resolves all enums to constant expressions
@@ -207,10 +210,10 @@ private:
int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const;
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
const QQmlImports *imports;
- QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes;
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes;
};
class QQmlCustomParserScriptIndexer: public QQmlCompilePass
@@ -223,7 +226,7 @@ public:
private:
void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false);
- const QList<QmlIR::Object*> &qmlObjects;
+ const QVector<QmlIR::Object*> &qmlObjects;
const QHash<int, QQmlCustomParser*> &customParsers;
};
@@ -235,8 +238,8 @@ public:
void annotateBindingsToAliases();
private:
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
class QQmlScriptStringScanner : public QQmlCompilePass
@@ -247,8 +250,8 @@ public:
void scan();
private:
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
class QQmlComponentAndAliasResolver : public QQmlCompilePass
@@ -262,64 +265,49 @@ public:
protected:
void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache);
bool collectIdsAndAliases(int objectIndex);
- bool resolveAliases();
+ bool resolveAliases(int componentIndex);
+ void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
+
+ enum AliasResolutionResult {
+ NoAliasResolved,
+ SomeAliasesResolved,
+ AllAliasesResolved
+ };
+
+ AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error);
QQmlEnginePrivate *enginePrivate;
QQmlJS::MemoryPool *pool;
- QList<QmlIR::Object*> *qmlObjects;
+ QVector<QmlIR::Object*> *qmlObjects;
const int indexOfRootObject;
// indices of the objects that are actually Component {}
- QVector<int> componentRoots;
- // indices of objects that are the beginning of a new component
- // scope. This is sorted and used for binary search.
- QVector<quint32> componentBoundaries;
-
- int _componentIndex;
- QHash<int, int> _idToObjectIndex;
- QHash<int, int> *_objectIndexToIdInScope;
- QList<int> _objectsWithAliases;
-
- QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes;
- QVector<QQmlPropertyCache *> propertyCaches;
- QVector<QByteArray> *vmeMetaObjectData;
- QHash<int, int> *objectIndexToIdForRoot;
- QHash<int, QHash<int, int> > *objectIndexToIdPerComponent;
+ QVector<quint32> componentRoots;
+
+ // Deliberate choice of map over hash here to ensure stable generated output.
+ QMap<int, int> _idToObjectIndex;
+ QVector<int> _objectsWithAliases;
+
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes;
+ QQmlPropertyCacheVector propertyCaches;
};
-class QQmlPropertyValidator : public QQmlCompilePass
+class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
{
- Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler);
-
- bool validate();
+ QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler);
- const QQmlImports &imports() const;
- QQmlEnginePrivate *engine() const { return enginePrivate; }
+ bool scanObject();
private:
- bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const;
- bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const;
- bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const;
-
- bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
-
- bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
+ bool scanObject(int objectIndex);
- QQmlEnginePrivate *enginePrivate;
- const QV4::CompiledData::Unit *qmlUnit;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ QVector<QmlIR::Object*> *qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QVector<QQmlPropertyCache *> &propertyCaches;
- const QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
- QHash<int, QBitArray> *customParserBindingsPerObject;
-
- // collected state variables, essentially write-only
- mutable QHash<int, QBitArray> _deferredBindingsPerObject;
- mutable bool _seenObjectWithId;
- mutable QVector<QV4::CompiledData::BindingPropertyData> _bindingPropertyDataPerObject;
+
+ bool _seenObjectWithId;
};
// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone.
@@ -331,16 +319,13 @@ public:
bool generateCodeForComponents();
private:
- bool compileComponent(int componentRoot, const QHash<int, int> &objectIndexToId);
+ bool compileComponent(int componentRoot);
bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
- bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
-
- const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> &propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
QmlIR::JSCodeGen * const v4CodeGen;
};
@@ -354,11 +339,11 @@ public:
private:
void mergeDefaultProperties(int objectIndex);
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache*> &propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
-class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass, public QV4::IR::StmtVisitor
+class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass
{
public:
QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler);
@@ -368,12 +353,30 @@ public:
private:
void reduceTranslationBindings(int objectIndex);
- virtual void visitMove(QV4::IR::Move *move);
- virtual void visitJump(QV4::IR::Jump *) {}
- virtual void visitCJump(QV4::IR::CJump *) { discard(); }
- virtual void visitExp(QV4::IR::Exp *) { discard(); }
- virtual void visitPhi(QV4::IR::Phi *) {}
- virtual void visitRet(QV4::IR::Ret *ret);
+ 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);
@@ -382,7 +385,7 @@ private:
bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding);
bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding);
- const QList<QmlIR::Object*> &qmlObjects;
+ const QVector<QmlIR::Object*> &qmlObjects;
QV4::IR::Module *jsModule;
bool _canSimplify;
@@ -397,8 +400,7 @@ private:
QVector<int> irFunctionsToRemove;
};
-class QQmlIRFunctionCleanser : public QQmlCompilePass, public QV4::IR::StmtVisitor,
- public QV4::IR::ExprVisitor
+class QQmlIRFunctionCleanser : public QQmlCompilePass
{
public:
QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove);
@@ -406,51 +408,13 @@ public:
void clean();
private:
- virtual void visitClosure(QV4::IR::Closure *closure);
-
- virtual void visitTemp(QV4::IR::Temp *) {}
- virtual void visitArgLocal(QV4::IR::ArgLocal *) {}
-
virtual void visitMove(QV4::IR::Move *s) {
- s->source->accept(this);
- s->target->accept(this);
- }
-
- virtual void visitConvert(QV4::IR::Convert *e) { e->expr->accept(this); }
- virtual void visitPhi(QV4::IR::Phi *) { }
-
- virtual void visitExp(QV4::IR::Exp *s) { s->expr->accept(this); }
-
- virtual void visitJump(QV4::IR::Jump *) {}
- virtual void visitCJump(QV4::IR::CJump *s) { s->cond->accept(this); }
- virtual void visitRet(QV4::IR::Ret *s) { s->expr->accept(this); }
-
- virtual void visitConst(QV4::IR::Const *) {}
- virtual void visitString(QV4::IR::String *) {}
- virtual void visitRegExp(QV4::IR::RegExp *) {}
- virtual void visitName(QV4::IR::Name *) {}
- virtual void visitUnop(QV4::IR::Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(QV4::IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(QV4::IR::Call *e) {
- e->base->accept(this);
- for (QV4::IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
+ visit(s->source);
+ visit(s->target);
}
- virtual void visitNew(QV4::IR::New *e) {
- e->base->accept(this);
- for (QV4::IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitSubscript(QV4::IR::Subscript *e) {
- e->base->accept(this);
- e->index->accept(this);
- }
-
- virtual void visitMember(QV4::IR::Member *e) {
- e->base->accept(this);
- }
+ void visit(QV4::IR::Stmt *s);
+ void visit(QV4::IR::Expr *e);
private:
QV4::IR::Module *module;
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 461ff89550..e0def1021b 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2026,6 +2026,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount);
function->isStrict = _env->isStrict;
function->isNamedExpression = _env->isNamedFunctionExpression;
+ function->isQmlBinding = _env->compilationMode == QmlBinding;
AST::SourceLocation loc = ast->firstSourceLocation();
function->line = loc.startLine;
@@ -2046,7 +2047,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
}
} else {
if (!_env->isStrict) {
- foreach (const QString &inheritedLocal, inheritedLocals) {
+ for (const QString &inheritedLocal : qAsConst(inheritedLocals)) {
function->LOCAL(inheritedLocal);
unsigned tempIndex = entryBlock->newTemp();
Environment::Member member = { Environment::UndefinedMember,
@@ -2088,7 +2089,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
_function->RECEIVE(it->name.toString());
}
- foreach (const Environment::Member &member, _env->members) {
+ for (const Environment::Member &member : qAsConst(_env->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);
@@ -2939,7 +2940,7 @@ QList<QQmlError> Codegen::qmlErrors() const
qmlErrors.reserve(_errors.size());
QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName));
- foreach (const QQmlJS::DiagnosticMessage &msg, _errors) {
+ for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) {
QQmlError e;
e.setUrl(url);
e.setLine(msg.loc.startLine);
diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp
new file mode 100644
index 0000000000..2e1213464c
--- /dev/null
+++ b/src/qml/compiler/qv4compilationunitmapper.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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 "qv4compilationunitmapper_p.h"
+
+#include "qv4compileddata_p.h"
+#include <QFileInfo>
+#include <QDateTime>
+#include <QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompilationUnitMapper::CompilationUnitMapper()
+ : dataPtr(nullptr)
+{
+
+}
+
+CompilationUnitMapper::~CompilationUnitMapper()
+{
+ close();
+}
+
+bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const QString &sourcePath, 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;
+ }
+
+ {
+ QFileInfo sourceCode(sourcePath);
+ QDateTime sourceTimeStamp;
+ if (sourceCode.exists())
+ sourceTimeStamp = sourceCode.lastModified();
+
+ // 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
new file mode 100644
index 0000000000..5b6939f1cf
--- /dev/null
+++ b/src/qml/compiler/qv4compilationunitmapper_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 QV4COMPILATIONUNITMAPPER_H
+#define QV4COMPILATIONUNITMAPPER_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 <QFile>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace CompiledData {
+struct Unit;
+}
+
+class CompilationUnitMapper
+{
+public:
+ CompilationUnitMapper();
+ ~CompilationUnitMapper();
+
+ CompiledData::Unit *open(const QString &cacheFilePath, const QString &sourcePath, QString *errorString);
+ void close();
+
+private:
+ static bool verifyHeader(const QV4::CompiledData::Unit *header, const QString &sourcePath, QString *errorString);
+
+#if defined(Q_OS_UNIX)
+ size_t length;
+#endif
+ void *dataPtr;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4COMPILATIONUNITMAPPER_H
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
new file mode 100644
index 0000000000..1aa3e05f5f
--- /dev/null
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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 "qv4compilationunitmapper_p.h"
+
+#include <sys/mman.h>
+#include <functional>
+#include <private/qcore_unix_p.h>
+#include <private/qdeferredcleanup_p.h>
+
+#include "qv4compileddata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString)
+{
+ close();
+
+ int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY);
+ if (fd == -1) {
+ *errorString = qt_error_string(errno);
+ return nullptr;
+ }
+
+ QDeferredCleanup cleanup([fd]{
+ qt_safe_close(fd) ;
+ });
+
+ CompiledData::Unit header;
+ qint64 bytesRead = qt_safe_read(fd, reinterpret_cast<char *>(&header), sizeof(header));
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return nullptr;
+ }
+
+ if (!verifyHeader(&header, sourcePath, errorString))
+ return nullptr;
+
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
+ length = static_cast<size_t>(lseek(fd, 0, SEEK_END));
+
+ void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0);
+ if (ptr == MAP_FAILED) {
+ *errorString = qt_error_string(errno);
+ return nullptr;
+ }
+ dataPtr = ptr;
+
+ return reinterpret_cast<CompiledData::Unit*>(dataPtr);
+}
+
+void CompilationUnitMapper::close()
+{
+ if (dataPtr != nullptr)
+ munmap(dataPtr, length);
+ dataPtr = nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
new file mode 100644
index 0000000000..457b702ac3
--- /dev/null
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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 "qv4compilationunitmapper_p.h"
+
+#include "qv4compileddata_p.h"
+#include <private/qdeferredcleanup_p.h>
+#include <QFileInfo>
+#include <QDateTime>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString)
+{
+ close();
+
+ // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry
+ // is exported from QtCore.
+ HANDLE handle =
+#if defined(Q_OS_WINRT)
+ CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()),
+ GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
+ OPEN_EXISTING, nullptr);
+#else
+ CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()),
+ GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+#endif
+ if (handle == INVALID_HANDLE_VALUE) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ QDeferredCleanup fileHandleCleanup([handle]{
+ CloseHandle(handle);
+ });
+
+#if !defined(Q_OS_WINRT) || _MSC_VER >= 1900
+ CompiledData::Unit header;
+ DWORD bytesRead;
+ if (!ReadFile(handle, reinterpret_cast<char *>(&header), sizeof(header), &bytesRead, nullptr)) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return nullptr;
+ }
+
+ if (!verifyHeader(&header, sourcePath, 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);
+ if (!fileMappingHandle) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ QDeferredCleanup mappingCleanup([fileMappingHandle]{
+ CloseHandle(fileMappingHandle);
+ });
+
+ dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0);
+ if (!dataPtr) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ return reinterpret_cast<CompiledData::Unit*>(dataPtr);
+#else
+ Q_UNUSED(sourcePath);
+ *errorString = QStringLiteral("Compilation unit mapping not supported on WinRT 8.1");
+ return nullptr;
+#endif
+}
+
+void CompilationUnitMapper::close()
+{
+#if !defined(Q_OS_WINRT) || _MSC_VER >= 1900
+ if (dataPtr != nullptr)
+ UnmapViewOfFile(dataPtr);
+#endif
+ dataPtr = nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index a63f35152a..e815c41a86 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -47,12 +47,30 @@
#include <private/qv4lookup_p.h>
#include <private/qv4regexpobject_p.h>
#include <private/qqmlpropertycache_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmlengine_p.h>
+#include "qv4compilationunitmapper_p.h"
+#include <QQmlPropertyMap>
+#include <QDateTime>
+#include <QSaveFile>
+#include <QFile>
+#include <QFileInfo>
+#include <QScopedValueRollback>
+#include <QStandardPaths>
+#include <QDir>
#endif
#include <private/qqmlirbuilder_p.h>
#include <QCoreApplication>
+#include <QCryptographicHash>
#include <algorithm>
+#if defined(QT_BUILD_INTERNAL)
+#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST)
+#include <dlfcn.h>
+#endif
+#endif
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -67,11 +85,20 @@ CompilationUnit::CompilationUnit()
, 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;
}
QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
@@ -109,7 +136,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup *l = runtimeLookups + i;
- Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags);
+ Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags));
if (type == CompiledData::Lookup::Type_Getter)
l->getter = QV4::Lookup::getterGeneric;
else if (type == CompiledData::Lookup::Type_Setter)
@@ -144,14 +171,18 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
}
- linkBackendToEngine(engine);
-
-#if 0
- runtimeFunctionsSortedByAddress.resize(runtimeFunctions.size());
- memcpy(runtimeFunctionsSortedByAddress.data(), runtimeFunctions.data(), runtimeFunctions.size() * sizeof(QV4::Function*));
- std::sort(runtimeFunctionsSortedByAddress.begin(), runtimeFunctionsSortedByAddress.end(), functionSortHelper);
+#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
+ linkBackendToEngine(engine);
+
if (data->indexOfRootFunction != -1)
return runtimeFunctions[data->indexOfRootFunction];
else
@@ -162,10 +193,26 @@ void CompilationUnit::unlink()
{
if (engine)
engine->compilationUnits.erase(engine->compilationUnits.find(this));
+
+ 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);
+ isRegisteredWithEngine = false;
+ }
+
+ propertyCaches.clear();
+
+ for (int ii = 0; ii < dependentScripts.count(); ++ii)
+ dependentScripts.at(ii)->release();
+ dependentScripts.clear();
+
+ importCache = nullptr;
+
+ qDeleteAll(resolvedTypes);
+ resolvedTypes.clear();
+
engine = 0;
- if (data && !(data->flags & QV4::CompiledData::Unit::StaticData))
- free(data);
- data = 0;
free(runtimeStrings);
runtimeStrings = 0;
delete [] runtimeLookups;
@@ -176,6 +223,9 @@ void CompilationUnit::unlink()
runtimeClasses = 0;
qDeleteAll(runtimeFunctions);
runtimeFunctions.clear();
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+#endif
}
void CompilationUnit::markObjects(QV4::ExecutionEngine *e)
@@ -189,6 +239,219 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e)
}
}
+void CompilationUnit::destroy()
+{
+ QQmlEngine *qmlEngine = 0;
+ if (engine && engine->v8Engine)
+ qmlEngine = engine->v8Engine->engine();
+ if (qmlEngine)
+ QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
+ else
+ delete this;
+}
+
+IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(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);
+ }
+ return *it;
+}
+
+void CompilationUnit::finalize(QQmlEnginePrivate *engine)
+{
+ // Add to type registry of composites
+ if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject))
+ engine->registerInternalCompositeType(this);
+ else {
+ const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject);
+ 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();
+ }
+ }
+
+ // Collect some data for instantiation later.
+ 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);
+ bindingCount += obj->nBindings;
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ if (QQmlType *qmlType = typeRef->type) {
+ if (qmlType->parserStatusCast() != -1)
+ ++parserStatusCount;
+ }
+ ++objectCount;
+ if (typeRef->compilationUnit) {
+ bindingCount += typeRef->compilationUnit->totalBindingsCount;
+ parserStatusCount += typeRef->compilationUnit->totalParserStatusCount;
+ objectCount += typeRef->compilationUnit->totalObjectCount;
+ }
+ }
+ }
+
+ totalBindingsCount = bindingCount;
+ totalParserStatusCount = parserStatusCount;
+ totalObjectCount = objectCount;
+}
+
+bool CompilationUnit::verifyChecksum(QQmlEngine *engine,
+ const ResolvedTypeReferenceMap &dependentTypes) const
+{
+ if (dependentTypes.isEmpty()) {
+ for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
+ if (data->dependencyMD5Checksum[i] != 0)
+ return false;
+ }
+ return true;
+ }
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (!dependentTypes.addToHash(&hash, engine))
+ return false;
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum));
+ return memcmp(data->dependencyMD5Checksum, checksum.constData(),
+ sizeof(data->dependencyMD5Checksum)) == 0;
+}
+
+static QString cacheFilePath(const QUrl &url)
+{
+ 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();
+}
+
+bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
+{
+ errorString->clear();
+
+ if (data->sourceTimeStamp == 0) {
+ *errorString = QStringLiteral("Missing time stamp for source file");
+ return false;
+ }
+
+ if (!QQmlFile::isLocalFile(unitUrl)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ // Foo.qml -> Foo.qmlc
+ QSaveFile cacheFile(cacheFilePath(unitUrl));
+ if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ *errorString = cacheFile.errorString();
+ return false;
+ }
+
+ QByteArray modifiedUnit;
+ modifiedUnit.resize(data->unitSize);
+ memcpy(modifiedUnit.data(), data, data->unitSize);
+ const char *dataPtr = modifiedUnit.data();
+ Unit *unitPtr;
+ 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;
+ }
+
+ return true;
+}
+
+bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString)
+{
+ if (!QQmlFile::isLocalFile(url)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ const QString sourcePath = url.toLocalFile();
+ QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
+
+ CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString);
+ if (!mappedUnit)
+ return false;
+
+ const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
+ QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit);
+
+ {
+ 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;
+ }
+ }
+
+ {
+ 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;
+ }
+ }
+
+ if (!memoryMapCode(errorString))
+ return false;
+
+ dataPtrChange.commit();
+ free(const_cast<Unit*>(oldDataPtr));
+ backingFile.reset(cacheFile.take());
+ return true;
+}
+
+void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit)
+{
+ 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;
+}
+
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ *errorString = QStringLiteral("Missing code mapping backend");
+ return false;
+}
#endif // V4_BOOTSTRAP
Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
@@ -205,7 +468,7 @@ QString Binding::valueAsString(const Unit *unit) const
case Type_Boolean:
return value.b ? QStringLiteral("true") : QStringLiteral("false");
case Type_Number:
- return QString::number(value.d);
+ return QString::number(valueAsNumber());
case Type_Invalid:
return QString();
#ifdef QT_NO_TRANSLATION
@@ -287,6 +550,130 @@ 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
+{
+ if (type)
+ return typePropertyCache;
+ else
+ return compilationUnit->rootPropertyCache();
+}
+
+/*!
+Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
+*/
+QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+{
+ if (typePropertyCache) {
+ return typePropertyCache;
+ } else if (type) {
+ typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject());
+ return typePropertyCache;
+ } else {
+ return compilationUnit->rootPropertyCache();
+ }
+}
+
+bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine)
+{
+ if (type) {
+ bool ok = false;
+ hash->addData(createPropertyCache(engine)->checksum(&ok));
+ return ok;
+ }
+ hash->addData(compilationUnit->data->md5Checksum, sizeof(compilationUnit->data->md5Checksum));
+ return true;
+}
+
+template <typename T>
+bool qtTypeInherits(const QMetaObject *mo) {
+ while (mo) {
+ if (mo == &T::staticMetaObject)
+ return true;
+ mo = mo->superClass();
+ }
+ return false;
+}
+
+void ResolvedTypeReference::doDynamicTypeCheck()
+{
+ const QMetaObject *mo = 0;
+ if (typePropertyCache)
+ mo = typePropertyCache->firstCppMetaObject();
+ else if (type)
+ 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(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST)
+ 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::Sha1);
+ 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) {
+ if (!it.value()->addToHash(hash, engine))
+ 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 Unit::generateChecksum()
+{
+#ifndef V4_BOOTSTRAP
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ const int checksummableDataOffset = qOffsetOf(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum);
+
+ const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset;
+ hash.addData(dataPtr, unitSize - checksummableDataOffset);
+
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(md5Checksum));
+ memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum));
+#else
+ memset(md5Checksum, 0, sizeof(md5Checksum));
+#endif
+}
+
}
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 8c617875e0..90cbe04505 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -60,11 +60,26 @@
#include <private/qv4executableallocator_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmlnullablevalue_p.h>
+#include <private/qv4identifier_p.h>
+#include <private/qflagpointer_p.h>
+#include <private/qjson_p.h>
+#ifndef V4_BOOTSTRAP
+#include <private/qqmltypenamecache_p.h>
+#include <private/qqmlpropertycache_p.h>
+#endif
QT_BEGIN_NAMESPACE
+// Bump this whenever the compiler data structures change in an incompatible way.
+#define QV4_DATA_STRUCTURE_VERSION 0x07
+
+class QIODevice;
class QQmlPropertyCache;
class QQmlPropertyData;
+class QQmlTypeNameCache;
+class QQmlScriptData;
+class QQmlType;
+class QQmlEngine;
namespace QmlIR {
struct Document;
@@ -76,25 +91,49 @@ struct Function;
}
struct Function;
+class EvalISelFactory;
+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;
struct RegExp;
struct Unit;
+template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const>
+struct TableIterator
+{
+ TableIterator(const Container *container, int index) : container(container), index(index) {}
+ const Container *container;
+ int index;
+
+ const ItemType *operator->() { return (container->*IndexedGetter)(index); }
+ void operator++() { ++index; }
+ bool operator==(const TableIterator &rhs) const { return index == rhs.index; }
+ 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
{
- qint32 line;
- qint32 column;
+ union {
+ QJsonPrivate::qle_bitfield<0, 20> line;
+ QJsonPrivate::qle_bitfield<20, 12> column;
+ };
- Location(): line(-1), column(-1) {}
+ Location() { line = 0; column = 0; }
inline bool operator<(const Location &other) const {
return line < other.line ||
@@ -104,20 +143,22 @@ struct Location
struct RegExp
{
- enum Flags {
+ enum Flags : unsigned int {
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
RegExp_Multiline = 0x04
};
- quint32 flags;
- quint32 stringIndex;
+ union {
+ QJsonPrivate::qle_bitfield<0, 4> flags;
+ QJsonPrivate::qle_bitfield<4, 28> stringIndex;
+ };
- static int calculateSize() { return sizeof(RegExp); }
+ RegExp() { flags = 0; stringIndex = 0; }
};
struct Lookup
{
- enum Type {
+ enum Type : unsigned int {
Type_Getter = 0x0,
Type_Setter = 0x1,
Type_GlobalGetter = 2,
@@ -125,21 +166,27 @@ struct Lookup
Type_IndexedSetter = 4
};
- quint32 type_and_flags;
- quint32 nameIndex;
+ union {
+ QJsonPrivate::qle_bitfield<0, 4> type_and_flags;
+ QJsonPrivate::qle_bitfield<4, 28> nameIndex;
+ };
- static int calculateSize() { return sizeof(Lookup); }
+ Lookup() { type_and_flags = 0; nameIndex = 0; }
};
struct JSClassMember
{
- uint nameOffset : 31;
- uint isAccessor : 1;
+ union {
+ QJsonPrivate::qle_bitfield<0, 31> nameOffset;
+ QJsonPrivate::qle_bitfield<31, 1> isAccessor;
+ };
+
+ JSClassMember() { nameOffset = 0; isAccessor = 0; }
};
struct JSClass
{
- uint nMembers;
+ LEUInt32 nMembers;
// JSClassMember[nMembers]
static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; }
@@ -147,8 +194,7 @@ struct JSClass
struct String
{
- quint32 flags; // isArrayIndex
- qint32 size;
+ LEInt32 size;
// uint16 strdata[]
static int calculateSize(const QString &str) {
@@ -156,9 +202,11 @@ struct String
}
};
+// 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.
struct Function
{
- enum Flags {
+ enum Flags : unsigned int {
HasDirectEval = 0x1,
UsesArgumentsObject = 0x2,
IsStrict = 0x4,
@@ -166,24 +214,26 @@ struct Function
HasCatchOrWith = 0x10
};
- quint32 index; // in CompilationUnit's function table
- quint32 nameIndex;
- qint64 flags;
- quint32 nFormals;
- quint32 formalsOffset;
- quint32 nLocals;
- quint32 localsOffset;
- quint32 nInnerFunctions;
- quint32 innerFunctionsOffset;
+ // 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;
Location location;
// Qml Extensions Begin
- quint32 nDependingIdObjects;
- quint32 dependingIdObjectsOffset; // Array of resolved ID objects
- quint32 nDependingContextProperties;
- quint32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index)
- quint32 nDependingScopeProperties;
- quint32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
+ 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)
// Qml Extensions End
// quint32 formalsIndex[nFormals]
@@ -191,11 +241,19 @@ struct Function
// quint32 offsetForInnerFunctions[nInnerFunctions]
// Function[nInnerFunctions]
- const quint32 *formalsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + formalsOffset); }
- const quint32 *localsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + localsOffset); }
- const quint32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); }
- const quint32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); }
- const quint32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); }
+ // Keep all unaligned data at the end
+ quint8 flags;
+
+ 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); }
+
+ // --- QQmlPropertyCacheCreator interface
+ const LEUInt32 *formalsBegin() const { return formalsTable(); }
+ const LEUInt32 *formalsEnd() const { return formalsTable() + nFormals; }
+ // ---
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
@@ -207,15 +265,15 @@ struct Function
// Qml data structures
struct Q_QML_EXPORT TranslationData {
- quint32 commentIndex;
- int number;
+ LEUInt32 commentIndex;
+ LEInt32 number;
};
struct Q_QML_PRIVATE_EXPORT Binding
{
- quint32 propertyNameIndex;
+ LEUInt32 propertyNameIndex;
- enum ValueType {
+ enum ValueType : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
@@ -228,26 +286,30 @@ struct Q_QML_PRIVATE_EXPORT Binding
Type_GroupProperty
};
- enum Flags {
+ enum Flags : unsigned int {
IsSignalHandlerExpression = 0x1,
IsSignalHandlerObject = 0x2,
IsOnAssignment = 0x4,
InitializerForReadOnlyDeclaration = 0x8,
IsResolvedEnum = 0x10,
IsListItem = 0x20,
- IsBindingToAlias = 0x40
+ IsBindingToAlias = 0x40,
+ IsDeferredBinding = 0x80,
+ IsCustomParserBinding = 0x100,
};
- quint32 flags : 16;
- quint32 type : 16;
+ union {
+ QJsonPrivate::qle_bitfield<0, 16> flags;
+ QJsonPrivate::qle_bitfield<16, 16> type;
+ };
union {
bool b;
- double d;
- quint32 compiledScriptIndex; // used when Type_Script
- quint32 objectIndex;
+ quint64 doubleValue; // do not access directly, needs endian protected access
+ LEUInt32 compiledScriptIndex; // used when Type_Script
+ LEUInt32 objectIndex;
TranslationData translationData; // used when Type_Translation
} value;
- quint32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings)
+ LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings)
Location location;
Location valueLocation;
@@ -307,11 +369,20 @@ struct Q_QML_PRIVATE_EXPORT Binding
QString valueAsScriptString(const Unit *unit) const;
double valueAsNumber() const
{
- if (type == Type_Number)
- return value.d;
- return 0.0;
-
+ 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);
+ }
+
bool valueAsBoolean() const
{
if (type == Type_Boolean)
@@ -323,17 +394,16 @@ struct Q_QML_PRIVATE_EXPORT Binding
struct Parameter
{
- quint32 nameIndex;
- quint32 type;
- quint32 customTypeNameIndex;
- quint32 reserved;
+ LEUInt32 nameIndex;
+ LEUInt32 type;
+ LEUInt32 customTypeNameIndex;
Location location;
};
struct Signal
{
- quint32 nameIndex;
- quint32 nParameters;
+ LEUInt32 nameIndex;
+ LEUInt32 nParameters;
Location location;
// Parameter parameters[1];
@@ -346,47 +416,95 @@ struct Signal
+ nParameters * sizeof(Parameter)
+ 7) & ~0x7;
}
+
+ // --- QQmlPropertyCacheCceatorInterface
+ const Parameter *parametersBegin() const { return parameterAt(0); }
+ const Parameter *parametersEnd() const { return parameterAt(nParameters); }
+ int parameterCount() const { return nParameters; }
+ // ---
};
struct Property
{
- enum Type { Var = 0, Variant, Int, Bool, Real, String, Url, Color,
+ enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color,
Font, Time, Date, DateTime, Rect, Point, Size,
Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion,
- Alias, Custom, CustomList };
+ Custom, CustomList };
- enum Flags {
+ enum Flags : unsigned int {
IsReadOnly = 0x1
};
- quint32 nameIndex;
- quint32 type;
+ LEUInt32 nameIndex;
union {
- quint32 customTypeNameIndex; // If type >= Custom
- quint32 aliasIdValueIndex; // If type == Alias
+ QJsonPrivate::qle_bitfield<0, 31> type;
+ QJsonPrivate::qle_bitfield<31, 1> flags; // readonly
};
- quint32 aliasPropertyValueIndex;
- quint32 flags; // readonly
+ LEUInt32 customTypeNameIndex; // If type >= Custom
Location location;
- Location aliasLocation; // If type == Alias
+};
+
+struct Alias {
+ enum Flags : unsigned int {
+ IsReadOnly = 0x1,
+ Resolved = 0x2,
+ AliasPointsToPointerObject = 0x4
+ };
+ union {
+ QJsonPrivate::qle_bitfield<0, 29> nameIndex;
+ QJsonPrivate::qle_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;
+ };
+ union {
+ LEUInt32 propertyNameIndex; // string index
+ LEInt32 encodedMetaPropertyIndex;
+ LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
+ };
+ Location location;
+ Location referenceLocation;
+
+ bool isObjectAlias() const {
+ Q_ASSERT(flags & Resolved);
+ return encodedMetaPropertyIndex == -1;
+ }
};
struct Object
{
+ enum Flags : unsigned int {
+ NoFlag = 0x0,
+ IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
+ HasDeferredBindings = 0x2, // any of the bindings are deferred
+ HasCustomParserBindings = 0x4
+ };
+
// 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.
- quint32 inheritedTypeNameIndex;
- quint32 idIndex;
- qint32 indexOfDefaultProperty; // -1 means no default property declared in this object
- quint32 nFunctions;
- quint32 offsetToFunctions;
- quint32 nProperties;
- quint32 offsetToProperties;
- quint32 nSignals;
- quint32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
- quint32 nBindings;
- quint32 offsetToBindings;
+ LEUInt32 inheritedTypeNameIndex;
+ LEUInt32 idNameIndex;
+ union {
+ QJsonPrivate::qle_bitfield<0, 15> flags;
+ QJsonPrivate::qle_bitfield<15, 1> defaultPropertyIsAlias;
+ QJsonPrivate::qle_signedbitfield<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;
Location location;
Location locationOfIdProperty;
// Function[]
@@ -394,20 +512,22 @@ struct Object
// Signal[]
// Binding[]
- static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nSignals, int nBindings)
+ static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings, int nNamedObjectsInComponent)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
+ nProperties * sizeof(Property)
+ + nAliases * sizeof(Alias)
+ nSignals * sizeof(quint32)
+ nBindings * sizeof(Binding)
+ + nNamedObjectsInComponent * sizeof(int)
+ 0x7
) & ~0x7;
}
- const quint32 *functionOffsetTable() const
+ const LEUInt32 *functionOffsetTable() const
{
- return reinterpret_cast<const quint32*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
+ return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
}
const Property *propertyTable() const
@@ -415,6 +535,11 @@ struct Object
return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties);
}
+ const Alias *aliasTable() const
+ {
+ return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases);
+ }
+
const Binding *bindingTable() const
{
return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings);
@@ -422,78 +547,116 @@ struct Object
const Signal *signalAt(int idx) const
{
- const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
+ const LEUInt32 offset = offsetTable[idx];
return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset);
}
+
+ const LEUInt32 *namedObjectsInComponentTable() const
+ {
+ return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
+ }
+
+ // --- QQmlPropertyCacheCreator interface
+ int propertyCount() const { return nProperties; }
+ int aliasCount() const { return nAliases; }
+ int signalCount() const { return nSignals; }
+ int functionCount() const { return nFunctions; }
+
+ const Binding *bindingsBegin() const { return bindingTable(); }
+ const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
+
+ const Property *propertiesBegin() const { return propertyTable(); }
+ const Property *propertiesEnd() const { return propertyTable() + nProperties; }
+
+ const Alias *aliasesBegin() const { return aliasTable(); }
+ const Alias *aliasesEnd() const { return aliasTable() + nAliases; }
+
+ typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator;
+ SignalIterator signalsBegin() const { return SignalIterator(this, 0); }
+ SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); }
+
+ int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
+ // ---
};
struct Import
{
- enum ImportType {
+ enum ImportType : unsigned int {
ImportLibrary = 0x1,
ImportFile = 0x2,
ImportScript = 0x3
};
- quint32 type;
+ LEUInt32 type;
- quint32 uriIndex;
- quint32 qualifierIndex;
+ LEUInt32 uriIndex;
+ LEUInt32 qualifierIndex;
- qint32 majorVersion;
- qint32 minorVersion;
+ LEInt32 majorVersion;
+ LEInt32 minorVersion;
Location location;
- Import(): type(0), uriIndex(0), qualifierIndex(0), majorVersion(0), minorVersion(0) {}
+ Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; }
};
static const char magic_str[] = "qv4cdata";
struct Unit
{
+ // DO NOT CHANGE THESE FIELDS EVER
char magic[8];
- qint16 architecture;
- qint16 version;
- quint32 unitSize; // Size of the Unit and any depending data.
+ LEUInt32 version;
+ LEUInt32 qtVersion;
+ LEInt64 sourceTimeStamp;
+ LEUInt32 unitSize; // Size of the Unit and any depending data.
+ // END DO NOT CHANGE THESE FIELDS EVER
- enum {
+ 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?
+ IsSharedLibrary = 0x10, // .pragma shared?
+ ContainsMachineCode = 0x20 // used to determine if we need to mmap with execute permissions
};
- quint32 flags;
- uint stringTableSize;
- uint offsetToStringTable;
- uint functionTableSize;
- uint offsetToFunctionTable;
- uint lookupTableSize;
- uint offsetToLookupTable;
- uint regexpTableSize;
- uint offsetToRegexpTable;
- uint constantTableSize;
- uint offsetToConstantTable;
- uint jsClassTableSize;
- uint offsetToJSClassTable;
- qint32 indexOfRootFunction;
- quint32 sourceFileIndex;
+ 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;
/* QML specific fields */
- quint32 nImports;
- quint32 offsetToImports;
- quint32 nObjects;
- quint32 offsetToObjects;
- quint32 indexOfRootObject;
+ 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 uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
- const uint offset = offsetTable[idx];
+ 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);
}
@@ -503,22 +666,33 @@ struct Unit
/* end QML specific fields*/
QString stringAt(int idx) const {
- const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
+ const LEUInt32 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
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
- if (flags & StaticData)
- return QString::fromRawData(characters, str->size);
+ // 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);
+ QString qstr(str->size, Qt::Uninitialized);
+ QChar *ch = qstr.data();
+ for (int i = 0; i < str->size; ++i)
+ ch[i] = QChar(characters[i]);
+ return qstr;
+#endif
}
- const uint *functionOffsetTable() const { return reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const LEUInt32 *functionOffsetTable() const { return reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
const Function *functionAt(int idx) const {
- const uint *offsetTable = functionOffsetTable();
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = functionOffsetTable();
+ const LEUInt32 offset = offsetTable[idx];
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
@@ -526,27 +700,18 @@ struct Unit
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
}
- const QV4::Value *constants() const {
- return reinterpret_cast<const QV4::Value*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
+ const LEUInt64 *constants() const {
+ return reinterpret_cast<const LEUInt64*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
}
const JSClassMember *jsClassAt(int idx, int *nMembers) const {
- const uint *offsetTable = reinterpret_cast<const uint *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
+ const LEUInt32 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));
}
-
- static int calculateSize(uint nFunctions, uint nRegExps, uint nConstants,
- uint nLookups, uint nClasses) {
- return (sizeof(Unit)
- + (nFunctions + nClasses) * sizeof(uint)
- + nRegExps * RegExp::calculateSize()
- + nConstants * sizeof(QV4::ReturnedValue)
- + nLookups * Lookup::calculateSize()
- + 7) & ~7; }
};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
@@ -565,7 +730,7 @@ struct TypeReference
bool errorWhenNotFound: 1;
};
-// map from name index to location of first use
+// Map from name index to location of first use.
struct TypeReferenceMap : QHash<int, TypeReference>
{
TypeReference &add(int nameIndex, const Location &loc) {
@@ -574,7 +739,75 @@ struct TypeReferenceMap : QHash<int, TypeReference>
return *it;
return *insert(nameIndex, loc);
}
+
+ template <typename CompiledObject>
+ void collectFromObject(const CompiledObject *obj)
+ {
+ if (obj->inheritedTypeNameIndex != 0) {
+ TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location);
+ r.needsCreation = true;
+ r.errorWhenNotFound = true;
+ }
+
+ for (auto prop = obj->propertiesBegin(), propEnd = obj->propertiesEnd(); 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);
+ r.errorWhenNotFound = true;
+ }
+ }
+
+ for (auto binding = obj->bindingsBegin(), bindingEnd = obj->bindingsEnd(); binding != bindingEnd; ++binding) {
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+ this->add(binding->propertyNameIndex, binding->location);
+ }
+ }
+
+ template <typename Iterator>
+ void collectFromObjects(Iterator it, Iterator end)
+ {
+ for (; it != end; ++it)
+ collectFromObject(*it);
+ }
+};
+
+#ifndef V4_BOOTSTRAP
+struct ResolvedTypeReference
+{
+ ResolvedTypeReference()
+ : type(0)
+ , majorVersion(0)
+ , minorVersion(0)
+ , isFullyDynamicType(false)
+ {}
+
+ QQmlType *type;
+ QQmlRefPointer<QQmlPropertyCache> typePropertyCache;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+
+ int majorVersion;
+ int minorVersion;
+ // Types such as QQmlPropertyMap can add properties dynamically at run-time and
+ // therefore cannot have a property cache installed when instantiated.
+ bool isFullyDynamicType;
+
+ QQmlPropertyCache *propertyCache() const;
+ QQmlPropertyCache *createPropertyCache(QQmlEngine *);
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
+
+ void doDynamicTypeCheck();
};
+// map from name index
+// While this could be a hash, a map is chosen here to provide a stable
+// order, which is used to calculating a check-sum on dependent meta-objects.
+struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*>
+{
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const;
+};
+#else
+struct ResolvedTypeReferenceMap {};
+#endif
// index is per-object binding index
typedef QVector<QQmlPropertyData*> BindingPropertyData;
@@ -597,7 +830,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
virtual ~CompilationUnit();
#endif
- Unit *data;
+ const Unit *data;
// 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);
@@ -614,20 +847,81 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
QVector<QV4::Function *> runtimeFunctions;
mutable QQmlNullableValue<QUrl> m_url;
+ // QML specific fields
+ QQmlPropertyCacheVector propertyCaches;
+ QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); }
+
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
+
// index is object index. This allows fast access to the
// property data when initializing bindings, avoiding expensive
// lookups by string (property name).
QVector<BindingPropertyData> bindingPropertyDataPerObject;
+ // 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);
+
+ // pointers either to data->constants() or little-endian memory copy.
+ const Value* constants;
+
+ void finalize(QQmlEnginePrivate *engine);
+
+ 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
+
+ QVector<QQmlScriptData *> dependentScripts;
+ ResolvedTypeReferenceMap resolvedTypes;
+
+ bool verifyChecksum(QQmlEngine *engine,
+ const ResolvedTypeReferenceMap &dependentTypes) const;
+
+ int metaTypeId;
+ int listMetaTypeId;
+ bool isRegisteredWithEngine;
+
+ QScopedPointer<CompilationUnitMapper> backingFile;
+
+ // --- 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); }
+
+ struct FunctionIterator
+ {
+ FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {}
+ const Unit *unit;
+ const Object *object;
+ int index;
+
+ const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); }
+ void operator++() { ++index; }
+ bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
+ bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
+ };
+ FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); }
+ FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); }
+ // ---
+
QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
void unlink();
- virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int /*functionIndex*/) { return 0; }
-
void markObjects(QV4::ExecutionEngine *e);
+ void destroy() Q_DECL_OVERRIDE;
+
+ bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+ bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString);
+
protected:
virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
+ virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit);
+ virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString);
+ virtual bool memoryMapCode(QString *errorString);
#endif // V4_BOOTSTRAP
};
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 3943642146..e1ea3a9b88 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -42,6 +42,9 @@
#include <qv4isel_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4alloca_p.h>
+#include <wtf/MathExtras.h>
+#include <QCryptographicHash>
QV4::Compiler::StringTableGenerator::StringTableGenerator()
{
@@ -75,16 +78,21 @@ void QV4::Compiler::StringTableGenerator::clear()
void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
{
char *dataStart = reinterpret_cast<char *>(unit);
- uint *stringTable = reinterpret_cast<uint *>(dataStart + unit->offsetToStringTable);
+ 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;
const QString &qstr = strings.at(i);
- QV4::CompiledData::String *s = (QV4::CompiledData::String*)(stringData);
- s->flags = 0; // ###
+ QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData);
s->size = qstr.length();
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort));
+#else
+ ushort *uc = reinterpret_cast<ushort *>(s + 1);
+ for (int i = 0; i < qstr.length(); ++i)
+ uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode());
+#endif
stringData += QV4::CompiledData::String::calculateSize(qstr);
}
@@ -92,7 +100,6 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module)
: irModule(module)
- , jsClassDataSize(0)
{
// Make sure the empty string always gets index 0
registerString(QString());
@@ -174,30 +181,32 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg
{
// ### re-use existing class definitions.
- QList<CompiledData::JSClassMember> members;
- members.reserve(count);
+ const int size = CompiledData::JSClass::calculateSize(count);
+ jsClassOffsets.append(jsClassData.size());
+ const int oldSize = jsClassData.size();
+ jsClassData.resize(jsClassData.size() + size);
+ memset(jsClassData.data() + oldSize, 0, size);
- IR::ExprList *it = args;
- for (int i = 0; i < count; ++i, it = it->next) {
- CompiledData::JSClassMember member;
+ CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
+ jsClass->nMembers = count;
+ 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;
- members << member;
+ member->nameOffset = registerString(*name->id);
+ member->isAccessor = !isData;
if (!isData)
it = it->next;
}
- jsClasses << members;
- jsClassDataSize += CompiledData::JSClass::calculateSize(members.count());
- return jsClasses.size() - 1;
+ return jsClassOffsets.size() - 1;
}
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
@@ -211,110 +220,70 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
registerString(*f->locals.at(i));
}
- int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(),
- constants.size(), lookups.size(), jsClasses.count());
-
- uint functionDataSize = 0;
- for (int i = 0; i < irModule->functions.size(); ++i) {
- QV4::IR::Function *f = irModule->functions.at(i);
- functionOffsets.insert(f, functionDataSize + unitSize);
-
- const int qmlIdDepsCount = f->idObjectDependencies.count();
- const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
- functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount);
+ CompiledData::LEUInt32 *functionOffsets = reinterpret_cast<CompiledData::LEUInt32*>(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32)));
+ uint jsClassDataOffset = 0;
+
+ char *dataPtr;
+ CompiledData::Unit *unit;
+ {
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
+ memset(dataPtr, 0, tempHeader.unitSize);
+ memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
+ memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- const int totalSize = unitSize + functionDataSize + jsClassDataSize + (option == GenerateWithStringTable ? stringTable.sizeOfTableAndData() : 0);
- char *data = (char *)malloc(totalSize);
- memset(data, 0, totalSize);
- QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data;
-
- memcpy(unit->magic, QV4::CompiledData::magic_str, sizeof(unit->magic));
- unit->architecture = 0; // ###
- unit->flags = QV4::CompiledData::Unit::IsJavascript;
- unit->version = 1;
- unit->unitSize = totalSize;
- unit->functionTableSize = irModule->functions.size();
- unit->offsetToFunctionTable = sizeof(*unit);
- unit->lookupTableSize = lookups.count();
- unit->offsetToLookupTable = unit->offsetToFunctionTable + unit->functionTableSize * sizeof(uint);
- unit->regexpTableSize = regexps.size();
- unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize();
- unit->constantTableSize = constants.size();
- unit->offsetToConstantTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize();
- unit->jsClassTableSize = jsClasses.count();
- unit->offsetToJSClassTable = unit->offsetToConstantTable + unit->constantTableSize * sizeof(ReturnedValue);
- if (option == GenerateWithStringTable) {
- unit->stringTableSize = stringTable.stringCount();
- unit->offsetToStringTable = unitSize + functionDataSize + jsClassDataSize;
- } else {
- unit->stringTableSize = 0;
- unit->offsetToStringTable = 0;
- }
- unit->indexOfRootFunction = -1;
- unit->sourceFileIndex = getStringId(irModule->fileName);
- unit->nImports = 0;
- unit->offsetToImports = 0;
- unit->nObjects = 0;
- unit->offsetToObjects = 0;
- unit->indexOfRootObject = 0;
-
- uint *functionTable = (uint *)(data + unit->offsetToFunctionTable);
- for (int i = 0; i < irModule->functions.size(); ++i)
- functionTable[i] = functionOffsets.value(irModule->functions.at(i));
-
- char *f = data + unitSize;
+ memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32));
+
for (int i = 0; i < irModule->functions.size(); ++i) {
QV4::IR::Function *function = irModule->functions.at(i);
if (function == irModule->rootFunction)
unit->indexOfRootFunction = i;
- const int bytes = writeFunction(f, i, function);
- f += bytes;
+ writeFunction(dataPtr + functionOffsets[i], function);
}
- CompiledData::Lookup *lookupsToWrite = (CompiledData::Lookup*)(data + unit->offsetToLookupTable);
+ CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
foreach (const CompiledData::Lookup &l, lookups)
*lookupsToWrite++ = l;
- CompiledData::RegExp *regexpTable = (CompiledData::RegExp *)(data + unit->offsetToRegexpTable);
+ CompiledData::RegExp *regexpTable = reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable);
memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable));
- ReturnedValue *constantTable = (ReturnedValue *)(data + unit->offsetToConstantTable);
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable);
memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue));
-
- // write js classes and js class lookup table
- uint *jsClassTable = (uint*)(data + unit->offsetToJSClassTable);
- char *jsClass = data + unitSize + functionDataSize;
- for (int i = 0; i < jsClasses.count(); ++i) {
- jsClassTable[i] = jsClass - data;
-
- const QList<CompiledData::JSClassMember> members = jsClasses.at(i);
-
- CompiledData::JSClass *c = reinterpret_cast<CompiledData::JSClass*>(jsClass);
- c->nMembers = members.count();
-
- CompiledData::JSClassMember *memberToWrite = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + sizeof(CompiledData::JSClass));
- foreach (const CompiledData::JSClassMember &member, members)
- *memberToWrite++ = member;
-
- jsClass += CompiledData::JSClass::calculateSize(members.count());
+#else
+ CompiledData::LEUInt64 *constantTable = reinterpret_cast<CompiledData::LEUInt64 *>(dataPtr + unit->offsetToConstantTable);
+ for (int i = 0; i < constants.count(); ++i)
+ constantTable[i] = constants.at(i);
+#endif
+
+ {
+ 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);
+ for (int i = 0; i < jsClassOffsets.count(); ++i)
+ jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
}
// write strings and string table
if (option == GenerateWithStringTable)
stringTable.serialize(unit);
+ unit->generateChecksum();
+
return unit;
}
-int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::Function *irFunction)
+void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
quint32 currentOffset = sizeof(QV4::CompiledData::Function);
+ currentOffset = (currentOffset + 7) & ~quint32(0x7);
- function->index = index;
function->nameIndex = getStringId(*irFunction->name);
function->flags = 0;
if (irFunction->hasDirectEval)
@@ -336,8 +305,6 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
currentOffset += function->nLocals * sizeof(quint32);
function->nInnerFunctions = irFunction->nestedFunctions.size();
- function->innerFunctionsOffset = currentOffset;
- currentOffset += function->nInnerFunctions * sizeof(quint32);
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -364,6 +331,9 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
function->location.line = irFunction->line;
function->location.column = irFunction->column;
+ function->codeOffset = 0;
+ function->codeSize = 0;
+
// write formals
quint32 *formals = (quint32 *)(f + function->formalsOffset);
for (int i = 0; i < irFunction->formals.size(); ++i)
@@ -374,15 +344,12 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
for (int i = 0; i < irFunction->locals.size(); ++i)
locals[i] = getStringId(*irFunction->locals.at(i));
- // write inner functions
- quint32 *innerFunctions = (quint32 *)(f + function->innerFunctionsOffset);
- for (int i = 0; i < irFunction->nestedFunctions.size(); ++i)
- innerFunctions[i] = functionOffsets.value(irFunction->nestedFunctions.at(i));
-
// write QML dependencies
quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset);
- foreach (int id, irFunction->idObjectDependencies)
- *writtenDeps++ = id;
+ for (int id : irFunction->idObjectDependencies) {
+ Q_ASSERT(id >= 0);
+ *writtenDeps++ = static_cast<quint32>(id);
+ }
writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset);
for (auto property : irFunction->contextObjectPropertyDependencies) {
@@ -395,7 +362,77 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, 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.version = QV4_DATA_STRUCTURE_VERSION;
+ unit.qtVersion = QT_VERSION;
+ memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
+ unit.architectureIndex = registerString(QSysInfo::buildAbi());
+ unit.codeGeneratorIndex = registerString(codeGeneratorName);
+ memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
+
+ quint32 nextOffset = sizeof(CompiledData::Unit);
+
+ unit.functionTableSize = irModule->functions.size();
+ unit.offsetToFunctionTable = nextOffset;
+ nextOffset += unit.functionTableSize * sizeof(uint);
+
+ unit.lookupTableSize = lookups.count();
+ unit.offsetToLookupTable = nextOffset;
+ nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
+
+ unit.regexpTableSize = regexps.size();
+ unit.offsetToRegexpTable = nextOffset;
+ nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp);
+
+ unit.constantTableSize = constants.size();
+
+ // Ensure we load constants from well-aligned addresses into for example SSE registers.
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset));
+ unit.offsetToConstantTable = nextOffset;
+ nextOffset += unit.constantTableSize * sizeof(ReturnedValue);
+
+ unit.jsClassTableSize = jsClassOffsets.count();
+ unit.offsetToJSClassTable = nextOffset;
+ nextOffset += unit.jsClassTableSize * sizeof(uint);
+
+ *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;
+
+ 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);
+ }
- return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions,
- function->nDependingIdObjects, function->nDependingContextProperties + function->nDependingScopeProperties);
+ if (option == GenerateWithStringTable) {
+ unit.stringTableSize = stringTable.stringCount();
+ unit.offsetToStringTable = nextOffset;
+ nextOffset += stringTable.sizeOfTableAndData();
+ } else {
+ unit.stringTableSize = 0;
+ unit.offsetToStringTable = 0;
+ }
+ unit.indexOfRootFunction = -1;
+ unit.sourceFileIndex = getStringId(irModule->fileName);
+ unit.sourceTimeStamp = irModule->sourceTimeStamp;
+ unit.nImports = 0;
+ unit.offsetToImports = 0;
+ unit.nObjects = 0;
+ unit.offsetToObjects = 0;
+ unit.indexOfRootObject = 0;
+
+ unit.unitSize = nextOffset;
+
+ return unit;
}
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 0321a83b4f..49b8664513 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -52,6 +52,7 @@
#include <QtCore/qstring.h>
#include "qv4jsir_p.h"
+#include <private/qjson_p.h>
QT_BEGIN_NAMESPACE
@@ -114,18 +115,20 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
// Returns bytes written
- int writeFunction(char *f, int index, IR::Function *irFunction);
+ void writeFunction(char *f, IR::Function *irFunction) const;
StringTableGenerator stringTable;
+ QString codeGeneratorName;
private:
+ CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset);
+
IR::Module *irModule;
- QHash<IR::Function *, uint> functionOffsets;
QList<CompiledData::Lookup> lookups;
QVector<CompiledData::RegExp> regexps;
QVector<ReturnedValue> constants;
- QList<QList<CompiledData::JSClassMember> > jsClasses;
- uint jsClassDataSize;
+ QByteArray jsClassData;
+ QVector<int> jsClassOffsets;
};
}
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 90010ccf52..ca4e0b73d4 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -50,18 +50,26 @@
//
// We mean it.
//
-
-#include <QtCore/qglobal.h>
+#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4function_p.h>
#include <private/qv4runtime_p.h>
+QT_REQUIRE_CONFIG(qml_interpreter);
+
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 FOR_EACH_MOTH_INSTR(F) \
F(Ret, ret) \
- F(Line, line) \
- F(Debug, debug) \
+ MOTH_DEBUG_INSTR(F) \
F(LoadRuntimeString, loadRuntimeString) \
F(LoadRegExp, loadRegExp) \
F(LoadClosure, loadClosure) \
@@ -162,7 +170,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
#ifdef MOTH_THREADED_INTERPRETER
-# define MOTH_INSTR_HEADER void *code;
+# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; };
#else
# define MOTH_INSTR_HEADER quint32 instructionType;
#endif
@@ -174,6 +182,8 @@ QT_BEGIN_NAMESPACE
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
@@ -243,6 +253,7 @@ union Instr
{
enum Type {
FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
+ LastInstruction
};
struct instr_common {
@@ -252,6 +263,8 @@ union Instr
MOTH_INSTR_HEADER
Param result;
};
+
+#ifndef QT_NO_QML_DEBUGGING
struct instr_line {
MOTH_INSTR_HEADER
qint32 lineNumber;
@@ -260,6 +273,8 @@ union Instr
MOTH_INSTR_HEADER
qint32 lineNumber;
};
+#endif // QT_NO_QML_DEBUGGING
+
struct instr_loadRuntimeString {
MOTH_INSTR_HEADER
int stringId;
@@ -322,12 +337,14 @@ union Instr
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
@@ -672,7 +689,7 @@ union Instr
};
struct instr_binop {
MOTH_INSTR_HEADER
- QV4::Runtime::BinaryOperation alu;
+ uint alu; // offset inside the runtime methods
Param lhs;
Param rhs;
Param result;
@@ -757,7 +774,7 @@ union Instr
};
struct instr_binopContext {
MOTH_INSTR_HEADER
- QV4::Runtime::BinaryOperationContext alu;
+ uint alu; // offset inside the runtime methods
Param lhs;
Param rhs;
Param result;
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index e84b9c6ec9..ca6319ef3c 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -46,6 +46,7 @@
#include <private/qv4regexpobject_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmlengine_p.h>
+#include <wtf/MathExtras.h>
#undef USE_TYPE_INFO
@@ -54,7 +55,7 @@ using namespace QV4::Moth;
namespace {
-inline QV4::Runtime::BinaryOperation aluOpFunction(IR::AluOp op)
+inline uint aluOpFunction(IR::AluOp op)
{
switch (op) {
case IR::OpInvalid:
@@ -70,43 +71,43 @@ inline QV4::Runtime::BinaryOperation aluOpFunction(IR::AluOp op)
case IR::OpCompl:
return 0;
case IR::OpBitAnd:
- return QV4::Runtime::bitAnd;
+ return offsetof(QV4::Runtime, bitAnd);
case IR::OpBitOr:
- return QV4::Runtime::bitOr;
+ return offsetof(QV4::Runtime, bitOr);
case IR::OpBitXor:
- return QV4::Runtime::bitXor;
+ return offsetof(QV4::Runtime, bitXor);
case IR::OpAdd:
return 0;
case IR::OpSub:
- return QV4::Runtime::sub;
+ return offsetof(QV4::Runtime, sub);
case IR::OpMul:
- return QV4::Runtime::mul;
+ return offsetof(QV4::Runtime, mul);
case IR::OpDiv:
- return QV4::Runtime::div;
+ return offsetof(QV4::Runtime, div);
case IR::OpMod:
- return QV4::Runtime::mod;
+ return offsetof(QV4::Runtime, mod);
case IR::OpLShift:
- return QV4::Runtime::shl;
+ return offsetof(QV4::Runtime, shl);
case IR::OpRShift:
- return QV4::Runtime::shr;
+ return offsetof(QV4::Runtime, shr);
case IR::OpURShift:
- return QV4::Runtime::ushr;
+ return offsetof(QV4::Runtime, ushr);
case IR::OpGt:
- return QV4::Runtime::greaterThan;
+ return offsetof(QV4::Runtime, greaterThan);
case IR::OpLt:
- return QV4::Runtime::lessThan;
+ return offsetof(QV4::Runtime, lessThan);
case IR::OpGe:
- return QV4::Runtime::greaterEqual;
+ return offsetof(QV4::Runtime, greaterEqual);
case IR::OpLe:
- return QV4::Runtime::lessEqual;
+ return offsetof(QV4::Runtime, lessEqual);
case IR::OpEqual:
- return QV4::Runtime::equal;
+ return offsetof(QV4::Runtime, equal);
case IR::OpNotEqual:
- return QV4::Runtime::notEqual;
+ return offsetof(QV4::Runtime, notEqual);
case IR::OpStrictEqual:
- return QV4::Runtime::strictEqual;
+ return offsetof(QV4::Runtime, strictEqual);
case IR::OpStrictNotEqual:
- return QV4::Runtime::strictNotEqual;
+ return offsetof(QV4::Runtime, strictNotEqual);
case IR::OpInstanceof:
return 0;
case IR::OpIn:
@@ -151,8 +152,8 @@ inline bool isBoolType(IR::Expr *e)
} // anonymous namespace
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- : EvalInstructionSelection(execAllocator, module, jsGenerator)
+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)
@@ -250,6 +251,7 @@ void InstructionSelection::run(int functionIndex)
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;
@@ -259,10 +261,11 @@ void InstructionSelection::run(int functionIndex)
line.lineNumber = currentLine;
addInstruction(line);
}
+#endif
}
}
- s->accept(this);
+ visit(s);
}
}
@@ -576,18 +579,20 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target
addInstruction(store);
}
-void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target)
+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) {
@@ -879,11 +884,11 @@ Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR
if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
Instruction::BinopContext binop;
if (oper == IR::OpInstanceof)
- binop.alu = QV4::Runtime::instanceof;
+ binop.alu = offsetof(QV4::Runtime, instanceof);
else if (oper == IR::OpIn)
- binop.alu = QV4::Runtime::in;
+ binop.alu = offsetof(QV4::Runtime, in);
else
- binop.alu = QV4::Runtime::add;
+ binop.alu = offsetof(QV4::Runtime, add);
binop.lhs = getParam(leftSource);
binop.rhs = getParam(rightSource);
binop.result = getResultParam(target);
@@ -930,6 +935,17 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint
}
}
+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)
@@ -937,11 +953,7 @@ void InstructionSelection::visitJump(IR::Jump *s)
if (_removableJumps.contains(s))
return;
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
+ addDebugInstruction();
Instruction::Jump jump;
jump.offset = 0;
@@ -952,11 +964,7 @@ void InstructionSelection::visitJump(IR::Jump *s)
void InstructionSelection::visitCJump(IR::CJump *s)
{
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
+ addDebugInstruction();
Param condition;
if (IR::Temp *t = s->cond->asTemp()) {
@@ -991,12 +999,8 @@ void InstructionSelection::visitCJump(IR::CJump *s)
void InstructionSelection::visitRet(IR::Ret *s)
{
- if (blockNeedsDebugInstruction) {
- // this is required so stepOut will always be guaranteed to stop in every stack frame
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
+ // this is required so stepOut will always be guaranteed to stop in every stack frame
+ addDebugInstruction();
Instruction::Ret ret;
ret.result = getParam(s->expr);
@@ -1332,12 +1336,7 @@ void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject()
ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
{
-
-#ifdef MOTH_THREADED_INTERPRETER
- instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)];
-#else
instr.common.instructionType = type;
-#endif
int instructionSize = Instr::size(type);
if (_codeEnd - _codeNext < instructionSize) {
@@ -1423,6 +1422,29 @@ CompilationUnit::~CompilationUnit()
void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
{
+#ifdef MOTH_THREADED_INTERPRETER
+ // link byte code against addresses of instructions
+ for (int i = 0; i < codeRefs.count(); ++i) {
+ QByteArray &codeRef = codeRefs[i];
+ char *code = codeRef.data();
+ int index = 0;
+ while (index < codeRef.size()) {
+ Instr *genericInstr = reinterpret_cast<Instr *>(code + index);
+
+ switch (genericInstr->common.instructionType) {
+#define LINK_INSTRUCTION(InstructionType, Member) \
+ case Instr::InstructionType: \
+ genericInstr->common.code = VME::instructionJumpTable()[static_cast<int>(genericInstr->common.instructionType)]; \
+ index += InstrMeta<(int)Instr::InstructionType>::Size; \
+ break;
+
+ FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION)
+
+ }
+ }
+ }
+#endif
+
runtimeFunctions.resize(data->functionTableSize);
runtimeFunctions.fill(0);
for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
@@ -1433,3 +1455,113 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
runtimeFunctions[i] = runtimeFunction;
}
}
+
+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;
+
+#ifdef MOTH_THREADED_INTERPRETER
+ // Map from instruction label back to instruction type. Only needed when persisting
+ // already linked compilation units;
+ QHash<void*, int> reverseInstructionMapping;
+ if (engine) {
+ void **instructions = VME::instructionJumpTable();
+ for (int i = 0; i < Instr::LastInstruction; ++i)
+ reverseInstructionMapping.insert(instructions[i], i);
+ }
+#endif
+
+ 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);
+
+#ifdef MOTH_THREADED_INTERPRETER
+ if (!reverseInstructionMapping.isEmpty()) {
+ char *codePtr = code.data(); // detaches
+ int index = 0;
+ while (index < code.size()) {
+ Instr *genericInstr = reinterpret_cast<Instr *>(codePtr + index);
+
+ genericInstr->common.instructionType = reverseInstructionMapping.value(genericInstr->common.code);
+
+ switch (genericInstr->common.instructionType) {
+ #define REVERSE_INSTRUCTION(InstructionType, Member) \
+ case Instr::InstructionType: \
+ index += InstrMeta<(int)Instr::InstructionType>::Size; \
+ break;
+
+ FOR_EACH_MOTH_INSTR(REVERSE_INSTRUCTION)
+ }
+ }
+ }
+#endif
+
+ written = device->write(code.constData(), compiledFunction->codeSize);
+ if (written != qint64(compiledFunction->codeSize)) {
+ *errorString = device->errorString();
+ return false;
+ }
+ }
+ return true;
+}
+
+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;
+}
+
+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
index 29d117af38..74323a2912 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -58,6 +58,8 @@
#include <private/qv4value_p.h>
#include "qv4instr_moth_p.h"
+QT_REQUIRE_CONFIG(qml_interpreter);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -66,7 +68,10 @@ namespace Moth {
struct CompilationUnit : public QV4::CompiledData::CompilationUnit
{
virtual ~CompilationUnit();
- virtual void linkBackendToEngine(QV4::ExecutionEngine *engine);
+ void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE;
+ void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE;
+ bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE;
+ bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE;
QVector<QByteArray> codeRefs;
@@ -77,7 +82,7 @@ class Q_QML_EXPORT InstructionSelection:
public EvalInstructionSelection
{
public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
~InstructionSelection();
virtual void run(int functionIndex);
@@ -134,7 +139,7 @@ protected:
virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName);
virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex);
virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex);
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target);
+ virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target);
virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target);
virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target);
virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex);
@@ -174,6 +179,8 @@ private:
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;
@@ -202,11 +209,14 @@ private:
class Q_QML_EXPORT ISelFactory: public EvalISelFactory
{
public:
+ ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {}
virtual ~ISelFactory() {}
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
- virtual bool jitCompileRegexps() const
+ 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>
diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp
index 0ae08160ab..efcfb9bd77 100644
--- a/src/qml/compiler/qv4isel_p.cpp
+++ b/src/qml/compiler/qv4isel_p.cpp
@@ -52,7 +52,7 @@
using namespace QV4;
using namespace QV4::IR;
-EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
+EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
: useFastLookups(true)
, useTypeInference(true)
, executableAllocator(execAllocator)
@@ -67,6 +67,7 @@ EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *exe
Q_ASSERT(execAllocator);
#endif
Q_ASSERT(module);
+ jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName;
}
EvalInstructionSelection::~EvalInstructionSelection()
@@ -146,24 +147,24 @@ void IRDecoder::visitMove(IR::Move *s)
const int attachedPropertiesId = m->attachedPropertiesId;
const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject;
- if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) {
+ if (_function && attachedPropertiesId == 0 && !m->property->isConstant() && _function->isQmlBinding) {
if (m->kind == IR::Member::MemberOfQmlContextObject) {
- _function->contextObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex);
+ _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);
+ _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, s->target);
+ 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);
+ 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, s->target);
+ 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);
@@ -186,7 +187,7 @@ void IRDecoder::visitMove(IR::Move *s)
#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);
+ callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target);
return;
}
#endif
@@ -215,10 +216,10 @@ void IRDecoder::visitMove(IR::Move *s)
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);
+ setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex());
return;
}
- setQObjectProperty(s->source, m->base, m->property->coreIndex);
+ setQObjectProperty(s->source, m->base, m->property->coreIndex());
#endif
return;
} else {
@@ -262,7 +263,7 @@ void IRDecoder::visitExp(IR::Exp *s)
#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);
+ callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0);
return;
}
#endif
@@ -294,7 +295,7 @@ void IRDecoder::callBuiltin(IR::Call *call, Expr *result)
if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
callBuiltinTypeofQmlContextProperty(member->base,
IR::Member::MemberKind(member->kind),
- member->property->coreIndex, result);
+ member->property->coreIndex(), result);
return;
}
#endif
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
index 88d2071c52..037c02e5ea 100644
--- a/src/qml/compiler/qv4isel_p.h
+++ b/src/qml/compiler/qv4isel_p.h
@@ -65,13 +65,14 @@ 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);
+ EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
virtual ~EvalInstructionSelection() = 0;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true);
@@ -104,23 +105,44 @@ protected:
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: protected IR::StmtVisitor
+class Q_QML_PRIVATE_EXPORT IRDecoder
{
public:
IRDecoder() : _function(0) {}
virtual ~IRDecoder() = 0;
- virtual void visitPhi(IR::Phi *) {}
-
-public: // visitor methods for StmtVisitor:
- virtual void visitMove(IR::Move *s);
- virtual void visitExp(IR::Exp *s);
+ 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;
@@ -166,7 +188,7 @@ public: // to implement by subclasses:
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, 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;
@@ -178,6 +200,11 @@ public: // to implement by subclasses:
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
diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h
index 674fc01623..1755193d32 100644
--- a/src/qml/compiler/qv4isel_util_p.h
+++ b/src/qml/compiler/qv4isel_util_p.h
@@ -104,7 +104,7 @@ inline Primitive convertToValue(IR::Const *c)
return Primitive::undefinedValue();
}
-class ConvertTemps: protected IR::StmtVisitor, protected IR::ExprVisitor
+class ConvertTemps
{
void renumber(IR::Temp *t)
{
@@ -132,7 +132,7 @@ protected:
virtual void process(IR::Stmt *s)
{
- s->accept(this);
+ visit(s);
}
public:
@@ -157,34 +157,28 @@ public:
}
protected:
- virtual void visitConst(IR::Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(IR::Name *) {}
- virtual void visitTemp(IR::Temp *e) { renumber(e); }
- virtual void visitArgLocal(IR::ArgLocal *) {}
- virtual void visitClosure(IR::Closure *) {}
- virtual void visitConvert(IR::Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(IR::Call *e) {
- e->base->accept(this);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
+ 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 visitNew(IR::New *e) {
- e->base->accept(this);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
+
+ 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);
+ }
}
- virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(IR::Member *e) { e->base->accept(this); }
- virtual void visitExp(IR::Exp *s) { s->expr->accept(this); }
- virtual void visitMove(IR::Move *s) { s->target->accept(this); s->source->accept(this); }
- virtual void visitJump(IR::Jump *) {}
- virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); }
- virtual void visitRet(IR::Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(IR::Phi *) { Q_UNREACHABLE(); }
};
} // namespace QV4
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
index b28db59190..5687834b00 100644
--- a/src/qml/compiler/qv4jsir.cpp
+++ b/src/qml/compiler/qv4jsir.cpp
@@ -157,12 +157,13 @@ AluOp binaryOperator(int op)
}
}
-struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
+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)
@@ -176,11 +177,12 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
clone.setBasicBlock(block);
for (Stmt *s : block->statements()) {
- s->accept(this);
+ visit(s);
}
}
}
+private:
template <typename Expr_>
Expr_ *cleanup(Expr_ *expr)
{
@@ -189,7 +191,7 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
subexpressions.insert(it, expr);
IR::Expr *e = expr;
qSwap(uniqueExpr, e);
- expr->accept(this);
+ visit(expr);
qSwap(uniqueExpr, e);
return static_cast<Expr_ *>(e);
}
@@ -199,83 +201,45 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
return clone(expr);
}
- // statements
- virtual void visitExp(Exp *s)
+ void visit(Stmt *s)
{
- s->expr = cleanup(s->expr);
- }
-
- virtual void visitMove(Move *s)
- {
- s->target = cleanup(s->target);
- s->source = cleanup(s->source);
- }
-
- virtual void visitJump(Jump *)
- {
- // nothing to do for Jump statements
- }
-
- virtual void visitCJump(CJump *s)
- {
- s->cond = cleanup(s->cond);
- }
-
- virtual void visitRet(Ret *s)
- {
- s->expr = cleanup(s->expr);
- }
-
- virtual void visitPhi(IR::Phi *) { Q_UNIMPLEMENTED(); }
-
- // expressions
- virtual void visitConst(Const *) {}
- virtual void visitString(String *) {}
- virtual void visitRegExp(RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
-
- virtual void visitConvert(Convert *e)
- {
- e->expr = cleanup(e->expr);
- }
-
- virtual void visitUnop(Unop *e)
- {
- e->expr = cleanup(e->expr);
- }
-
- virtual void visitBinop(Binop *e)
- {
- e->left = cleanup(e->left);
- e->right = cleanup(e->right);
- }
-
- virtual void visitCall(Call *e)
- {
- e->base = cleanup(e->base);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr = cleanup(it->expr);
- }
-
- virtual void visitNew(New *e)
- {
- e->base = cleanup(e->base);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr = cleanup(it->expr);
- }
-
- virtual void visitSubscript(Subscript *e)
- {
- e->base = cleanup(e->base);
- e->index = cleanup(e->index);
+ 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);
+ }
}
- virtual void visitMember(Member *e)
+ void visit(Expr *e)
{
- e->base = cleanup(e->base);
+ 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);
+ }
}
};
@@ -404,9 +368,10 @@ Function::Function(Module *module, Function *outer, const QString &name)
, isNamedExpression(false)
, hasTry(false)
, hasWith(false)
+ , isQmlBinding(false)
, unused(0)
- , line(-1)
- , column(-1)
+ , line(0)
+ , column(0)
, _allBasicBlocks(0)
, _statementCount(0)
{
@@ -548,75 +513,39 @@ ExprList *CloneExpr::clone(ExprList *list)
return clonedList;
}
-void CloneExpr::visitConst(Const *e)
-{
- cloned = cloneConst(e, block->function);
-}
-
-void CloneExpr::visitString(String *e)
-{
- cloned = block->STRING(e->value);
-}
-
-void CloneExpr::visitRegExp(RegExp *e)
-{
- cloned = block->REGEXP(e->value, e->flags);
-}
-
-void CloneExpr::visitName(Name *e)
-{
- cloned = cloneName(e, block->function);
-}
-
-void CloneExpr::visitTemp(Temp *e)
-{
- cloned = cloneTemp(e, block->function);
-}
-
-void CloneExpr::visitArgLocal(ArgLocal *e)
-{
- cloned = cloneArgLocal(e, block->function);
-}
-
-void CloneExpr::visitClosure(Closure *e)
-{
- cloned = block->CLOSURE(e->value);
-}
-
-void CloneExpr::visitConvert(Convert *e)
-{
- cloned = block->CONVERT(clone(e->expr), e->type);
-}
-
-void CloneExpr::visitUnop(Unop *e)
-{
- cloned = block->UNOP(e->op, clone(e->expr));
-}
-
-void CloneExpr::visitBinop(Binop *e)
-{
- cloned = block->BINOP(e->op, clone(e->left), clone(e->right));
-}
-
-void CloneExpr::visitCall(Call *e)
-{
- cloned = block->CALL(clone(e->base), clone(e->args));
-}
-
-void CloneExpr::visitNew(New *e)
-{
- cloned = block->NEW(clone(e->base), clone(e->args));
-}
-
-void CloneExpr::visitSubscript(Subscript *e)
-{
- cloned = block->SUBSCRIPT(clone(e->base), clone(e->index));
-}
-
-void CloneExpr::visitMember(Member *e)
-{
- Expr *clonedBase = clone(e->base);
- cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->idIndex);
+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)
@@ -632,17 +561,17 @@ IRPrinter::~IRPrinter()
void IRPrinter::print(Stmt *s)
{
- s->accept(this);
+ visit(s);
}
void IRPrinter::print(const Expr &e)
{
- const_cast<Expr *>(&e)->accept(this);
+ visit(const_cast<Expr *>(&e));
}
void IRPrinter::print(Expr *e)
{
- e->accept(this);
+ visit(e);
}
void IRPrinter::print(Function *f)
@@ -696,7 +625,7 @@ void IRPrinter::print(BasicBlock *bb)
QTextStream *prevOut = &os;
std::swap(out, prevOut);
addStmtNr(s);
- s->accept(this);
+ visit(s);
if (s->location.startLine) {
out->flush();
for (int i = 58 - str.length(); i > 0; --i)
@@ -713,10 +642,29 @@ void IRPrinter::print(BasicBlock *bb)
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 ";
- s->expr->accept(this);
+ visit(s->expr);
}
void IRPrinter::visitMove(Move *s)
@@ -725,13 +673,13 @@ void IRPrinter::visitMove(Move *s)
if (!s->swap && targetTemp->type != UnknownType)
*out << typeName(targetTemp->type) << ' ';
- s->target->accept(this);
+ visit(s->target);
*out << ' ';
if (s->swap)
*out << "<=> ";
else
*out << "= ";
- s->source->accept(this);
+ visit(s->source);
}
void IRPrinter::visitJump(Jump *s)
@@ -742,7 +690,7 @@ void IRPrinter::visitJump(Jump *s)
void IRPrinter::visitCJump(CJump *s)
{
*out << "if ";
- s->cond->accept(this);
+ visit(s->cond);
*out << " goto L" << s->iftrue->index()
<< " else goto L" << s->iffalse->index();
}
@@ -752,7 +700,7 @@ void IRPrinter::visitRet(Ret *s)
*out << "return";
if (s->expr) {
*out << ' ';
- s->expr->accept(this);
+ visit(s->expr);
}
}
@@ -761,7 +709,7 @@ void IRPrinter::visitPhi(Phi *s)
if (s->targetTemp->type != UnknownType)
*out << typeName(s->targetTemp->type) << ' ';
- s->targetTemp->accept(this);
+ visit(s->targetTemp);
*out << " = phi ";
for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
if (i > 0)
@@ -769,7 +717,42 @@ void IRPrinter::visitPhi(Phi *s)
if (currentBB)
*out << 'L' << currentBB->in.at(i)->index() << ": ";
if (s->incoming[i])
- s->incoming[i]->accept(this);
+ 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();
}
}
@@ -867,32 +850,32 @@ void IRPrinter::visitClosure(Closure *e)
void IRPrinter::visitConvert(Convert *e)
{
*out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' ';
- e->expr->accept(this);
+ visit(e->expr);
}
void IRPrinter::visitUnop(Unop *e)
{
*out << opname(e->op) << ' ';
- e->expr->accept(this);
+ visit(e->expr);
}
void IRPrinter::visitBinop(Binop *e)
{
*out << opname(e->op) << ' ';
- e->left->accept(this);
+ visit(e->left);
*out << ", ";
- e->right->accept(this);
+ visit(e->right);
}
void IRPrinter::visitCall(Call *e)
{
*out << "call ";
- e->base->accept(this);
+ visit(e->base);
*out << '(';
for (ExprList *it = e->args; it; it = it->next) {
if (it != e->args)
*out << ", ";
- it->expr->accept(this);
+ visit(it->expr);
}
*out << ')';
}
@@ -900,21 +883,21 @@ void IRPrinter::visitCall(Call *e)
void IRPrinter::visitNew(New *e)
{
*out << "new ";
- e->base->accept(this);
+ visit(e->base);
*out << '(';
for (ExprList *it = e->args; it; it = it->next) {
if (it != e->args)
*out << ", ";
- it->expr->accept(this);
+ visit(it->expr);
}
*out << ')';
}
void IRPrinter::visitSubscript(Subscript *e)
{
- e->base->accept(this);
+ visit(e->base);
*out << '[';
- e->index->accept(this);
+ visit(e->index);
*out << ']';
}
@@ -924,12 +907,12 @@ void IRPrinter::visitMember(Member *e)
&& e->attachedPropertiesId != 0 && !e->base->asTemp())
*out << "[[attached property from " << e->attachedPropertiesId << "]]";
else
- e->base->accept(this);
+ visit(e->base);
*out << '.' << *e->name;
#ifndef V4_BOOTSTRAP
if (e->property)
- *out << " (meta-property " << e->property->coreIndex
- << " <" << QMetaType::typeName(e->property->propType)
+ *out << " (meta-property " << e->property->coreIndex()
+ << " <" << QMetaType::typeName(e->property->propType())
<< ">)";
else if (e->kind == Member::MemberOfIdObjectsArray)
*out << "(id object " << e->idIndex << ")";
@@ -942,15 +925,15 @@ QString IRPrinter::escape(const QString &s)
for (int i = 0; i < s.length(); ++i) {
const QChar ch = s.at(i);
if (ch == QLatin1Char('\n'))
- r += QStringLiteral("\\n");
+ r += QLatin1String("\\n");
else if (ch == QLatin1Char('\r'))
- r += QStringLiteral("\\r");
+ r += QLatin1String("\\r");
else if (ch == QLatin1Char('\\'))
- r += QStringLiteral("\\\\");
+ r += QLatin1String("\\\\");
else if (ch == QLatin1Char('"'))
- r += QStringLiteral("\\\"");
+ r += QLatin1String("\\\"");
else if (ch == QLatin1Char('\''))
- r += QStringLiteral("\\'");
+ r += QLatin1String("\\'");
else
r += ch;
}
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
index 94fa65cf71..73aa6c4975 100644
--- a/src/qml/compiler/qv4jsir_p.h
+++ b/src/qml/compiler/qv4jsir_p.h
@@ -192,7 +192,7 @@ enum AluOp {
AluOp binaryOperator(int op);
const char *opname(IR::AluOp op);
-enum Type {
+enum Type : quint16 {
UnknownType = 0,
MissingType = 1 << 0,
@@ -217,34 +217,6 @@ inline bool strictlyEqualTypes(Type t1, Type t2)
QString typeName(Type t);
-struct ExprVisitor {
- virtual ~ExprVisitor() {}
- virtual void visitConst(Const *) = 0;
- virtual void visitString(String *) = 0;
- virtual void visitRegExp(RegExp *) = 0;
- virtual void visitName(Name *) = 0;
- virtual void visitTemp(Temp *) = 0;
- virtual void visitArgLocal(ArgLocal *) = 0;
- virtual void visitClosure(Closure *) = 0;
- virtual void visitConvert(Convert *) = 0;
- virtual void visitUnop(Unop *) = 0;
- virtual void visitBinop(Binop *) = 0;
- virtual void visitCall(Call *) = 0;
- virtual void visitNew(New *) = 0;
- virtual void visitSubscript(Subscript *) = 0;
- virtual void visitMember(Member *) = 0;
-};
-
-struct StmtVisitor {
- virtual ~StmtVisitor() {}
- virtual void visitExp(Exp *) = 0;
- virtual void visitMove(Move *) = 0;
- virtual void visitJump(Jump *) = 0;
- virtual void visitCJump(CJump *) = 0;
- virtual void visitRet(Ret *) = 0;
- virtual void visitPhi(Phi *) = 0;
-};
-
struct MemberExpressionResolver;
struct DiscoveredType {
@@ -288,28 +260,129 @@ struct MemberExpressionResolver
};
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);
+ }
- Expr(): type(UnknownType) {}
- virtual ~Expr() {}
- virtual void accept(ExprVisitor *) = 0;
- virtual bool isLValue() { return false; }
- virtual Const *asConst() { return 0; }
- virtual String *asString() { return 0; }
- virtual RegExp *asRegExp() { return 0; }
- virtual Name *asName() { return 0; }
- virtual Temp *asTemp() { return 0; }
- virtual ArgLocal *asArgLocal() { return 0; }
- virtual Closure *asClosure() { return 0; }
- virtual Convert *asConvert() { return 0; }
- virtual Unop *asUnop() { return 0; }
- virtual Binop *asBinop() { return 0; }
- virtual Call *asCall() { return 0; }
- virtual New *asNew() { return 0; }
- virtual Subscript *asSubscript() { return 0; }
- virtual Member *asMember() { return 0; }
+ 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() { return as<Const>(); }
+ String *asString() { return as<String>(); }
+ RegExp *asRegExp() { return as<RegExp>(); }
+ Name *asName() { return as<Name>(); }
+ Temp *asTemp() { return as<Temp>(); }
+ ArgLocal *asArgLocal() { return as<ArgLocal>(); }
+ Closure *asClosure() { return as<Closure>(); }
+ Convert *asConvert() { return as<Convert>(); }
+ Unop *asUnop() { return as<Unop>(); }
+ Binop *asBinop() { return as<Binop>(); }
+ Call *asCall() { return as<Call>(); }
+ New *asNew() { return as<New>(); }
+ Subscript *asSubscript() { return as<Subscript>(); }
+ Member *asMember() { return as<Member>(); }
};
+#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;
@@ -326,26 +399,28 @@ struct ExprList {
struct Const: Expr {
double value;
+ Const(): Expr(ConstExpr) {}
+
void init(Type type, double value)
{
this->type = type;
this->value = value;
}
- virtual void accept(ExprVisitor *v) { v->visitConst(this); }
- virtual Const *asConst() { return this; }
+ 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;
}
- virtual void accept(ExprVisitor *v) { v->visitString(this); }
- virtual String *asString() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == StringExpr; }
};
struct RegExp: Expr {
@@ -359,14 +434,15 @@ struct RegExp: Expr {
const QString *value;
int flags;
+ RegExp(): Expr(RegExpExpr) {}
+
void init(const QString *value, int flags)
{
this->value = value;
this->flags = flags;
}
- virtual void accept(ExprVisitor *v) { v->visitRegExp(this); }
- virtual RegExp *asRegExp() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; }
};
struct Name: Expr {
@@ -399,13 +475,13 @@ struct Name: Expr {
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);
- virtual void accept(ExprVisitor *v) { v->visitName(this); }
- virtual bool isLValue() { return true; }
- virtual Name *asName() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == NameExpr; }
};
struct Q_AUTOTEST_EXPORT Temp: Expr {
@@ -424,7 +500,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr {
MemberExpressionResolver *memberResolver;
Temp()
- : index((1 << 28) - 1)
+ : Expr(TempExpr)
+ , index((1 << 28) - 1)
, isReadOnly(0)
, kind(Invalid)
, memberResolver(0)
@@ -438,9 +515,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr {
}
bool isInvalid() const { return kind == Invalid; }
- virtual void accept(ExprVisitor *v) { v->visitTemp(this); }
- virtual bool isLValue() { return !isReadOnly; }
- virtual Temp *asTemp() { return this; }
+
+ static bool classof(const Expr *c) { return c->exprKind == TempExpr; }
};
inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
@@ -479,44 +555,48 @@ struct Q_AUTOTEST_EXPORT ArgLocal: Expr {
this->isArgumentsOrEval = false;
}
- virtual void accept(ExprVisitor *v) { v->visitArgLocal(this); }
- virtual bool isLValue() { return true; }
- virtual ArgLocal *asArgLocal() { return this; }
+ 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;
}
- virtual void accept(ExprVisitor *v) { v->visitClosure(this); }
- virtual Closure *asClosure() { return this; }
+ 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;
}
- virtual void accept(ExprVisitor *v) { v->visitConvert(this); }
- virtual Convert *asConvert() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; }
};
struct Unop: Expr {
- AluOp op;
Expr *expr;
+ AluOp op;
+
+ Unop(): Expr(UnopExpr) {}
void init(AluOp op, Expr *expr)
{
@@ -524,14 +604,15 @@ struct Unop: Expr {
this->expr = expr;
}
- virtual void accept(ExprVisitor *v) { v->visitUnop(this); }
- virtual Unop *asUnop() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == UnopExpr; }
};
struct Binop: Expr {
- AluOp op;
Expr *left; // Temp or Const
Expr *right; // Temp or Const
+ AluOp op;
+
+ Binop(): Expr(BinopExpr) {}
void init(AluOp op, Expr *left, Expr *right)
{
@@ -540,14 +621,15 @@ struct Binop: Expr {
this->right = right;
}
- virtual void accept(ExprVisitor *v) { v->visitBinop(this); }
- virtual Binop *asBinop() { return this; }
+ 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;
@@ -560,14 +642,15 @@ struct Call: Expr {
return 0;
}
- virtual void accept(ExprVisitor *v) { v->visitCall(this); }
- virtual Call *asCall() { return this; }
+ 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;
@@ -580,23 +663,22 @@ struct New: Expr {
return 0;
}
- virtual void accept(ExprVisitor *v) { v->visitNew(this); }
- virtual New *asNew() { return this; }
+ 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;
}
- virtual void accept(ExprVisitor *v) { v->visitSubscript(this); }
- virtual bool isLValue() { return true; }
- virtual Subscript *asSubscript() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; }
};
struct Member: Expr {
@@ -628,6 +710,8 @@ struct Member: Expr {
uchar kind: 3; // MemberKind
+ Member(): Expr(MemberExpr) {}
+
void setEnumValue(int value) {
kind = MemberOfEnum;
enumValue = value;
@@ -649,35 +733,52 @@ struct Member: Expr {
this->kind = kind;
}
- virtual void accept(ExprVisitor *v) { v->visitMember(this); }
- virtual bool isLValue() { return true; }
- virtual Member *asMember() { return this; }
+ 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): _id(id) {}
+ explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {}
- virtual ~Stmt()
- {
-#ifdef Q_CC_MSVC
- // MSVC complains about potential memory leaks if a destructor never returns.
-#else
- Q_UNREACHABLE();
-#endif
- }
- virtual Stmt *asTerminator() { return 0; }
+ Stmt *asTerminator();
- virtual void accept(StmtVisitor *) = 0;
- virtual Exp *asExp() { return 0; }
- virtual Move *asMove() { return 0; }
- virtual Jump *asJump() { return 0; }
- virtual CJump *asCJump() { return 0; }
- virtual Ret *asRet() { return 0; }
- virtual Phi *asPhi() { return 0; }
+ Exp *asExp() { return as<Exp>(); }
+ Move *asMove() { return as<Move>(); }
+ Jump *asJump() { return as<Jump>(); }
+ CJump *asCJump() { return as<CJump>(); }
+ Ret *asRet() { return as<Ret>(); }
+ Phi *asPhi() { return as<Phi>(); }
int id() const { return _id; }
@@ -687,21 +788,52 @@ private: // For memory management in 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) {}
+ Exp(int id): Stmt(id, ExpStmt) {}
void init(Expr *expr)
{
this->expr = expr;
}
- virtual void accept(StmtVisitor *v) { v->visitExp(this); }
- virtual Exp *asExp() { return this; }
-
+ static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; }
};
struct Move: Stmt {
@@ -709,7 +841,7 @@ struct Move: Stmt {
Expr *source;
bool swap;
- Move(int id): Stmt(id) {}
+ Move(int id): Stmt(id, MoveStmt) {}
void init(Expr *target, Expr *source)
{
@@ -718,25 +850,20 @@ struct Move: Stmt {
this->swap = false;
}
- virtual void accept(StmtVisitor *v) { v->visitMove(this); }
- virtual Move *asMove() { return this; }
-
+ static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; }
};
struct Jump: Stmt {
BasicBlock *target;
- Jump(int id): Stmt(id) {}
+ Jump(int id): Stmt(id, JumpStmt) {}
void init(BasicBlock *target)
{
this->target = target;
}
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitJump(this); }
- virtual Jump *asJump() { return this; }
+ static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; }
};
struct CJump: Stmt {
@@ -745,7 +872,7 @@ struct CJump: Stmt {
BasicBlock *iffalse;
BasicBlock *parent;
- CJump(int id): Stmt(id) {}
+ CJump(int id): Stmt(id, CJumpStmt) {}
void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent)
{
@@ -755,26 +882,20 @@ struct CJump: Stmt {
this->parent = parent;
}
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitCJump(this); }
- virtual CJump *asCJump() { return this; }
+ static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; }
};
struct Ret: Stmt {
Expr *expr;
- Ret(int id): Stmt(id) {}
+ Ret(int id): Stmt(id, RetStmt) {}
void init(Expr *expr)
{
this->expr = expr;
}
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitRet(this); }
- virtual Ret *asRet() { return this; }
+ 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
@@ -785,30 +906,54 @@ struct Phi: Stmt {
Temp *targetTemp;
VarLengthArray<Expr *, 4> incoming;
- Phi(int id): Stmt(id) {}
+ Phi(int id): Stmt(id, PhiStmt) {}
- virtual void accept(StmtVisitor *v) { v->visitPhi(this); }
- virtual Phi *asPhi() { return this; }
+ 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;
+ qint64 sourceTimeStamp;
bool isQmlModule; // implies rootFunction is always 0
+ uint unitFlags; // flags merged into CompiledData::Unit::flags
+#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)
+ , sourceTimeStamp(0)
, isQmlModule(false)
+ , unitFlags(0)
+#ifndef QT_NO_QML_DEBUGGER
, debugMode(debugMode)
{}
+#else
+ { Q_UNUSED(debugMode); }
+#endif
~Module();
void setFileName(const QString &name);
@@ -1060,6 +1205,20 @@ private:
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
{
@@ -1125,14 +1284,15 @@ struct Function {
uint isNamedExpression : 1;
uint hasTry: 1;
uint hasWith: 1;
- uint unused : 25;
+ uint isQmlBinding: 1;
+ uint unused : 24;
- // Location of declaration in source code (-1 if not specified)
- int line;
- int column;
+ // Location of declaration in source code (0 if not specified)
+ uint line;
+ uint column;
// Qml extension:
- QSet<int> idObjectDependencies;
+ SmallSet<int> idObjectDependencies;
PropertyDependencyMap contextObjectPropertyDependencies;
PropertyDependencyMap scopeObjectPropertyDependencies;
@@ -1192,7 +1352,7 @@ private:
int _statementCount;
};
-class CloneExpr: protected IR::ExprVisitor
+class CloneExpr
{
public:
explicit CloneExpr(IR::BasicBlock *block = 0);
@@ -1210,7 +1370,7 @@ public:
{
Expr *c = expr;
qSwap(cloned, c);
- expr->accept(this);
+ visit(expr);
qSwap(cloned, c);
return static_cast<ExprSubclass *>(c);
}
@@ -1253,23 +1413,10 @@ public:
return newArgLocal;
}
-protected:
+private:
IR::ExprList *clone(IR::ExprList *list);
- virtual void visitConst(Const *);
- virtual void visitString(String *);
- virtual void visitRegExp(RegExp *);
- virtual void visitName(Name *);
- virtual void visitTemp(Temp *);
- virtual void visitArgLocal(ArgLocal *);
- virtual void visitClosure(Closure *);
- virtual void visitConvert(Convert *);
- virtual void visitUnop(Unop *);
- virtual void visitBinop(Binop *);
- virtual void visitCall(Call *);
- virtual void visitNew(New *);
- virtual void visitSubscript(Subscript *);
- virtual void visitMember(Member *);
+ void visit(Expr *e);
protected:
IR::BasicBlock *block;
@@ -1278,7 +1425,7 @@ private:
IR::Expr *cloned;
};
-class Q_AUTOTEST_EXPORT IRPrinter: public StmtVisitor, public ExprVisitor
+class Q_AUTOTEST_EXPORT IRPrinter
{
public:
IRPrinter(QTextStream *out);
@@ -1291,6 +1438,7 @@ public:
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);
@@ -1298,6 +1446,7 @@ public:
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);
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index f021e1f760..4111cc77db 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -642,7 +642,7 @@ public:
qout << from;
else
qout << "(none)";
- qout << " -> " << to->index() << endl;
+ qout << " dominates " << to->index() << endl;
}
qDebug("%s", buf.data().constData());
}
@@ -740,6 +740,21 @@ public:
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)
@@ -898,7 +913,7 @@ private:
}
};
-class VariableCollector: public StmtVisitor, ExprVisitor {
+class VariableCollector {
std::vector<Temp> _allTemps;
std::vector<BasicBlockSet> _defsites;
std::vector<std::vector<int> > A_orig;
@@ -946,7 +961,7 @@ public:
currentBB = bb;
killed.assign(function->tempCount, false);
for (Stmt *s : bb->statements())
- s->accept(this);
+ visit(s);
}
}
@@ -971,62 +986,45 @@ public:
return nonLocals.at(var.index);
}
-protected:
- virtual void visitPhi(Phi *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
-
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { e->base->accept(this); }
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
-
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitMove(Move *s) {
- s->source->accept(this);
+private:
+ void visit(Stmt *s)
+ {
+ if (s->asPhi()) {
+ // nothing to do
+ } else if (auto move = s->asMove()) {
+ visit(move->source);
- if (Temp *t = s->target->asTemp()) {
- addTemp(t);
+ if (Temp *t = move->target->asTemp()) {
+ addTemp(t);
- if (isCollectable(t)) {
- _defsites[t->index].insert(currentBB);
- addDefInCurrentBlock(t);
+ if (isCollectable(t)) {
+ _defsites[t->index].insert(currentBB);
+ addDefInCurrentBlock(t);
- // For semi-pruned SSA:
- killed.setBit(t->index);
+ // For semi-pruned SSA:
+ killed.setBit(t->index);
+ }
+ } else {
+ visit(move->target);
}
} else {
- s->target->accept(this);
+ STMT_VISIT_ALL_KINDS(s)
}
}
- virtual void visitTemp(Temp *t)
+ void visit(Expr *e)
{
- addTemp(t);
+ if (auto t = e->asTemp()) {
+ addTemp(t);
- if (isCollectable(t))
- if (!killed.at(t->index))
- nonLocals.setBit(t->index);
+ if (isCollectable(t)) {
+ if (!killed.at(t->index)) {
+ nonLocals.setBit(t->index);
+ }
+ }
+ } else {
+ EXPR_VISIT_ALL_KINDS(e);
+ }
}
};
@@ -1192,6 +1190,15 @@ public:
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());
@@ -1345,7 +1352,7 @@ void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) {
//
// Undo(t, c) =
// mapping[t] = c
-class VariableRenamer: public StmtVisitor, public ExprVisitor
+class VariableRenamer
{
Q_DISABLE_COPY(VariableRenamer)
@@ -1466,7 +1473,7 @@ private:
for (Stmt *s : bb->statements()) {
currentStmt = s;
- s->accept(this);
+ visit(s);
}
for (BasicBlock *Y : bb->out) {
@@ -1532,23 +1539,35 @@ private:
return newIndex;
}
-protected:
- virtual void visitTemp(Temp *e) { // only called for uses, not defs
-// qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top();
- e->index = currentNumber(*e);
- e->kind = Temp::VirtualRegister;
- defUses.addUse(*e, currentStmt);
- }
+private:
+ void visit(Stmt *s)
+ {
+ if (auto move = s->asMove()) {
+ // uses:
+ visit(move->source);
- virtual void visitMove(Move *s) {
- // uses:
- s->source->accept(this);
+ // 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);
+ }
+ }
- // defs:
- if (Temp *t = s->target->asTemp())
- renameTemp(t);
- else
- s->target->accept(this);
+ 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
@@ -1558,44 +1577,6 @@ protected:
t->index = newIdx;
defUses.addDef(t, currentStmt, currentBB);
}
-
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); }
-
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
-
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
-
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitSubscript(Subscript *e) {
- e->base->accept(this);
- e->index->accept(this);
- }
-
- virtual void visitMember(Member *e) {
- e->base->accept(this);
- }
};
// This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm,
@@ -1922,7 +1903,7 @@ private:
}
};
-class SideEffectsChecker: public ExprVisitor
+class SideEffectsChecker
{
bool _sideEffect;
@@ -1931,11 +1912,14 @@ public:
: _sideEffect(false)
{}
+ ~SideEffectsChecker()
+ {}
+
bool hasSideEffects(Expr *expr)
{
bool sideEffect = false;
qSwap(_sideEffect, sideEffect);
- expr->accept(this);
+ visit(expr);
qSwap(_sideEffect, sideEffect);
return sideEffect;
}
@@ -1948,12 +1932,35 @@ protected:
bool seenSideEffects() const { return _sideEffect; }
-protected:
- void visitConst(Const *) Q_DECL_OVERRIDE {}
- void visitString(IR::String *) Q_DECL_OVERRIDE {}
- void visitRegExp(IR::RegExp *) Q_DECL_OVERRIDE {}
+ 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);
+ }
+ }
- void visitName(Name *e) Q_DECL_OVERRIDE {
+ 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
@@ -1962,15 +1969,12 @@ protected:
markAsSideEffect();
}
- void visitTemp(Temp *) Q_DECL_OVERRIDE {}
- void visitArgLocal(ArgLocal *) Q_DECL_OVERRIDE {}
-
- void visitClosure(Closure *) Q_DECL_OVERRIDE {
+ void visitClosure(Closure *) {
markAsSideEffect();
}
- void visitConvert(Convert *e) Q_DECL_OVERRIDE {
- e->expr->accept(this);
+ void visitConvert(Convert *e) {
+ visit(e->expr);
switch (e->expr->type) {
case QObjectType:
@@ -1983,8 +1987,8 @@ protected:
}
}
- void visitUnop(Unop *e) Q_DECL_OVERRIDE {
- e->expr->accept(this);
+ void visitUnop(Unop *e) {
+ visit(e->expr);
switch (e->op) {
case OpUPlus:
@@ -2001,7 +2005,7 @@ protected:
}
}
- void visitBinop(Binop *e) Q_DECL_OVERRIDE {
+ 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.
@@ -2013,30 +2017,30 @@ protected:
markAsSideEffect();
}
- void visitSubscript(Subscript *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
- e->index->accept(this);
+ void visitSubscript(Subscript *e) {
+ visit(e->base);
+ visit(e->index);
markAsSideEffect();
}
- void visitMember(Member *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
+ void visitMember(Member *e) {
+ visit(e->base);
if (e->freeOfSideEffects)
return;
markAsSideEffect();
}
- void visitCall(Call *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
+ void visitCall(Call *e) {
+ visit(e->base);
for (ExprList *args = e->args; args; args = args->next)
- args->expr->accept(this);
+ visit(args->expr);
markAsSideEffect(); // TODO: there are built-in functions that have no side effect.
}
- void visitNew(New *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
+ void visitNew(New *e) {
+ visit(e->base);
for (ExprList *args = e->args; args; args = args->next)
- args->expr->accept(this);
+ visit(args->expr);
markAsSideEffect(); // TODO: there are built-in types that have no side effect.
}
};
@@ -2045,21 +2049,19 @@ class EliminateDeadCode: public SideEffectsChecker
{
DefUses &_defUses;
StatementWorklist &_worklist;
- QVector<Temp *> _collectedTemps;
+ QVarLengthArray<Temp *, 8> _collectedTemps;
public:
EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist)
: _defUses(defUses)
, _worklist(worklist)
- {
- _collectedTemps.reserve(8);
- }
+ {}
void run(Expr *&expr, Stmt *stmt) {
_collectedTemps.clear();
if (!hasSideEffects(expr)) {
expr = 0;
- foreach (Temp *t, _collectedTemps) {
+ for (Temp *t : _collectedTemps) {
_defUses.removeUse(stmt, *t);
_worklist += _defUses.defStmt(*t);
}
@@ -2067,13 +2069,13 @@ public:
}
protected:
- void visitTemp(Temp *e) Q_DECL_OVERRIDE
+ void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL
{
_collectedTemps.append(e);
}
};
-class PropagateTempTypes: public StmtVisitor, ExprVisitor
+class PropagateTempTypes
{
const DefUses &defUses;
UntypedTemp theTemp;
@@ -2089,64 +2091,31 @@ public:
newType = type;
theTemp = temp;
if (Stmt *defStmt = defUses.defStmt(temp.temp))
- defStmt->accept(this);
+ visit(defStmt);
foreach (Stmt *use, defUses.uses(temp.temp))
- use->accept(this);
- }
-
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *e) {
- if (theTemp == UntypedTemp(*e)) {
- e->type = static_cast<Type>(newType.type);
- e->memberResolver = newType.memberResolver;
- }
- }
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
-
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitSubscript(Subscript *e) {
- e->base->accept(this);
- e->index->accept(this);
- }
-
- virtual void visitMember(Member *e) {
- e->base->accept(this);
+ visit(use);
}
- virtual void visitExp(Exp *s) {s->expr->accept(this);}
- virtual void visitMove(Move *s) {
- s->source->accept(this);
- s->target->accept(this);
+private:
+ void visit(Stmt *s)
+ {
+ STMT_VISIT_ALL_KINDS(s);
}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(Phi *s) {
- s->targetTemp->accept(this);
- foreach (Expr *e, s->incoming)
- e->accept(this);
+ 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: public StmtVisitor, public ExprVisitor
+class TypeInference
{
enum { DebugTypeInference = 0 };
@@ -2248,7 +2217,7 @@ private:
TypingResult ty;
std::swap(_ty, ty);
std::swap(_currentStmt, s);
- _currentStmt->accept(this);
+ visit(_currentStmt);
std::swap(_currentStmt, s);
std::swap(_ty, ty);
return ty.fullyTyped;
@@ -2257,7 +2226,7 @@ private:
TypingResult run(Expr *e) {
TypingResult ty;
std::swap(_ty, ty);
- e->accept(this);
+ visit(e);
std::swap(_ty, ty);
if (ty.type != UnknownType)
@@ -2299,39 +2268,74 @@ private:
}
}
-protected:
- virtual void visitConst(Const *e) {
- if (e->type & NumberType) {
- if (canConvertToSignedInteger(e->value))
+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(e->value))
+ else if (canConvertToUnsignedInteger(c->value))
_ty = TypingResult(UInt32Type);
else
- _ty = TypingResult(e->type);
+ _ty = TypingResult(c->type);
} else
- _ty = TypingResult(e->type);
+ _ty = TypingResult(c->type);
}
- virtual void visitString(IR::String *) { _ty = TypingResult(StringType); }
- virtual void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); }
- virtual void visitName(Name *) { _ty = TypingResult(VarType); }
- virtual void visitTemp(Temp *e) {
+ 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);
}
- virtual void visitArgLocal(ArgLocal *e) {
+ void visitArgLocal(ArgLocal *e) {
_ty = TypingResult(VarType);
setType(e, _ty.type);
}
- virtual void visitClosure(Closure *) { _ty = TypingResult(VarType); }
- virtual void visitConvert(Convert *e) {
+ void visitClosure(Closure *) { _ty = TypingResult(VarType); }
+ void visitConvert(Convert *e) {
_ty = TypingResult(e->type);
}
- virtual void visitUnop(Unop *e) {
+ void visitUnop(Unop *e) {
_ty = run(e->expr);
switch (e->op) {
case OpUPlus: _ty.type = DoubleType; return;
@@ -2348,7 +2352,7 @@ protected:
}
}
- virtual void visitBinop(Binop *e) {
+ void visitBinop(Binop *e) {
TypingResult leftTy = run(e->left);
TypingResult rightTy = run(e->right);
_ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped;
@@ -2406,24 +2410,24 @@ protected:
}
}
- virtual void visitCall(Call *e) {
+ 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;
}
- virtual void visitNew(New *e) {
+ 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;
}
- virtual void visitSubscript(Subscript *e) {
+ void visitSubscript(Subscript *e) {
_ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped;
_ty.type = VarType;
}
- virtual void visitMember(Member *e) {
+ void visitMember(Member *e) {
_ty = run(e->base);
if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) {
@@ -2433,8 +2437,27 @@ protected:
_ty.type = VarType;
}
- virtual void visitExp(Exp *s) { _ty = run(s->expr); }
- virtual void visitMove(Move *s) {
+ 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) {
@@ -2455,10 +2478,10 @@ protected:
_ty.fullyTyped &= sourceTy.fullyTyped;
}
- virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); }
- virtual void visitCJump(CJump *s) { _ty = run(s->cond); }
- virtual void visitRet(Ret *s) { _ty = run(s->expr); }
- virtual void visitPhi(Phi *s) {
+ 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]);
@@ -2677,14 +2700,15 @@ void convertConst(Const *c, Type targetType)
c->type = targetType;
}
-class TypePropagation: public StmtVisitor, public ExprVisitor {
+class TypePropagation
+{
DefUses &_defUses;
Type _ty;
IR::Function *_f;
bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) {
qSwap(_ty, requestedType);
- e->accept(this);
+ visit(e);
qSwap(_ty, requestedType);
if (requestedType != UnknownType) {
@@ -2731,7 +2755,7 @@ public:
for (Stmt *s : bb->statements()) {
_currStmt = s;
- s->accept(this);
+ visit(s);
}
foreach (const Conversion &conversion, _conversions) {
@@ -2817,8 +2841,29 @@ public:
}
}
-protected:
- virtual void visitConst(Const *c) {
+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);
@@ -2828,15 +2873,7 @@ protected:
}
}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { run(e->expr, e->type); }
- virtual void visitUnop(Unop *e) { run(e->expr, e->type); }
- virtual void visitBinop(Binop *e) {
+ void visitBinop(Binop *e) {
// FIXME: This routine needs more tuning!
switch (e->op) {
case OpAdd:
@@ -2887,20 +2924,36 @@ protected:
Q_UNREACHABLE();
}
}
- virtual void visitCall(Call *e) {
+ void visitCall(Call *e) {
run(e->base);
for (ExprList *it = e->args; it; it = it->next)
run(it->expr);
}
- virtual void visitNew(New *e) {
+ void visitNew(New *e) {
run(e->base);
for (ExprList *it = e->args; it; it = it->next)
run(it->expr);
}
- virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
- virtual void visitMember(Member *e) { run(e->base); }
- virtual void visitExp(Exp *s) { run(s->expr); }
- virtual void visitMove(Move *s) {
+ 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
@@ -2925,12 +2978,11 @@ protected:
run(s->source, s->target->type, !inhibitConversion);
}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) {
+ void visitCJump(CJump *s) {
run(s->cond, BoolType);
}
- virtual void visitRet(Ret *s) { run(s->expr); }
- virtual void visitPhi(Phi *s) {
+ 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);
@@ -3299,6 +3351,15 @@ class BlockScheduler
// 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
}
@@ -3504,7 +3565,7 @@ static Expr *clone(Expr *e, IR::Function *function) {
}
}
-class ExprReplacer: public StmtVisitor, public ExprVisitor
+class ExprReplacer
{
DefUses &_defUses;
IR::Function* _function;
@@ -3535,7 +3596,7 @@ public:
// qout << " " << uses.size() << " uses:"<<endl;
foreach (Stmt *use, uses) {
// qout<<" ";use->dump(qout);qout<<"\n";
- use->accept(this);
+ visit(use);
// qout<<" -> ";use->dump(qout);qout<<"\n";
W += use;
if (newUses)
@@ -3546,45 +3607,101 @@ public:
qSwap(_toReplace, toReplace);
}
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { check(e->expr); }
- virtual void visitUnop(Unop *e) { check(e->expr); }
- virtual void visitBinop(Binop *e) { check(e->left); check(e->right); }
- virtual void visitCall(Call *e) {
+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);
}
- virtual void visitNew(New *e) {
+ void visitNew(New *e) {
check(e->base);
for (ExprList *it = e->args; it; it = it->next)
check(it->expr);
}
- virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- virtual void visitMember(Member *e) { check(e->base); }
- virtual void visitExp(Exp *s) { check(s->expr); }
- virtual void visitMove(Move *s) { check(s->target); check(s->source); }
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { check(s->cond); }
- virtual void visitRet(Ret *s) { check(s->expr); }
- virtual void visitPhi(Phi *s) {
+ 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))
+ if (equals(e, _toReplace)) {
e = clone(_replacement, _function);
- else
- e->accept(this);
+ } else {
+ visit(e);
+ }
}
// This only calculates equality for everything needed by constant propagation
@@ -3625,6 +3742,8 @@ namespace {
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)
{
@@ -3663,11 +3782,17 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
}
};
+ 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);
@@ -3677,8 +3802,12 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
// 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);
@@ -3686,6 +3815,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
while (!toPurge.isEmpty()) {
BasicBlock *bb = toPurge.first();
toPurge.removeFirst();
+ if (DebugUnlinking)
+ qDebug("... purging L%d", bb->index());
if (bb->isRemoved())
continue;
@@ -3729,6 +3860,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
}
dt.recalculateIDoms(siblings);
+ if (DebugUnlinking)
+ qDebug("Unlinking done.");
}
bool tryOptimizingComparison(Expr *&expr)
@@ -3748,42 +3881,42 @@ bool tryOptimizingComparison(Expr *&expr)
switch (b->op) {
case OpGt:
- leftConst->value = Runtime::compareGreaterThan(l, r);
+ leftConst->value = Runtime::method_compareGreaterThan(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpLt:
- leftConst->value = Runtime::compareLessThan(l, r);
+ leftConst->value = Runtime::method_compareLessThan(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpGe:
- leftConst->value = Runtime::compareGreaterEqual(l, r);
+ leftConst->value = Runtime::method_compareGreaterEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpLe:
- leftConst->value = Runtime::compareLessEqual(l, r);
+ leftConst->value = Runtime::method_compareLessEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpStrictEqual:
- leftConst->value = Runtime::compareStrictEqual(l, r);
+ leftConst->value = Runtime::method_compareStrictEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpEqual:
- leftConst->value = Runtime::compareEqual(l, r);
+ leftConst->value = Runtime::method_compareEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpStrictNotEqual:
- leftConst->value = Runtime::compareStrictNotEqual(l, r);
+ leftConst->value = Runtime::method_compareStrictNotEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpNotEqual:
- leftConst->value = Runtime::compareNotEqual(l, r);
+ leftConst->value = Runtime::method_compareNotEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
@@ -4153,7 +4286,8 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
}
//### TODO: use DefUses from the optimizer, because it already has all this information
-class InputOutputCollector: protected StmtVisitor, protected ExprVisitor {
+class InputOutputCollector
+{
void setOutput(Temp *out)
{
Q_ASSERT(!output);
@@ -4170,48 +4304,33 @@ public:
void collect(Stmt *s) {
inputs.resize(0);
output = 0;
- s->accept(this);
+ visit(s);
}
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *e) {
- inputs.push_back(e);
- }
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { e->base->accept(this); }
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
- virtual void visitMove(Move *s) {
- s->source->accept(this);
- if (Temp *t = s->target->asTemp()) {
- setOutput(t);
+private:
+ void visit(Expr *e)
+ {
+ if (auto t = e->asTemp()) {
+ inputs.push_back(t);
} else {
- s->target->accept(this);
+ EXPR_VISIT_ALL_KINDS(e);
}
}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(Phi *) {
- // Handled separately
+
+ 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);
+ }
}
};
@@ -4224,7 +4343,59 @@ protected:
* See LifeTimeIntervals::renumber for details on the numbering.
*/
class LifeRanges {
- typedef QSet<Temp> LiveRegs;
+ 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;
@@ -4232,14 +4403,21 @@ class LifeRanges {
LifeTimeInterval &interval(const Temp *temp)
{
- LifeTimeInterval *&lti = _intervals[temp->index];
- if (Q_UNLIKELY(!lti)) {
- lti = new LifeTimeInterval;
- lti->setTemp(*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;
@@ -4293,13 +4471,13 @@ public:
IRPrinter printer(&qout);
for (size_t i = 0, ei = _liveIn.size(); i != ei; ++i) {
qout << "L" << i <<" live-in: ";
- QList<Temp> live = QList<Temp>::fromSet(_liveIn.at(i));
- if (live.isEmpty())
+ 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 << ", ";
- printer.print(&live[i]);
+ qout << '%' << live.at(i);
}
qout << endl;
}
@@ -4318,8 +4496,10 @@ private:
for (Stmt *s : successor->statements()) {
if (Phi *phi = s->asPhi()) {
- if (Temp *t = phi->incoming.at(bbIndex)->asTemp())
- live.insert(*t);
+ if (Temp *t = phi->incoming.at(bbIndex)->asTemp()) {
+ ensureInterval(*t);
+ live.insert(t->index);
+ }
} else {
break;
}
@@ -4328,14 +4508,15 @@ private:
const QVector<Stmt *> &statements = bb->statements();
- foreach (const Temp &opd, live)
- interval(&opd).addRange(start(bb), end(bb));
+ 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()) {
- LiveRegs::iterator it = live.find(*phi->targetTemp);
+ 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));
@@ -4348,25 +4529,27 @@ private:
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());
+ 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);
+ live.insert(opd->index);
}
}
if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null.
- foreach (const Temp &opd, live)
- interval(&opd).addRange(start(bb), usePosition(loopEnd->terminator()));
+ for (int reg : live)
+ _intervals[reg]->addRange(start(bb), usePosition(loopEnd->terminator()));
}
- _liveIn[bb->index()] = live;
+ _liveIn[bb->index()] = std::move(live);
}
};
@@ -4381,7 +4564,7 @@ void removeUnreachleBlocks(IR::Function *function)
function->renumberBasicBlocks();
}
-class ConvertArgLocals: protected StmtVisitor, protected ExprVisitor
+class ConvertArgLocals
{
public:
ConvertArgLocals(IR::Function *function)
@@ -4419,10 +4602,13 @@ public:
}
}
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- for (Stmt *s : bb->statements())
- s->accept(this);
+ 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);
@@ -4430,39 +4616,45 @@ public:
function->locals.clear();
}
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { check(e->expr); }
- virtual void visitUnop(Unop *e) { check(e->expr); }
- virtual void visitBinop(Binop *e) { check(e->left); check(e->right); }
- virtual void visitCall(Call *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- virtual void visitNew(New *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
+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);
+ }
}
- virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- virtual void visitMember(Member *e) { check(e->base); }
- virtual void visitExp(Exp *s) { check(s->expr); }
- virtual void visitMove(Move *s) { check(s->target); check(s->source); }
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { check(s->cond); }
- virtual void visitRet(Ret *s) { check(s->expr); }
- virtual void visitPhi(Phi *) {
- Q_UNREACHABLE();
+
+ 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);
+ }
}
-private:
void check(Expr *&e) {
if (ArgLocal *al = e->asArgLocal()) {
if (al->kind == ArgLocal::Local) {
@@ -4475,7 +4667,7 @@ private:
e = t;
}
} else {
- e->accept(this);
+ visit(e);
}
}
@@ -4498,7 +4690,7 @@ private:
std::vector<int> tempForLocal;
};
-class CloneBasicBlock: protected IR::StmtVisitor, protected CloneExpr
+class CloneBasicBlock: protected CloneExpr
{
public:
BasicBlock *operator()(IR::BasicBlock *originalBlock)
@@ -4506,38 +4698,37 @@ public:
block = new BasicBlock(originalBlock->function, 0);
for (Stmt *s : originalBlock->statements()) {
- s->accept(this);
+ visit(s);
clonedStmt->location = s->location;
}
return block;
}
-protected:
- virtual void visitExp(Exp *stmt)
- { clonedStmt = block->EXP(clone(stmt->expr)); }
-
- virtual void visitMove(Move *stmt)
- { clonedStmt = block->MOVE(clone(stmt->target), clone(stmt->source)); }
-
- virtual void visitJump(Jump *stmt)
- { clonedStmt = block->JUMP(stmt->target); }
-
- virtual void visitCJump(CJump *stmt)
- { clonedStmt = block->CJUMP(clone(stmt->cond), stmt->iftrue, stmt->iffalse); }
-
- virtual void visitRet(Ret *stmt)
- { clonedStmt = block->RET(clone(stmt->expr)); }
-
- virtual void visitPhi(Phi *stmt)
+private:
+ void visit(Stmt *s)
{
- Phi *phi = block->function->NewStmt<Phi>();
- clonedStmt = phi;
-
- phi->targetTemp = clone(stmt->targetTemp);
- foreach (Expr *in, stmt->incoming)
- phi->incoming.append(clone(in));
- block->appendStatement(phi);
+ 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);
+ foreach (Expr *in, p->incoming)
+ phi->incoming.append(clone(in));
+ block->appendStatement(phi);
+ } else {
+ Q_UNREACHABLE();
+ }
}
private:
@@ -4706,13 +4897,20 @@ static void verifyCFG(IR::Function *function)
Q_ASSERT(function->basicBlock(bb->index()) == bb);
// Check the terminators:
- if (Jump *jump = bb->terminator()->asJump()) {
+ 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 = bb->terminator()->asCJump()) {
+ } else if (CJump *cjump = terminator->asCJump()) {
Q_UNUSED(cjump);
Q_ASSERT(bb->out.size() == 2);
Q_ASSERT(cjump->iftrue);
@@ -4721,7 +4919,7 @@ static void verifyCFG(IR::Function *function)
Q_ASSERT(cjump->iffalse);
Q_ASSERT(!cjump->iffalse->isRemoved());
Q_ASSERT(cjump->iffalse == bb->out[1]);
- } else if (bb->terminator()->asRet()) {
+ } else if (terminator->asRet()) {
Q_ASSERT(bb->out.size() == 0);
} else {
Q_UNREACHABLE();
@@ -4769,7 +4967,7 @@ static void verifyNoPointerSharing(IR::Function *function)
if (!DoVerification)
return;
- class : public StmtVisitor, public ExprVisitor {
+ class {
public:
void operator()(IR::Function *f)
{
@@ -4777,44 +4975,23 @@ static void verifyNoPointerSharing(IR::Function *function)
if (bb->isRemoved())
continue;
- for (Stmt *s : bb->statements())
- s->accept(this);
+ for (Stmt *s : bb->statements()) {
+ visit(s);
+ }
}
}
- protected:
- virtual void visitExp(Exp *s) { check(s); s->expr->accept(this); }
- virtual void visitMove(Move *s) { check(s); s->target->accept(this); s->source->accept(this); }
- virtual void visitJump(Jump *s) { check(s); }
- virtual void visitCJump(CJump *s) { check(s); s->cond->accept(this); }
- virtual void visitRet(Ret *s) { check(s); s->expr->accept(this); }
- virtual void visitPhi(Phi *s)
+ private:
+ void visit(Stmt *s)
{
check(s);
- s->targetTemp->accept(this);
- foreach (Expr *e, s->incoming)
- e->accept(this);
- }
-
- virtual void visitConst(Const *e) { check(e); }
- virtual void visitString(IR::String *e) { check(e); }
- virtual void visitRegExp(IR::RegExp *e) { check(e); }
- virtual void visitName(Name *e) { check(e); }
- virtual void visitTemp(Temp *e) { check(e); }
- virtual void visitArgLocal(ArgLocal *e) { check(e); }
- virtual void visitClosure(Closure *e) { check(e); }
- virtual void visitConvert(Convert *e) { check(e); e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { check(e); e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { check(e); e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(Call *e) { check(e); e->base->accept(this); check(e->args); }
- virtual void visitNew(New *e) { check(e); e->base->accept(this); check(e->args); }
- virtual void visitSubscript(Subscript *e) { check(e); e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { check(e); e->base->accept(this); }
-
- void check(ExprList *l)
+ STMT_VISIT_ALL_KINDS(s);
+ }
+
+ void visit(Expr *e)
{
- for (ExprList *it = l; it; it = it->next)
- check(it->expr);
+ check(e);
+ EXPR_VISIT_ALL_KINDS(e);
}
private:
@@ -4836,7 +5013,7 @@ static void verifyNoPointerSharing(IR::Function *function)
V(function);
}
-class RemoveLineNumbers: public SideEffectsChecker, public StmtVisitor
+class RemoveLineNumbers: private SideEffectsChecker
{
public:
static void run(IR::Function *function)
@@ -4854,28 +5031,99 @@ public:
}
private:
+ ~RemoveLineNumbers() {}
+
static bool hasSideEffects(Stmt *stmt)
{
RemoveLineNumbers checker;
- stmt->accept(&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 visitExp(Exp *s) Q_DECL_OVERRIDE { s->expr->accept(this); }
- void visitMove(Move *s) Q_DECL_OVERRIDE { s->source->accept(this); s->target->accept(this); }
- void visitJump(Jump *) Q_DECL_OVERRIDE {}
- void visitCJump(CJump *s) Q_DECL_OVERRIDE { s->cond->accept(this); }
- void visitRet(Ret *s) Q_DECL_OVERRIDE { s->expr->accept(this); }
- void visitPhi(Phi *) Q_DECL_OVERRIDE {}
+ 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.push_front(Range(from, from));
+ _ranges.prepend(Range(from, from));
if (_end == InvalidPosition)
_end = from;
} else {
@@ -4889,7 +5137,7 @@ void LifeTimeInterval::addRange(int from, int to) {
Q_ASSERT(to >= from);
if (_ranges.isEmpty()) {
- _ranges.push_front(Range(from, to));
+ _ranges.prepend(Range(from, to));
_end = to;
return;
}
@@ -4904,12 +5152,12 @@ void LifeTimeInterval::addRange(int from, int to) {
break;
p1->start = qMin(p->start, p1->start);
p1->end = qMax(p->end, p1->end);
- _ranges.pop_front();
+ _ranges.remove(0);
p = &_ranges.first();
}
} else {
if (to < p->start) {
- _ranges.push_front(Range(from, to));
+ _ranges.prepend(Range(from, to));
} else {
Q_ASSERT(from > _ranges.last().end);
_ranges.push_back(Range(from, to));
@@ -4950,7 +5198,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
}
if (newInterval._ranges.first().end == atPosition)
- newInterval._ranges.removeFirst();
+ newInterval._ranges.remove(0);
if (newStart == InvalidPosition) {
// the temp stays inactive for the rest of its lifetime
@@ -4970,7 +5218,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
break;
} else {
// the temp stays inactive for this interval, so remove it.
- newInterval._ranges.removeFirst();
+ newInterval._ranges.remove(0);
}
}
Q_ASSERT(!newInterval._ranges.isEmpty());
@@ -5001,15 +5249,6 @@ void LifeTimeInterval::dump(QTextStream &out) const {
out << " (register " << _reg << ")";
}
-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;
-}
bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
{
@@ -5103,6 +5342,8 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee
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");
@@ -5178,6 +5419,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee
}
verifyNoPointerSharing(function);
+ mergeBasicBlocks(function, &defUses, &df);
// 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
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index 5d4b12e275..3a787f0347 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -75,7 +75,7 @@ public:
bool covers(int position) const { return start <= position && position <= end; }
};
- typedef QVector<Range> Ranges;
+ typedef QVarLengthArray<Range, 4> Ranges;
private:
Temp _temp;
@@ -89,7 +89,7 @@ public:
enum { InvalidPosition = -1 };
enum { InvalidRegister = -1 };
- explicit LifeTimeInterval(int rangeCapacity = 2)
+ explicit LifeTimeInterval(int rangeCapacity = 4)
: _end(InvalidPosition)
, _reg(InvalidRegister)
, _isFixedInterval(0)
@@ -146,6 +146,17 @@ public:
}
};
+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)
@@ -379,7 +390,7 @@ protected:
_unhandled.removeLast();
}
- s->accept(this);
+ visit(s);
}
if (IR::Jump *jump = s->asJump()) {
@@ -402,7 +413,7 @@ protected:
moves.order();
QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
foreach (IR::Move *move, newMoves)
- move->accept(this);
+ visit(move);
}
}