aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmlcodegenerator.cpp544
-rw-r--r--src/qml/compiler/qqmlcodegenerator_p.h143
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp1908
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h247
-rw-r--r--src/qml/compiler/qv4compileddata.cpp72
-rw-r--r--src/qml/compiler/qv4compileddata_p.h106
-rw-r--r--src/qml/compiler/qv4compiler.cpp21
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h107
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp115
-rw-r--r--src/qml/compiler/qv4isel_masm_p.h32
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp329
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h4
-rw-r--r--src/qml/compiler/qv4isel_p.h2
-rw-r--r--src/qml/compiler/qv4isel_util_p.h36
-rw-r--r--src/qml/compiler/qv4jsir.cpp16
-rw-r--r--src/qml/compiler/qv4regalloc.cpp5
-rw-r--r--src/qml/compiler/qv4ssa.cpp18
-rw-r--r--src/qml/compiler/qv4ssa_p.h2
20 files changed, 3239 insertions, 476 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index df4f5e8dc3..b2569f9111 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -15,7 +15,8 @@ HEADERS += \
$$PWD/qv4ssa_p.h \
$$PWD/qv4regalloc_p.h \
$$PWD/qqmlcodegenerator_p.h \
- $$PWD/qv4isel_masm_p.h
+ $$PWD/qv4isel_masm_p.h \
+ $$PWD/qqmltypecompiler_p.h
SOURCES += \
$$PWD/qv4compileddata.cpp \
@@ -28,6 +29,7 @@ SOURCES += \
$$PWD/qv4ssa.cpp \
$$PWD/qv4regalloc.cpp \
$$PWD/qqmlcodegenerator.cpp \
- $$PWD/qv4isel_masm.cpp
+ $$PWD/qv4isel_masm.cpp \
+ $$PWD/qqmltypecompiler.cpp
include(../../3rdparty/masm/masm.pri)
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp
index 13b23fde68..f933465100 100644
--- a/src/qml/compiler/qqmlcodegenerator.cpp
+++ b/src/qml/compiler/qqmlcodegenerator.cpp
@@ -54,6 +54,8 @@
QT_USE_NAMESPACE
+static const quint32 emptyStringIndex = 0;
+
DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS);
using namespace QtQml;
@@ -64,6 +66,22 @@ using namespace QtQml;
return false; \
}
+void QmlObject::init(MemoryPool *pool, int typeNameIndex, int id, const AST::SourceLocation &loc)
+{
+ inheritedTypeNameIndex = typeNameIndex;
+
+ location.line = loc.startLine;
+ location.column = loc.startColumn;
+
+ idIndex = id;
+ indexOfDefaultProperty = -1;
+ properties = pool->New<PoolList<QmlProperty> >();
+ qmlSignals = pool->New<PoolList<Signal> >();
+ bindings = pool->New<PoolList<Binding> >();
+ functions = pool->New<PoolList<Function> >();
+ declarationsOverride = 0;
+}
+
void QmlObject::dump(DebugStream &out)
{
out << inheritedTypeNameIndex << " {" << endl;
@@ -73,6 +91,95 @@ void QmlObject::dump(DebugStream &out)
out << "}" << endl;
}
+QString QmlObject::sanityCheckFunctionNames(const QList<CompiledFunctionOrExpression> &allFunctions, const QSet<QString> &illegalNames, AST::SourceLocation *errorLocation)
+{
+ QSet<int> functionNames;
+ for (Function *f = functions->first; f; f = f->next) {
+ AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(allFunctions.at(f->index).node);
+ Q_ASSERT(function);
+ *errorLocation = function->identifierToken;
+ QString name = function->name.toString();
+ if (functionNames.contains(f->nameIndex))
+ return tr("Duplicate method name");
+ functionNames.insert(f->nameIndex);
+ if (signalNames.contains(f->nameIndex))
+ return tr("Duplicate method name");
+
+ if (name.at(0).isUpper())
+ return tr("Method names cannot begin with an upper case letter");
+ if (illegalNames.contains(name))
+ return tr("Illegal method name");
+ }
+ return QString(); // no error
+}
+
+QString QmlObject::appendSignal(Signal *signal)
+{
+ QmlObject *target = declarationsOverride;
+ if (!target)
+ target = this;
+ if (target->signalNames.contains(signal->nameIndex))
+ return tr("Duplicate signal name");
+ target->signalNames.insert(signal->nameIndex);
+ target->qmlSignals->append(signal);
+ return QString(); // no error
+}
+
+QString QmlObject::appendProperty(QmlProperty *prop, const QString &propertyName, bool isDefaultProperty, const AST::SourceLocation &defaultToken, AST::SourceLocation *errorLocation)
+{
+ QmlObject *target = declarationsOverride;
+ if (!target)
+ target = this;
+
+ if (target->propertyNames.contains(prop->nameIndex))
+ return tr("Duplicate property name");
+
+ if (propertyName.constData()->isUpper())
+ return tr("Property names cannot begin with an upper case letter");
+
+ target->propertyNames.insert(prop->nameIndex);
+
+ const int index = target->properties->append(prop);
+ if (isDefaultProperty) {
+ if (target->indexOfDefaultProperty != -1) {
+ *errorLocation = defaultToken;
+ return tr("Duplicate default property");
+ }
+ target->indexOfDefaultProperty = index;
+ }
+ return QString(); // no error
+}
+
+void QmlObject::appendFunction(Function *f)
+{
+ QmlObject *target = declarationsOverride;
+ if (!target)
+ target = this;
+ target->functions->append(f);
+}
+
+QString QmlObject::appendBinding(Binding *b, bool isListBinding)
+{
+ const bool bindingToDefaultProperty = (b->propertyNameIndex == 0);
+ if (!isListBinding && !bindingToDefaultProperty
+ && b->type != QV4::CompiledData::Binding::Type_GroupProperty
+ && b->type != QV4::CompiledData::Binding::Type_AttachedProperty
+ && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ if (bindingNames.contains(b->propertyNameIndex))
+ return tr("Property value set multiple times");
+ bindingNames.insert(b->propertyNameIndex);
+ }
+ if (isListBinding) {
+ bindings->append(b);
+ } else if (bindingToDefaultProperty) {
+ Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::location>(b);
+ bindings->insertAfter(insertionPoint, b);
+ } else {
+ bindings->prepend(b);
+ }
+ return QString(); // no error
+}
+
QStringList Signal::parameterStringList(const QStringList &stringPool) const
{
QStringList result;
@@ -82,8 +189,10 @@ QStringList Signal::parameterStringList(const QStringList &stringPool) const
return result;
}
-QQmlCodeGenerator::QQmlCodeGenerator()
- : _object(0)
+QQmlCodeGenerator::QQmlCodeGenerator(const QSet<QString> &illegalNames)
+ : illegalNames(illegalNames)
+ , _object(0)
+ , _propertyDeclaration(0)
, jsGenerator(0)
{
}
@@ -132,7 +241,7 @@ bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, co
this->pool = output->jsParserEngine.pool();
this->jsGenerator = &output->jsGenerator;
- emptyStringIndex = registerString(QString());
+ Q_ASSERT(registerString(QString()) == emptyStringIndex);
sourceCode = code;
@@ -205,7 +314,7 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node)
int idx = defineQMLObject(node);
appendBinding(node->qualifiedTypeNameId->identifierToken, emptyStringIndex, idx);
} else {
- int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer);
+ int idx = defineQMLObject(/*qualfied type name id*/0, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object);
appendBinding(node->qualifiedTypeNameId, idx);
}
return false;
@@ -213,8 +322,8 @@ bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node)
bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node)
{
- int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer);
- appendBinding(node->qualifiedId, idx);
+ int idx = defineQMLObject(node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer);
+ appendBinding(node->qualifiedId, idx, node->hasOnToken);
return false;
}
@@ -277,63 +386,31 @@ void QQmlCodeGenerator::accept(AST::Node *node)
AST::Node::acceptChild(node, this);
}
-bool QQmlCodeGenerator::sanityCheckFunctionNames()
-{
- QSet<QString> functionNames;
- for (Function *f = _object->functions->first; f; f = f->next) {
- AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(_functions.at(f->index).node);
- Q_ASSERT(function);
- QString name = function->name.toString();
- if (functionNames.contains(name))
- COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name"));
- functionNames.insert(name);
- if (_signalNames.contains(name))
- COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name"));
-
- if (name.at(0).isUpper())
- COMPILE_EXCEPTION(function->identifierToken, tr("Method names cannot begin with an upper case letter"));
-#if 0 // ###
- if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name.toString()))
- COMPILE_EXCEPTION(&currSlot, tr("Illegal method name"));
-#endif
- }
- return true;
-}
-
-int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer)
+int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, const AST::SourceLocation &location, AST::UiObjectInitializer *initializer, QmlObject *declarationsOverride)
{
QmlObject *obj = New<QmlObject>();
_objects.append(obj);
const int objectIndex = _objects.size() - 1;
qSwap(_object, obj);
- _object->inheritedTypeNameIndex = registerString(asString(qualifiedTypeNameId));
+ _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location);
+ _object->declarationsOverride = declarationsOverride;
- AST::SourceLocation loc;
- if (qualifiedTypeNameId)
- loc = qualifiedTypeNameId->firstSourceLocation();
- _object->location.line = loc.startLine;
- _object->location.column = loc.startColumn;
-
- _object->idIndex = emptyStringIndex;
- _object->indexOfDefaultProperty = -1;
- _object->properties = New<PoolList<QmlProperty> >();
- _object->qmlSignals = New<PoolList<Signal> >();
- _object->bindings = New<PoolList<Binding> >();
- _object->functions = New<PoolList<Function> >();
-
- QSet<QString> propertyNames;
- qSwap(_propertyNames, propertyNames);
- QSet<QString> signalNames;
- qSwap(_signalNames, signalNames);
+ // A new object is also a boundary for property declarations.
+ QmlProperty *declaration = 0;
+ qSwap(_propertyDeclaration, declaration);
accept(initializer);
- sanityCheckFunctionNames();
+ qSwap(_propertyDeclaration, declaration);
- qSwap(_propertyNames, propertyNames);
- qSwap(_signalNames, signalNames);
qSwap(_object, obj);
+
+ AST::SourceLocation loc;
+ QString error = obj->sanityCheckFunctionNames(_functions, illegalNames, &loc);
+ if (!error.isEmpty())
+ recordError(loc, error);
+
return objectIndex;
}
@@ -522,7 +599,7 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node)
QString signalName = node->name.toString();
signal->nameIndex = registerString(signalName);
- AST::SourceLocation loc = node->firstSourceLocation();
+ AST::SourceLocation loc = node->typeToken;
signal->location.line = loc.startLine;
signal->location.column = loc.startColumn;
@@ -582,19 +659,17 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node)
p = p->next;
}
- if (_signalNames.contains(signalName))
- COMPILE_EXCEPTION(node->identifierToken, tr("Duplicate signal name"));
- _signalNames.insert(signalName);
-
if (signalName.at(0).isUpper())
COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter"));
-#if 0 // ### cannot access identifier table from separate thread
- if (enginePrivate->v8engine()->illegalNames().contains(currSig.name.toString()))
- COMPILE_EXCEPTION(&currSig, tr("Illegal signal name"));
-#endif
+ if (illegalNames.contains(signalName))
+ COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name"));
- _object->qmlSignals->append(signal);
+ QString error = _object->appendSignal(signal);
+ if (!error.isEmpty()) {
+ recordError(node->identifierToken, error);
+ return false;
+ }
} else {
const QStringRef &memberType = node->memberType;
const QStringRef &name = node->name;
@@ -662,7 +737,8 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node)
else
property->customTypeNameIndex = emptyStringIndex;
- property->nameIndex = registerString(name.toString());
+ const QString propName = name.toString();
+ property->nameIndex = registerString(propName);
AST::SourceLocation loc = node->firstSourceLocation();
property->location.line = loc.startLine;
@@ -710,25 +786,38 @@ bool QQmlCodeGenerator::visit(AST::UiPublicMember *node)
propertyValue += alias.at(2);
}
property->aliasPropertyValueIndex = registerString(propertyValue);
- } else if (node->statement)
- appendBinding(node->identifierToken, property->nameIndex, node->statement);
+ } else if (node->statement) {
+ qSwap(_propertyDeclaration, property);
+ appendBinding(node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ qSwap(_propertyDeclaration, property);
+ }
- _object->properties->append(property);
+ AST::SourceLocation errorLocation;
+ QString error;
- if (node->isDefaultMember) {
- if (_object->indexOfDefaultProperty != -1) {
- QQmlError error;
- error.setDescription(QCoreApplication::translate("QQmlParser","Duplicate default property"));
- error.setLine(node->defaultToken.startLine);
- error.setColumn(node->defaultToken.startColumn);
- errors << error;
- return false;
- }
- _object->indexOfDefaultProperty = _object->properties->count - 1;
+ 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;
+
+ QQmlError qmlError;
+ qmlError.setDescription(error);
+ qmlError.setLine(errorLocation.startLine);
+ qmlError.setColumn(errorLocation.startColumn);
+ errors << qmlError;
+ return false;
}
- // process QML-like initializers (e.g. property Object o: Object {})
- AST::Node::accept(node->binding, this);
+ if (node->binding) {
+ qSwap(_propertyDeclaration, property);
+ // process QML-like initializers (e.g. property Object o: Object {})
+ AST::Node::accept(node->binding, this);
+ qSwap(_propertyDeclaration, property);
+ }
}
return false;
@@ -739,8 +828,13 @@ bool QQmlCodeGenerator::visit(AST::UiSourceElement *node)
if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
_functions << funDecl;
Function *f = New<Function>();
+ f->functionDeclaration = funDecl;
+ AST::SourceLocation loc = funDecl->identifierToken;
+ f->location.line = loc.startLine;
+ f->location.column = loc.startColumn;
f->index = _functions.size() - 1;
- _object->functions->append(f);
+ f->nameIndex = registerString(funDecl->name.toString());
+ _object->appendFunction(f);
} else {
QQmlError error;
error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
@@ -798,7 +892,12 @@ QStringRef QQmlCodeGenerator::textRefAt(const AST::SourceLocation &first, const
void QQmlCodeGenerator::setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement)
{
+ AST::SourceLocation loc = statement->firstSourceLocation();
+ binding->valueLocation.line = loc.startLine;
+ binding->valueLocation.column = loc.startColumn;
binding->type = QV4::CompiledData::Binding::Type_Invalid;
+ if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly))
+ binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(statement)) {
AST::ExpressionNode *expr = stmt->expression;
@@ -844,23 +943,20 @@ void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement *
qSwap(_object, object);
}
-void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex)
+void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment)
{
QmlObject *object = 0;
if (!resolveQualifiedId(&name, &object))
return;
qSwap(_object, object);
- appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex);
+ appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment);
qSwap(_object, object);
}
-void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value)
+void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, AST::Statement *value)
{
- if (!sanityCheckPropertyName(nameLocation, propertyNameIndex))
- return;
-
if (stringAt(propertyNameIndex) == QStringLiteral("id")) {
- setId(value);
+ setId(nameLocation, value);
return;
}
@@ -870,14 +966,14 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
setBindingValue(binding, value);
- _object->bindings->append(binding);
+ QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
+ if (!error.isEmpty()) {
+ recordError(nameLocation, error);
+ }
}
-void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem)
+void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment)
{
- if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem))
- return;
-
if (stringAt(propertyNameIndex) == QStringLiteral("id")) {
recordError(nameLocation, tr("Invalid component id specification"));
return;
@@ -887,26 +983,55 @@ void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, i
binding->propertyNameIndex = propertyNameIndex;
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
+
+ const QmlObject *obj = _objects.at(objectIndex);
+ binding->valueLocation = obj->location;
+
binding->flags = 0;
- binding->type = QV4::CompiledData::Binding::Type_Object;
+
+ if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly))
+ binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration;
+
+ // No type name on the initializer means it must be a group property
+ if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex)
+ binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
+ else
+ binding->type = QV4::CompiledData::Binding::Type_Object;
+
+ if (isOnAssignment)
+ binding->flags |= QV4::CompiledData::Binding::IsOnAssignment;
+ if (isListItem)
+ binding->flags |= QV4::CompiledData::Binding::IsListItem;
+
binding->value.objectIndex = objectIndex;
- _object->bindings->append(binding);
+ QString error = bindingsTarget()->appendBinding(binding, isListItem);
+ if (!error.isEmpty()) {
+ recordError(nameLocation, error);
+ }
+}
+
+QmlObject *QQmlCodeGenerator::bindingsTarget() const
+{
+ if (_propertyDeclaration && _object->declarationsOverride)
+ return _object->declarationsOverride;
+ return _object;
}
-bool QQmlCodeGenerator::setId(AST::Statement *value)
+bool QQmlCodeGenerator::setId(const AST::SourceLocation &idLocation, AST::Statement *value)
{
AST::SourceLocation loc = value->firstSourceLocation();
QStringRef str;
AST::Node *node = value;
if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node)) {
- if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression))
+ if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression)) {
str = lit->value;
- else
+ node = 0;
+ } else
node = stmt->expression;
}
- if (str.isEmpty())
+ if (node && str.isEmpty())
str = asStringRef(node);
if (str.isEmpty())
@@ -926,12 +1051,14 @@ bool QQmlCodeGenerator::setId(AST::Statement *value)
COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores"));
}
-#if 0 // ###
- if (enginePrivate->v8engine()->illegalNames().contains(str))
- COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
-#endif
+ QString idQString(str.toString());
+ if (illegalNames.contains(idQString))
+ COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property"));
- _object->idIndex = registerString(str.toString());
+ if (_object->idIndex != emptyStringIndex)
+ COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
+
+ _object->idIndex = registerString(idQString);
_object->locationOfIdProperty.line = loc.startLine;
_object->locationOfIdProperty.column = loc.startColumn;
@@ -940,61 +1067,57 @@ bool QQmlCodeGenerator::setId(AST::Statement *value)
bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object)
{
- AST::UiQualifiedId *name = *nameToResolve;
+ AST::UiQualifiedId *qualifiedIdElement = *nameToResolve;
+
+ if (qualifiedIdElement->name == QStringLiteral("id") && qualifiedIdElement->next)
+ COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property"));
+
+ // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type.
+ QString currentName = qualifiedIdElement->name.toString();
+ if (qualifiedIdElement->next) {
+ foreach (QV4::CompiledData::Import* import, _imports)
+ if (import->qualifierIndex != emptyStringIndex
+ && stringAt(import->qualifierIndex) == currentName) {
+ qualifiedIdElement = qualifiedIdElement->next;
+ currentName += QLatin1Char('.');
+ currentName += qualifiedIdElement->name;
+
+ if (!qualifiedIdElement->name.unicode()->isUpper())
+ COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name"));
- if (name->name == QStringLiteral("id") && name->next)
- COMPILE_EXCEPTION(name->identifierToken, tr( "Invalid use of id property"));
+ break;
+ }
+ }
*object = _object;
- while (name->next) {
+ while (qualifiedIdElement->next) {
Binding *binding = New<Binding>();
- binding->propertyNameIndex = registerString(name->name.toString());
- binding->location.line = name->identifierToken.startLine;
- binding->location.column = name->identifierToken.startColumn;
+ binding->propertyNameIndex = registerString(currentName);
+ binding->location.line = qualifiedIdElement->identifierToken.startLine;
+ binding->location.column = qualifiedIdElement->identifierToken.startColumn;
+ binding->valueLocation.line = binding->valueLocation.column = 0;
binding->flags = 0;
- if (name->name.unicode()->isUpper())
+ if (qualifiedIdElement->name.unicode()->isUpper())
binding->type = QV4::CompiledData::Binding::Type_AttachedProperty;
else
binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
- int objIndex = defineQMLObject(0, 0);
+ int objIndex = defineQMLObject(0, AST::SourceLocation(), 0, 0);
binding->value.objectIndex = objIndex;
- (*object)->bindings->append(binding);
+ QString error = (*object)->appendBinding(binding, /*isListBinding*/false);
+ if (!error.isEmpty()) {
+ recordError(qualifiedIdElement->identifierToken, error);
+ return false;
+ }
*object = _objects[objIndex];
- name = name->next;
- }
- *nameToResolve = name;
- return true;
-}
-
-bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem)
-{
- const QString &name = jsGenerator->strings.at(nameIndex);
- if (name.isEmpty())
- return true;
-
- // List items are implement by multiple bindings to the same name, so allow duplicates.
- if (!isListItem) {
- if (_propertyNames.contains(name))
- COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name"));
-
- _propertyNames.insert(name);
+ qualifiedIdElement = qualifiedIdElement->next;
+ if (qualifiedIdElement)
+ currentName = qualifiedIdElement->name.toString();
}
-
- if (name.at(0).isUpper())
- COMPILE_EXCEPTION(nameLocation, tr("Property names cannot begin with an upper case letter"));
-
-#if 0 // ### how to check against illegalNames when in separate thread?
- if (enginePrivate->v8engine()->illegalNames().contains(prop.name.toString())) {
- COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
- prop.nameLocation.column,
- tr("Illegal property name"));
- }
-#endif
-
+ *nameToResolve = qualifiedIdElement;
return true;
}
@@ -1011,20 +1134,21 @@ void QQmlCodeGenerator::recordError(const AST::SourceLocation &location, const Q
void QQmlCodeGenerator::collectTypeReferences()
{
foreach (QmlObject *obj, _objects) {
- if (!stringAt(obj->inheritedTypeNameIndex).isEmpty())
- _typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
-
- for (QmlProperty *prop = obj->properties->first; prop; prop = prop->next) {
- if (prop->type >= QV4::CompiledData::Property::Custom)
- _typeReferences.add(prop->customTypeNameIndex, prop->location);
+ if (obj->inheritedTypeNameIndex != emptyStringIndex) {
+ QV4::CompiledData::TypeReference &r = _typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
+ r.needsCreation = true;
}
- for (Signal *sig = obj->qmlSignals->first; sig; sig = sig->next)
- for (SignalParameter *param = sig->parameters->first; param; param = param->next)
- if (!stringAt(param->customTypeNameIndex).isEmpty())
- _typeReferences.add(param->customTypeNameIndex, param->location);
+ for (const QmlProperty *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;
+ }
+ }
- for (Binding *binding = obj->bindings->first; binding; binding = binding->next) {
+ for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
_typeReferences.add(binding->propertyNameIndex, binding->location);
}
@@ -1082,10 +1206,10 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const
int objectsSize = 0;
foreach (QmlObject *o, output.objects) {
objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize);
- objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count);
+ objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount());
int signalTableSize = 0;
- for (Signal *s = o->qmlSignals->first; s; s = s->next)
+ for (const Signal *s = o->firstSignal(); s; s = s->next)
signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count);
objectsSize += signalTableSize;
@@ -1128,46 +1252,45 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const
quint32 nextOffset = sizeof(QV4::CompiledData::Object);
- objectToWrite->nFunctions = o->functions->count;
+ objectToWrite->nFunctions = o->functionCount();
objectToWrite->offsetToFunctions = nextOffset;
nextOffset += objectToWrite->nFunctions * sizeof(quint32);
- objectToWrite->nProperties = o->properties->count;
+ objectToWrite->nProperties = o->propertyCount();
objectToWrite->offsetToProperties = nextOffset;
nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property);
- objectToWrite->nSignals = o->qmlSignals->count;
+ objectToWrite->nSignals = o->signalCount();
objectToWrite->offsetToSignals = nextOffset;
nextOffset += objectToWrite->nSignals * sizeof(quint32);
- objectToWrite->nBindings = o->bindings->count;
+ objectToWrite->nBindings = o->bindingCount();
objectToWrite->offsetToBindings = nextOffset;
nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions);
- for (Function *f = o->functions->first; f; f = f->next)
+ for (const Function *f = o->firstFunction(); f; f = f->next)
*functionsTable++ = runtimeFunctionIndices[f->index];
char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
- for (QmlProperty *p = o->properties->first; p; p = p->next) {
+ for (const QmlProperty *p = o->firstProperty(); p; p = p->next) {
QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr);
*propertyToWrite = *p;
propertiesPtr += sizeof(QV4::CompiledData::Property);
}
char *bindingPtr = objectPtr + objectToWrite->offsetToBindings;
- for (Binding *b = o->bindings->first; b; b = b->next) {
- QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
- *bindingToWrite = *b;
- if (b->type == QV4::CompiledData::Binding::Type_Script)
- bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex];
- bindingPtr += sizeof(QV4::CompiledData::Binding);
- }
+ bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isValueBindingNoAlias);
+ bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isSignalHandler);
+ bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isAttachedProperty);
+ bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isGroupProperty);
+ bindingPtr = writeBindings(bindingPtr, o, runtimeFunctionIndices, &QV4::CompiledData::Binding::isValueBindingToAlias);
+ Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount()));
quint32 *signalOffsetTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToSignals);
quint32 signalTableSize = 0;
char *signalPtr = objectPtr + nextOffset;
- for (Signal *s = o->qmlSignals->first; s; s = s->next) {
+ for (const Signal *s = o->firstSignal(); s; s = s->next) {
*signalOffsetTable++ = signalPtr - objectPtr;
QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr);
@@ -1184,7 +1307,7 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const
signalPtr += size;
}
- objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count);
+ objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount());
objectPtr += signalTableSize;
}
@@ -1199,6 +1322,20 @@ QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const
return qmlUnit;
}
+char *QmlUnitGenerator::writeBindings(char *bindingPtr, QmlObject *o, const QVector<int> &runtimeFunctionIndices, BindingFilter filter) const
+{
+ for (const Binding *b = o->firstBinding(); b; b = b->next) {
+ if (!(b->*(filter))())
+ continue;
+ QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
+ *bindingToWrite = *b;
+ if (b->type == QV4::CompiledData::Binding::Type_Script)
+ bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex];
+ bindingPtr += sizeof(QV4::CompiledData::Binding);
+ }
+ return bindingPtr;
+}
+
int QmlUnitGenerator::getStringId(const QString &str) const
{
return jsUnitGenerator->getStringId(str);
@@ -1620,16 +1757,24 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate,
: enginePrivate(enginePrivate)
, parsedQML(parsedQML)
, unit(unit)
+ , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(enginePrivate))->illegalNames())
{
}
bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations()
{
- foreach (QmlObject *obj, parsedQML->objects) {
+ for (int objectIndex = 0; objectIndex < parsedQML->objects.count(); ++objectIndex) {
+ QmlObject * const obj = parsedQML->objects.at(objectIndex);
QString elementName = stringAt(obj->inheritedTypeNameIndex);
if (elementName.isEmpty())
continue;
- QQmlPropertyCache *cache = unit->resolvedTypes[obj->inheritedTypeNameIndex].createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlCompiledData::TypeReference *tr = unit->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ QQmlCustomParser *customParser = (tr && tr->type) ? tr->type->customParser() : 0;
+ if (customParser && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers))
+ continue;
+ QQmlPropertyCache *cache = unit->propertyCaches.value(objectIndex);
+ if (!cache)
+ continue;
if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache))
return false;
}
@@ -1641,12 +1786,13 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
// map from signal name defined in qml itself to list of parameters
QHash<QString, QStringList> customSignals;
- for (Binding *binding = obj->bindings->first; binding; binding = binding->next) {
+ for (Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
QString propertyName = stringAt(binding->propertyNameIndex);
// Attached property?
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
QmlObject *attachedObj = parsedQML->objects[binding->value.objectIndex];
- QQmlType *type = unit->resolvedTypes.value(binding->propertyNameIndex).type;
+ QQmlCompiledData::TypeReference *typeRef = unit->resolvedTypes.value(binding->propertyNameIndex);
+ QQmlType *type = typeRef ? typeRef->type : 0;
const QMetaObject *attachedType = type ? type->attachedPropertiesType() : 0;
if (!attachedType)
COMPILE_EXCEPTION(binding->location, tr("Non-existent attached object"));
@@ -1659,10 +1805,6 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
if (!QQmlCodeGenerator::isSignalPropertyName(propertyName))
continue;
- if (binding->type != QV4::CompiledData::Binding::Type_Script) {
- COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment"));
- }
-
PropertyResolver resolver(propertyCache);
Q_ASSERT(propertyName.startsWith(QStringLiteral("on")));
@@ -1683,8 +1825,22 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
QQmlPropertyData *signal = resolver.signal(propertyName, &notInRevision);
if (signal) {
int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex);
- foreach (const QByteArray &param, propertyCache->signalParameterNames(sigIndex))
- parameters << QString::fromUtf8(param);
+ sigIndex = propertyCache->originalClone(sigIndex);
+
+ bool unnamedParameter = false;
+
+ QList<QByteArray> parameterNames = propertyCache->signalParameterNames(sigIndex);
+ for (int i = 0; i < parameterNames.count(); ++i) {
+ const QString param = QString::fromUtf8(parameterNames.at(i));
+ if (param.isEmpty())
+ unnamedParameter = true;
+ else if (unnamedParameter) {
+ COMPILE_EXCEPTION(binding->location, tr("Signal uses unnamed parameter followed by named parameter."));
+ } else if (illegalNames.contains(param)) {
+ COMPILE_EXCEPTION(binding->location, tr("Signal parameter \"%1\" hides global variable.").arg(param));
+ }
+ parameters += param;
+ }
} else {
if (notInRevision) {
// Try assinging it as a property later
@@ -1693,7 +1849,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
- const QQmlType *type = unit->resolvedTypes.value(obj->inheritedTypeNameIndex).type;
+ QQmlCompiledData::TypeReference *typeRef = unit->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ const QQmlType *type = typeRef ? typeRef->type : 0;
if (type) {
COMPILE_EXCEPTION(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion()));
} else {
@@ -1705,12 +1862,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
// build cache if necessary
if (customSignals.isEmpty()) {
- for (Signal *signal = obj->qmlSignals->first; signal; signal = signal->next) {
+ for (const Signal *signal = obj->firstSignal(); signal; signal = signal->next) {
const QString &signalName = stringAt(signal->nameIndex);
customSignals.insert(signalName, signal->parameterStringList(parsedQML->jsGenerator.strings));
}
- for (QmlProperty *property = obj->properties->first; property; property = property->next) {
+ for (const QmlProperty *property = obj->firstProperty(); property; property = property->next) {
const QString propName = stringAt(property->nameIndex);
customSignals.insert(propName, QStringList());
}
@@ -1731,6 +1888,18 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
parameters = entry.value();
}
+ binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName);
+
+ // Binding object to signal means connect the signal to the object's default method.
+ if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerObject;
+ continue;
+ }
+
+ if (binding->type != QV4::CompiledData::Binding::Type_Script) {
+ COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment"));
+ }
+
QQmlJS::Engine &jsEngine = parsedQML->jsParserEngine;
QQmlJS::MemoryPool *pool = jsEngine.pool();
@@ -1758,7 +1927,6 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
parsedQML->functions[binding->value.compiledScriptIndex] = functionDeclaration;
binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression;
- binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName);
}
return true;
}
@@ -1773,11 +1941,11 @@ void SignalHandlerConverter::recordError(const QV4::CompiledData::Location &loca
errors << error;
}
-QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision)
+QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context)
{
if (notInRevision) *notInRevision = false;
- QQmlPropertyData *d = cache->property(name, 0, 0);
+ QQmlPropertyData *d = cache->property(name, object, context);
// Find the first property
while (d && d->isFunction())
@@ -1792,11 +1960,11 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev
}
-QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision)
+QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context)
{
if (notInRevision) *notInRevision = false;
- QQmlPropertyData *d = cache->property(name, 0, 0);
+ QQmlPropertyData *d = cache->property(name, object, context);
if (notInRevision) *notInRevision = false;
while (d && !(d->isFunction()))
@@ -1812,7 +1980,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis
if (name.endsWith(QStringLiteral("Changed"))) {
QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed")));
- d = property(propName, notInRevision);
+ d = property(propName, notInRevision, object, context);
if (d)
return cache->signal(d->notifyIndex);
}
diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h
index 0a0e4f2d5b..7c09b8bfa2 100644
--- a/src/qml/compiler/qqmlcodegenerator_p.h
+++ b/src/qml/compiler/qqmlcodegenerator_p.h
@@ -93,15 +93,49 @@ struct PoolList
T *last;
int count;
- void append(T *item) {
+ int append(T *item) {
item->next = 0;
if (last)
last->next = item;
else
first = item;
last = item;
+ return count++;
+ }
+
+ void prepend(T *item) {
+ item->next = first;
+ first = item;
+ if (!last)
+ last = first;
++count;
}
+
+ template <typename Sortable, typename Base, Sortable Base::*sortMember>
+ T *findSortedInsertionPoint(T *item) const
+ {
+ T *insertPos = 0;
+
+ for (T *it = first; it; it = it->next) {
+ if (!(it->*sortMember < item->*sortMember))
+ break;
+ insertPos = it;
+ }
+
+ return insertPos;
+ }
+
+ void insertAfter(T *insertionPoint, T *item) {
+ if (!insertionPoint) {
+ prepend(item);
+ } else if (insertionPoint == last) {
+ append(item);
+ } else {
+ item->next = insertionPoint->next;
+ insertionPoint->next = item;
+ ++count;
+ }
+ }
};
struct QmlObject;
@@ -135,25 +169,73 @@ struct Binding : public QV4::CompiledData::Binding
struct Function
{
+ AST::FunctionDeclaration *functionDeclaration;
+ QV4::CompiledData::Location location;
+ int nameIndex;
int index; // index in parsedQML::functions
Function *next;
};
+struct CompiledFunctionOrExpression
+{
+ CompiledFunctionOrExpression()
+ : node(0)
+ , disableAcceleratedLookups(false)
+ {}
+ CompiledFunctionOrExpression(AST::Node *n)
+ : node(n)
+ , disableAcceleratedLookups(false)
+ {}
+ AST::Node *node; // FunctionDeclaration, Statement or Expression
+ QString name;
+ bool disableAcceleratedLookups;
+};
+
struct QmlObject
{
- int inheritedTypeNameIndex;
- int idIndex;
+ Q_DECLARE_TR_FUNCTIONS(QmlObject)
+public:
+ quint32 inheritedTypeNameIndex;
+ quint32 idIndex;
int indexOfDefaultProperty;
QV4::CompiledData::Location location;
QV4::CompiledData::Location locationOfIdProperty;
+ const QmlProperty *firstProperty() const { return properties->first; }
+ int propertyCount() const { return properties->count; }
+ const Signal *firstSignal() const { return qmlSignals->first; }
+ int signalCount() const { return qmlSignals->count; }
+ Binding *firstBinding() const { return bindings->first; }
+ int bindingCount() const { return bindings->count; }
+ const Function *firstFunction() const { return functions->first; }
+ int functionCount() const { return functions->count; }
+
+ // If set, then declarations for this object (and init bindings for these) should go into the
+ // specified object. Used for declarations inside group properties.
+ QmlObject *declarationsOverride;
+
+ void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const AST::SourceLocation &location = AST::SourceLocation());
+
+ void dump(DebugStream &out);
+
+ QString sanityCheckFunctionNames(const QList<CompiledFunctionOrExpression> &allFunctions, const QSet<QString> &illegalNames, AST::SourceLocation *errorLocation);
+
+ QString appendSignal(Signal *signal);
+ QString appendProperty(QmlProperty *prop, const QString &propertyName, bool isDefaultProperty, const AST::SourceLocation &defaultToken, AST::SourceLocation *errorLocation);
+ void appendFunction(Function *f);
+
+ QString appendBinding(Binding *b, bool isListBinding);
+
+private:
PoolList<QmlProperty> *properties;
PoolList<Signal> *qmlSignals;
PoolList<Binding> *bindings;
PoolList<Function> *functions;
- void dump(DebugStream &out);
+ QSet<int> propertyNames;
+ QSet<int> bindingNames;
+ QSet<int> signalNames;
};
struct Pragma
@@ -166,20 +248,6 @@ struct Pragma
QV4::CompiledData::Location location;
};
-struct CompiledFunctionOrExpression
-{
- CompiledFunctionOrExpression()
- : disableAcceleratedLookups(false)
- {}
- CompiledFunctionOrExpression(AST::Node *n)
- : node(n)
- , disableAcceleratedLookups(false)
- {}
- AST::Node *node; // FunctionDeclaration, Statement or Expression
- QString name;
- bool disableAcceleratedLookups;
-};
-
struct ParsedQML
{
ParsedQML(bool debugMode)
@@ -207,7 +275,7 @@ struct Q_QML_EXPORT QQmlCodeGenerator : public AST::Visitor
{
Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator)
public:
- QQmlCodeGenerator();
+ QQmlCodeGenerator(const QSet<QString> &illegalNames);
bool generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output);
static bool isSignalPropertyName(const QString &name);
@@ -234,9 +302,9 @@ public:
void accept(AST::Node *node);
// returns index in _objects
- int defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer);
- int defineQMLObject(AST::UiObjectDefinition *node)
- { return defineQMLObject(node->qualifiedTypeNameId, node->initializer); }
+ int defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, const AST::SourceLocation &location, AST::UiObjectInitializer *initializer, QmlObject *declarationsOverride = 0);
+ int defineQMLObject(AST::UiObjectDefinition *node, QmlObject *declarationsOverride = 0)
+ { return defineQMLObject(node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, declarationsOverride); }
static QString asString(AST::UiQualifiedId *node);
QStringRef asStringRef(AST::Node *node);
@@ -253,26 +321,26 @@ public:
void setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement);
void appendBinding(AST::UiQualifiedId *name, AST::Statement *value);
- void appendBinding(AST::UiQualifiedId *name, int objectIndex);
- void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value);
- void appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem = false);
+ void appendBinding(AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false);
+ void appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, AST::Statement *value);
+ void appendBinding(const AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
+
+ QmlObject *bindingsTarget() const;
- bool setId(AST::Statement *value);
+ bool setId(const AST::SourceLocation &idLocation, AST::Statement *value);
// resolves qualified name (font.pixelSize for example) and returns the last name along
// with the object any right-hand-side of a binding should apply to.
bool resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object);
- bool sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem = false);
-
void recordError(const AST::SourceLocation &location, const QString &description);
void collectTypeReferences();
static QQmlScript::LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
- int registerString(const QString &str) const { return jsGenerator->registerString(str); }
- template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
+ quint32 registerString(const QString &str) const { return jsGenerator->registerString(str); }
+ template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); }
QString stringAt(int index) const { return jsGenerator->strings.at(index); }
@@ -280,6 +348,8 @@ public:
QList<QQmlError> errors;
+ QSet<QString> illegalNames;
+
QList<QV4::CompiledData::Import*> _imports;
QList<Pragma*> _pragmas;
QList<QmlObject*> _objects;
@@ -288,15 +358,12 @@ public:
QV4::CompiledData::TypeReferenceMap _typeReferences;
QmlObject *_object;
- QSet<QString> _propertyNames;
- QSet<QString> _signalNames;
+ QmlProperty *_propertyDeclaration;
QQmlJS::MemoryPool *pool;
QString sourceCode;
QUrl url;
QV4::Compiler::JSUnitGenerator *jsGenerator;
- int emptyStringIndex;
- bool sanityCheckFunctionNames();
};
struct Q_QML_EXPORT QmlUnitGenerator
@@ -309,6 +376,9 @@ struct Q_QML_EXPORT QmlUnitGenerator
QV4::CompiledData::QmlUnit *generate(ParsedQML &output, const QVector<int> &runtimeFunctionIndices);
private:
+ typedef bool (Binding::*BindingFilter)() const;
+ char *writeBindings(char *bindingPtr, QmlObject *o, const QVector<int> &runtimeFunctionIndices, BindingFilter filter) const;
+
int getStringId(const QString &str) const;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
@@ -325,10 +395,10 @@ struct PropertyResolver
return cache->property(index);
}
- QQmlPropertyData *property(const QString &name, bool *notInRevision = 0);
+ QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, QObject *object = 0, QQmlContextData *context = 0);
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
- QQmlPropertyData *signal(const QString &name, bool *notInRevision);
+ QQmlPropertyData *signal(const QString &name, bool *notInRevision, QObject *object = 0, QQmlContextData *context = 0);
QQmlPropertyCache *cache;
};
@@ -357,6 +427,7 @@ private:
QQmlEnginePrivate *enginePrivate;
ParsedQML *parsedQML;
QQmlCompiledData *unit;
+ const QSet<QString> &illegalNames;
};
struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
new file mode 100644
index 0000000000..dae57fe6f4
--- /dev/null
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -0,0 +1,1908 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltypecompiler_p.h"
+
+#include <private/qqmlcompiler_p.h>
+#include <private/qqmlobjectcreator_p.h>
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qqmlcomponent_p.h>
+#include <private/qqmlstringconverters_p.h>
+
+#define COMPILE_EXCEPTION(token, desc) \
+ { \
+ recordError((token)->location, desc); \
+ return false; \
+ }
+
+QT_BEGIN_NAMESPACE
+
+QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QtQml::ParsedQML *parsedQML)
+ : engine(engine)
+ , compiledData(compiledData)
+ , typeData(typeData)
+ , parsedQML(parsedQML)
+{
+}
+
+bool 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);
+ compiledData->importCache->addref();
+
+ 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()) {
+ QQmlError cacheError;
+ ref->typePropertyCache = engine->cache(ref->type,
+ resolvedType->minorVersion,
+ cacheError);
+ if (!ref->typePropertyCache) {
+ 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;
+ compiledData->resolvedTypes.insert(resolvedType.key(), ref.take());
+ }
+
+ // Build property caches and VME meta object data
+
+ const int objectCount = parsedQML->objects.count();
+ compiledData->datas.reserve(objectCount);
+ compiledData->propertyCaches.reserve(objectCount);
+
+ {
+ QQmlPropertyCacheCreator propertyCacheBuilder(this);
+ if (!propertyCacheBuilder.buildMetaObjects())
+ return false;
+ }
+
+ {
+ SignalHandlerConverter converter(engine, parsedQML, compiledData);
+ if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) {
+ errors << converter.errors;
+ return false;
+ }
+ }
+
+ {
+ QQmlEnumTypeResolver enumResolver(this);
+ if (!enumResolver.resolveEnumBindings())
+ return false;
+ }
+
+ {
+ QQmlAliasAnnotator annotator(this);
+ 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);
+
+ QString qualifier = script.qualifier;
+ QString enclosingNamespace;
+
+ const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
+ if (lastDotIndex != -1) {
+ enclosingNamespace = qualifier.left(lastDotIndex);
+ qualifier = qualifier.mid(lastDotIndex+1);
+ }
+
+ compiledData->importCache->add(qualifier, 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;
+ }
+
+ // Compile JS binding expressions and signal handlers
+
+ JSCodeGen jsCodeGen(typeData->finalUrlString(), parsedQML->code, &parsedQML->jsModule, &parsedQML->jsParserEngine, parsedQML->program, compiledData->importCache);
+ const QVector<int> runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(parsedQML->functions);
+ QList<QQmlError> jsErrors = jsCodeGen.errors();
+ if (!jsErrors.isEmpty()) {
+ errors << jsErrors;
+ return false;
+ }
+
+ QV4::ExecutionEngine *v4 = engine->v4engine();
+
+ QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(engine, v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator));
+ isel->setUseFastLookups(false);
+ QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/false);
+
+ // Generate QML compiled type data structures
+
+ QmlUnitGenerator qmlGenerator;
+ QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(*parsedQML, runtimeFunctionIndices);
+
+ if (jsUnit) {
+ Q_ASSERT(!jsUnit->data);
+ jsUnit->ownsData = false;
+ jsUnit->data = &qmlUnit->header;
+ }
+
+ compiledData->compilationUnit = jsUnit;
+ if (compiledData->compilationUnit)
+ compiledData->compilationUnit->ref();
+ compiledData->qmlUnit = qmlUnit; // ownership transferred to m_compiledData
+
+ // 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, runtimeFunctionIndices);
+ if (!validator.validate())
+ return false;
+
+ // Collect some data for instantiation later.
+ int bindingCount = 0;
+ int parserStatusCount = 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;
+ }
+ if (typeRef->component) {
+ bindingCount += typeRef->component->totalBindingsCount;
+ parserStatusCount += typeRef->component->totalParserStatusCount;
+ }
+ }
+ }
+
+ compiledData->totalBindingsCount = bindingCount;
+ compiledData->totalParserStatusCount = parserStatusCount;
+
+ return errors.isEmpty();
+}
+
+void QQmlTypeCompiler::recordError(const QQmlError &error)
+{
+ QQmlError e = error;
+ e.setUrl(compiledData->url);
+ errors << e;
+}
+
+QString QQmlTypeCompiler::stringAt(int idx) const
+{
+ return parsedQML->stringAt(idx);
+}
+
+int QQmlTypeCompiler::registerString(const QString &str)
+{
+ return parsedQML->jsGenerator.registerString(str);
+}
+
+const QV4::CompiledData::QmlUnit *QQmlTypeCompiler::qmlUnit() const
+{
+ return compiledData->qmlUnit;
+}
+
+const QQmlImports *QQmlTypeCompiler::imports() const
+{
+ return &typeData->imports();
+}
+
+QHash<int, QQmlCompiledData::TypeReference*> *QQmlTypeCompiler::resolvedTypes()
+{
+ return &compiledData->resolvedTypes;
+}
+
+QList<QmlObject *> *QQmlTypeCompiler::qmlObjects()
+{
+ return &parsedQML->objects;
+}
+
+int QQmlTypeCompiler::rootObjectIndex() const
+{
+ return parsedQML->indexOfRootObject;
+}
+
+void QQmlTypeCompiler::setPropertyCaches(const QVector<QQmlPropertyCache *> &caches)
+{
+ Q_ASSERT(compiledData->propertyCaches.isEmpty());
+ compiledData->propertyCaches = caches;
+ Q_ASSERT(caches.count() >= parsedQML->indexOfRootObject);
+ compiledData->rootPropertyCache = caches.at(parsedQML->indexOfRootObject);
+ compiledData->rootPropertyCache->addref();
+}
+
+const QVector<QQmlPropertyCache *> &QQmlTypeCompiler::propertyCaches() const
+{
+ return compiledData->propertyCaches;
+}
+
+void QQmlTypeCompiler::setVMEMetaObjects(const QVector<QByteArray> &metaObjects)
+{
+ Q_ASSERT(compiledData->datas.isEmpty());
+ compiledData->datas = metaObjects;
+}
+
+QVector<QByteArray> *QQmlTypeCompiler::vmeMetaObjects() const
+{
+ return &compiledData->datas;
+}
+
+QHash<int, int> *QQmlTypeCompiler::objectIndexToIdForRoot()
+{
+ return &compiledData->objectIndexToIdForRoot;
+}
+
+QHash<int, QHash<int, int> > *QQmlTypeCompiler::objectIndexToIdPerComponent()
+{
+ return &compiledData->objectIndexToIdPerComponent;
+}
+
+QHash<int, QByteArray> *QQmlTypeCompiler::customParserData()
+{
+ return &compiledData->customParserData;
+}
+
+MemoryPool *QQmlTypeCompiler::memoryPool()
+{
+ return parsedQML->jsParserEngine.pool();
+}
+
+const QList<CompiledFunctionOrExpression> &QQmlTypeCompiler::functions() const
+{
+ return parsedQML->functions;
+}
+
+void QQmlTypeCompiler::setCustomParserBindings(const QVector<int> &bindings)
+{
+ compiledData->customParserBindings = bindings;
+}
+
+QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
+ : compiler(typeCompiler)
+{
+}
+
+void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description)
+{
+ 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)
+{
+ const QmlObject *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 = PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), &notInRevision);
+ if (instantiatingProperty) {
+ if (instantiatingProperty->isQObject()) {
+ baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType);
+ Q_ASSERT(baseTypeCache);
+ } else if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(instantiatingProperty->propType)) {
+ baseTypeCache = enginePrivate->cache(vt->metaObject());
+ Q_ASSERT(baseTypeCache);
+ }
+ }
+ }
+
+ bool needVMEMetaObject = obj->propertyCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0;
+ if (!needVMEMetaObject) {
+ for (const QtQml::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);
+ baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ Q_ASSERT(baseTypeCache);
+ }
+
+ if (needVMEMetaObject) {
+ if (!createMetaObject(objectIndex, obj, baseTypeCache))
+ return false;
+ } else if (baseTypeCache) {
+ propertyCaches[objectIndex] = baseTypeCache;
+ baseTypeCache->addref();
+ }
+
+ if (propertyCaches.at(objectIndex)) {
+ for (const QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next)
+ if (binding->type == QV4::CompiledData::Binding::Type_Object || binding->type == QV4::CompiledData::Binding::Type_GroupProperty)
+ if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding))
+ return false;
+ }
+
+ return true;
+}
+
+bool QQmlPropertyCacheCreator::ensureMetaObject(int objectIndex)
+{
+ if (!vmeMetaObjects.at(objectIndex).isEmpty())
+ return true;
+ const QtQml::QmlObject *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 QtQml::QmlObject *obj, QQmlPropertyCache *baseTypeCache)
+{
+ QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(QQmlEnginePrivate::get(enginePrivate),
+ 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, qMetaTypeId<QJSValue>() },
+ { 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 (false /* ### compileState->root == obj && !compileState->nested*/) {
+#if 0 // ###
+ QString path = output->url.path();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ if (lastSlash > -1) {
+ QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5);
+ if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
+ newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
+ QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+ }
+#endif
+ }
+ 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;
+
+ PropertyResolver resolver(baseTypeCache);
+
+ for (const QtQml::QmlProperty *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_Var = 1, NSS_Alias = 2 };
+ for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
+
+ if (ii == NSS_Var && varPropCount == 0) continue;
+ else if (ii == NSS_Alias && aliasCount == 0) continue;
+
+ for (const QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next) {
+ if ((ii == NSS_Normal && (p->type == QV4::CompiledData::Property::Alias ||
+ p->type == QV4::CompiledData::Property::Var)) ||
+ ((ii == NSS_Var) && (p->type != QV4::CompiledData::Property::Var)) ||
+ ((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 QtQml::Signal *s = obj->firstSignal(); s; s = s->next) {
+ const int paramCount = s->parameters->count;
+
+ QList<QByteArray> names;
+ QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
+
+ if (paramCount) {
+ paramTypes[0] = paramCount;
+
+ QtQml::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 QtQml::Function *s = obj->firstFunction(); s; s = s->next) {
+ 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;
+ 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 QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) {
+
+ if (p->type == QV4::CompiledData::Property::Alias ||
+ p->type == QV4::CompiledData::Property::Var)
+ continue;
+
+ int propertyType = 0;
+ int vmePropertyType = 0;
+ quint32 propertyFlags = 0;
+
+ 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++;
+ }
+
+ // Now do var properties
+ propertyIdx = 0;
+ for (const QtQml::QmlProperty *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) {
+
+ if (p->type != QV4::CompiledData::Property::Var)
+ continue;
+
+ quint32 propertyFlags = QQmlPropertyData::IsVarProperty;
+ if (!p->flags & QV4::CompiledData::Property::IsReadOnly)
+ propertyFlags |= QQmlPropertyData::IsWritable;
+
+ VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
+ (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant;
+ vmd->propertyCount++;
+ ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++;
+
+ QString propertyName = stringAt(p->nameIndex);
+ if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName;
+ cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ QMetaType::QVariant, effectiveSignalIndex);
+
+ effectiveSignalIndex++;
+ }
+
+ // Alias property count. Actual data is setup in buildDynamicMetaAliases
+ ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
+
+ // Dynamic slot data - comes after the property data
+ for (const QtQml::Function *s = obj->firstFunction(); s; s = s->next) {
+ AST::FunctionDeclaration *astFunction = s->functionDeclaration;
+ int formalsCount = 0;
+ 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;
+}
+
+QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+ , imports(typeCompiler->imports())
+ , resolvedTypes(typeCompiler->resolvedTypes())
+{
+}
+
+bool QQmlEnumTypeResolver::resolveEnumBindings()
+{
+ for (int i = 0; i < qmlObjects.count(); ++i) {
+ QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ if (!propertyCache)
+ continue;
+ const QmlObject *obj = qmlObjects.at(i);
+
+ PropertyResolver resolver(propertyCache);
+
+ for (QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ continue;
+
+ if (binding->type != QV4::CompiledData::Binding::Type_Script)
+ continue;
+
+ const QString propertyName = stringAt(binding->propertyNameIndex);
+ bool notInRevision = false;
+ QQmlPropertyData *pd = resolver.property(propertyName, &notInRevision);
+ if (!pd)
+ continue;
+
+ if (!pd->isEnum() && pd->propType != QMetaType::Int)
+ continue;
+
+ if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+struct StaticQtMetaObject : public QObject
+{
+ static const QMetaObject *get()
+ { return &staticQtMetaObject; }
+};
+
+bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlObject *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, Binding *binding)
+{
+ bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum();
+ if (!prop->isEnum() && !isIntProp)
+ return true;
+
+ if (!prop->isWritable() && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration))
+ COMPILE_EXCEPTION(binding, tr("Invalid property assignment: \"%1\" is a read-only property").arg(stringAt(binding->propertyNameIndex)));
+
+ Q_ASSERT(binding->type = QV4::CompiledData::Binding::Type_Script);
+ QString string = stringAt(binding->stringIndex);
+ if (!string.at(0).isUpper())
+ return true;
+
+ int dot = string.indexOf(QLatin1Char('.'));
+ if (dot == -1 || dot == string.length()-1)
+ return true;
+
+ if (string.indexOf(QLatin1Char('.'), dot+1) != -1)
+ return true;
+
+ QHashedStringRef typeName(string.constData(), dot);
+ QString enumValue = string.mid(dot+1);
+
+ if (isIntProp) {
+ // Allow enum assignment to ints.
+ bool ok;
+ int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok);
+ if (ok) {
+ binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->value.d = (double)enumval;
+ binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
+ }
+ return true;
+ }
+ QQmlType *type = 0;
+ imports->resolveType(typeName, &type, 0, 0, 0);
+
+ if (!type && typeName != QLatin1String("Qt"))
+ return true;
+ if (type && type->isComposite()) //No enums on composite (or composite singleton) types
+ return true;
+
+ int value = 0;
+ bool ok = false;
+
+ QQmlCompiledData::TypeReference *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ if (type && tr && tr->type == type) {
+ QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex);
+
+ // When these two match, we can short cut the search
+ if (mprop.isFlagType()) {
+ value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok);
+ } else {
+ value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok);
+ }
+ } else {
+ // Otherwise we have to search the whole type
+ if (type) {
+ value = type->enumValue(QHashedStringRef(enumValue), &ok);
+ } else {
+ QByteArray enumName = enumValue.toUtf8();
+ const QMetaObject *metaObject = StaticQtMetaObject::get();
+ for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
+ QMetaEnum e = metaObject->enumerator(ii);
+ value = e.keyToValue(enumName.constData(), &ok);
+ }
+ }
+ }
+
+ if (!ok)
+ return true;
+
+ binding->type = QV4::CompiledData::Binding::Type_Number;
+ binding->value.d = (double)value;
+ binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
+ return true;
+}
+
+int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const
+{
+ Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer");
+ *ok = false;
+
+ if (scope != QLatin1String("Qt")) {
+ QQmlType *type = 0;
+ imports->resolveType(scope, &type, 0, 0, 0);
+ return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1;
+ }
+
+ const QMetaObject *mo = StaticQtMetaObject::get();
+ int i = mo->enumeratorCount();
+ while (i--) {
+ int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
+ if (*ok)
+ return v;
+ }
+ return -1;
+}
+
+
+QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , qmlObjects(*typeCompiler->qmlObjects())
+ , propertyCaches(typeCompiler->propertyCaches())
+{
+}
+
+void QQmlAliasAnnotator::annotateBindingsToAliases()
+{
+ for (int i = 0; i < qmlObjects.count(); ++i) {
+ QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ if (!propertyCache)
+ continue;
+
+ const QmlObject *obj = qmlObjects.at(i);
+
+ PropertyResolver resolver(propertyCache);
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+
+ for (QtQml::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;
+ if (pd && pd->isAlias())
+ binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
+ }
+ }
+}
+
+QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler)
+ : QQmlCompilePass(typeCompiler)
+ , enginePrivate(typeCompiler->enginePrivate())
+ , 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())
+{
+}
+
+void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QtQml::QmlObject *obj, int objectIndex)
+{
+ QQmlPropertyCache *propertyCache = propertyCaches.value(objectIndex);
+ if (!propertyCache)
+ return;
+
+ PropertyResolver propertyResolver(propertyCache);
+
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+
+ for (QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Object)
+ continue;
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ continue;
+
+ const QtQml::QmlObject *targetObject = qmlObjects->at(binding->value.objectIndex);
+ QQmlCompiledData::TypeReference *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
+ Q_ASSERT(tr);
+ QQmlType *targetType = tr->type;
+ if (targetType && targetType->metaObject() == &QQmlComponent::staticMetaObject)
+ continue;
+
+ QQmlPropertyData *pd = 0;
+ if (binding->propertyNameIndex != 0) {
+ bool notInRevision = false;
+ pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
+ } else {
+ pd = defaultProperty;
+ }
+ if (!pd || !pd->isQObject())
+ continue;
+
+ QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType);
+ const QMetaObject *mo = pc->firstCppMetaObject();
+ while (mo) {
+ if (mo == &QQmlComponent::staticMetaObject)
+ break;
+ mo = mo->superClass();
+ }
+
+ if (!mo)
+ continue;
+
+ static QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject);
+ Q_ASSERT(componentType);
+
+ QtQml::QmlObject *syntheticComponent = pool->New<QtQml::QmlObject>();
+ syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), compiler->registerString(QString()));
+
+ if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
+ QQmlCompiledData::TypeReference *typeRef = new QQmlCompiledData::TypeReference;
+ typeRef->type = componentType;
+ typeRef->majorVersion = componentType->majorVersion();
+ typeRef->minorVersion = componentType->minorVersion();
+ resolvedTypes->insert(syntheticComponent->inheritedTypeNameIndex, typeRef);
+ }
+
+ qmlObjects->append(syntheticComponent);
+ const int componentIndex = qmlObjects->count() - 1;
+
+ QtQml::Binding *syntheticBinding = pool->New<QtQml::Binding>();
+ *syntheticBinding = *binding;
+ syntheticBinding->type = QV4::CompiledData::Binding::Type_Object;
+ QString error = syntheticComponent->appendBinding(syntheticBinding, /*isListBinding*/false);
+ Q_ASSERT(error.isEmpty());
+ Q_UNUSED(error);
+
+ binding->value.objectIndex = componentIndex;
+
+ componentRoots.append(componentIndex);
+ componentBoundaries.append(syntheticBinding->value.objectIndex);
+ }
+}
+
+bool QQmlComponentAndAliasResolver::resolve()
+{
+ // Detect real Component {} objects as well as implicitly defined components, such as
+ // someItemDelegate: Item {}
+ // In the implicit case Item is surrounded by a synthetic Component {} because the property
+ // on the left hand side is of QQmlComponent type.
+ const int objCountWithoutSynthesizedComponents = qmlObjects->count();
+ for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) {
+ const QtQml::QmlObject *obj = qmlObjects->at(i);
+ if (obj->inheritedTypeNameIndex == 0)
+ continue;
+
+ QQmlCompiledData::TypeReference *tref = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(tref);
+ if (!tref->type || tref->type->metaObject() != &QQmlComponent::staticMetaObject) {
+ findAndRegisterImplicitComponents(obj, i);
+ continue;
+ }
+
+ componentRoots.append(i);
+
+ if (obj->functionCount() > 0)
+ COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
+ if (obj->propertyCount() > 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."));
+
+ if (obj->bindingCount() == 0)
+ COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
+
+ const QtQml::Binding *rootBinding = obj->firstBinding();
+ if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
+ COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id"));
+
+ componentBoundaries.append(rootBinding->value.objectIndex);
+ }
+
+ std::sort(componentBoundaries.begin(), componentBoundaries.end());
+
+ for (int i = 0; i < componentRoots.count(); ++i) {
+ const QtQml::QmlObject *component = qmlObjects->at(componentRoots.at(i));
+ const QtQml::Binding *rootBinding = component->firstBinding();
+
+ _componentIndex = i;
+ _idToObjectIndex.clear();
+
+ _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)];
+
+ _objectsWithAliases.clear();
+
+ if (!collectIdsAndAliases(rootBinding->value.objectIndex))
+ return false;
+
+ if (!resolveAliases())
+ return false;
+ }
+
+ // Collect ids and aliases for root
+ _componentIndex = -1;
+ _idToObjectIndex.clear();
+ _objectIndexToIdInScope = objectIndexToIdForRoot;
+ _objectsWithAliases.clear();
+
+ collectIdsAndAliases(indexOfRootObject);
+
+ resolveAliases();
+
+ return true;
+}
+
+bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
+{
+ const QtQml::QmlObject *obj = qmlObjects->at(objectIndex);
+
+ if (obj->idIndex != 0) {
+ if (_idToObjectIndex.contains(obj->idIndex)) {
+ recordError(obj->locationOfIdProperty, tr("id is not unique"));
+ return false;
+ }
+ _idToObjectIndex.insert(obj->idIndex, objectIndex);
+ _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count());
+ }
+
+ for (const QtQml::QmlProperty *property = obj->firstProperty(); property; property = property->next) {
+ if (property->type == QV4::CompiledData::Property::Alias) {
+ _objectsWithAliases.append(objectIndex);
+ break;
+ }
+ }
+
+ for (const QtQml::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Object
+ && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
+ && 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;
+ }
+
+ return true;
+}
+
+bool QQmlComponentAndAliasResolver::resolveAliases()
+{
+ foreach (int objectIndex, _objectsWithAliases) {
+ const QtQml::QmlObject *obj = qmlObjects->at(objectIndex);
+
+ QQmlPropertyCache *propertyCache = propertyCaches.value(objectIndex);
+ Q_ASSERT(propertyCache);
+
+ int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
+ int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
+ int effectiveAliasIndex = 0;
+
+ const QtQml::QmlProperty *p = obj->firstProperty();
+ for (int propertyIndex = 0; propertyIndex < obj->propertyCount(); ++propertyIndex, p = p->next) {
+ if (p->type != QV4::CompiledData::Property::Alias)
+ continue;
+
+ 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)));
+ 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 QtQml::QmlObject *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;
+ } else {
+ QQmlPropertyCache *targetCache = propertyCaches.value(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QtQml::PropertyResolver resolver(targetCache);
+
+ QQmlPropertyData *targetProperty = resolver.property(property.toString());
+ if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) {
+ recordError(p->aliasLocation, tr("Invalid alias location"));
+ return false;
+ }
+
+ propIdx = targetProperty->coreIndex;
+ type = targetProperty->propType;
+
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+ notifySignal = targetProperty->notifyIndex;
+
+ if (!subProperty.isEmpty()) {
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
+ if (!valueType) {
+ recordError(p->aliasLocation, tr("Invalid alias location"));
+ return false;
+ }
+
+ propType = type;
+
+ int valueTypeIndex =
+ valueType->metaObject()->indexOfProperty(subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1) {
+ recordError(p->aliasLocation, tr("Invalid alias location"));
+ return false;
+ }
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+
+ propIdx |= (valueTypeIndex << 16);
+ if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
+ type = QVariant::Int;
+ else
+ type = valueType->metaObject()->property(valueTypeIndex).userType();
+
+ } else {
+ if (targetProperty->isEnum()) {
+ type = QVariant::Int;
+ } else {
+ // Copy type flags
+ propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask;
+
+ if (targetProperty->isVarProperty())
+ propertyFlags |= QQmlPropertyData::IsQVariant;
+
+ if (targetProperty->isQObject())
+ flags |= QML_ALIAS_FLAG_PTR;
+ }
+ }
+ }
+
+ QQmlVMEMetaData::AliasData aliasData = { targetId, propIdx, propType, flags, notifySignal };
+
+ typedef QQmlVMEMetaData VMD;
+ QByteArray &dynamicData = (*vmeMetaObjectData)[objectIndex];
+ Q_ASSERT(!dynamicData.isEmpty());
+ VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
+ *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
+
+ Q_ASSERT(dynamicData.isDetached());
+
+ if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && writable)
+ propertyFlags |= QQmlPropertyData::IsWritable;
+ else
+ propertyFlags &= ~QQmlPropertyData::IsWritable;
+
+ if (resettable)
+ propertyFlags |= QQmlPropertyData::IsResettable;
+ else
+ propertyFlags &= ~QQmlPropertyData::IsResettable;
+
+ QString propertyName = stringAt(p->nameIndex);
+ if (propertyIndex == obj->indexOfDefaultProperty) propertyCache->_defaultPropertyName = propertyName;
+ propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ type, effectiveSignalIndex++);
+
+ }
+ }
+ return true;
+}
+
+
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QVector<int> &runtimeFunctionIndices)
+ : QQmlCompilePass(typeCompiler)
+ , enginePrivate(typeCompiler->enginePrivate())
+ , qmlUnit(typeCompiler->qmlUnit())
+ , resolvedTypes(*typeCompiler->resolvedTypes())
+ , propertyCaches(typeCompiler->propertyCaches())
+ , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent())
+ , customParserData(typeCompiler->customParserData())
+ , runtimeFunctionIndices(runtimeFunctionIndices)
+{
+}
+
+bool QQmlPropertyValidator::validate()
+{
+ if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0))
+ return false;
+ compiler->setCustomParserBindings(customParserBindings);
+ return true;
+}
+
+const QQmlImports &QQmlPropertyValidator::imports() const
+{
+ return *compiler->imports();
+}
+
+AST::Node *QQmlPropertyValidator::astForBinding(int scriptIndex) const
+{
+ // ####
+ int reverseIndex = runtimeFunctionIndices.indexOf(scriptIndex);
+ if (reverseIndex == -1)
+ return 0;
+ return compiler->functions().value(reverseIndex).node;
+}
+
+QQmlBinding::Identifier QQmlPropertyValidator::bindingIdentifier(const QV4::CompiledData::Binding *binding, QQmlCustomParser *)
+{
+ int id = customParserBindings.count();
+ customParserBindings.append(binding->value.compiledScriptIndex);
+ return id;
+}
+
+bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding)
+{
+ const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
+
+ if (isComponent(objectIndex)) {
+ 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 true;
+
+ QQmlCustomParser *customParser = 0;
+ QQmlCompiledData::TypeReference *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ if (objectType && objectType->type)
+ customParser = objectType->type->customParser();
+ QList<const QV4::CompiledData::Binding*> customBindings;
+
+ PropertyResolver propertyResolver(propertyCache);
+
+ QString defaultPropertyName;
+ QQmlPropertyData *defaultProperty = 0;
+ if (obj->indexOfDefaultProperty != -1) {
+ QQmlPropertyCache *cache = propertyCache->parent();
+ defaultPropertyName = cache->defaultPropertyName();
+ defaultProperty = cache->defaultProperty();
+ } else {
+ defaultPropertyName = propertyCache->defaultPropertyName();
+ defaultProperty = propertyCache->defaultProperty();
+ }
+
+ const QV4::CompiledData::Binding *binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+
+ if (customParser) {
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
+ customBindings << binding;
+ continue;
+ }
+ } else if ((binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
+ && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
+ customBindings << binding;
+ continue;
+ } else if (binding->type == QV4::CompiledData::Binding::Type_Object
+ || binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+ customBindings << binding;
+ continue;
+ }
+ }
+
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ if (!validateObject(binding->value.objectIndex, binding))
+ return false;
+ // Nothing further to check for attached properties.
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+ continue;
+ }
+
+ // 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;
+
+ QString name = stringAt(binding->propertyNameIndex);
+
+ bool bindingToDefaultProperty = false;
+
+ 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);
+
+ if (notInRevision) {
+ QString typeName = stringAt(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));
+ }
+ }
+ } else {
+ if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty)
+ COMPILE_EXCEPTION(binding, tr("Cannot assign a value directly to a grouped property"));
+
+ pd = defaultProperty;
+ name = defaultPropertyName;
+ bindingToDefaultProperty = true;
+ }
+
+ if (pd) {
+ if (!pd->isWritable()
+ && !pd->isQList()
+ && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
+ && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
+ ) {
+ 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 (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 (customParser) {
+ customBindings << binding;
+ 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 (customParser && !customBindings.isEmpty()) {
+ customParser->clearErrors();
+ customParser->compiler = this;
+ QByteArray data = customParser->compile(qmlUnit, customBindings);
+ customParser->compiler = 0;
+ customParserData->insert(objectIndex, data);
+ const QList<QQmlError> parserErrors = customParser->errors();
+ if (!parserErrors.isEmpty()) {
+ foreach (QQmlError error, parserErrors)
+ compiler->recordError(error);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding)
+{
+ if (property->isEnum()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
+ return true;
+
+ QString value = binding->valueAsString(&qmlUnit->header);
+ 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->type != QV4::CompiledData::Binding::Type_String) {
+ recordError(binding->valueLocation, tr("Invalid property assignment: string expected"));
+ return false;
+ }
+ }
+ break;
+ case QVariant::StringList: {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ 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->header), &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->header), &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->header), &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->header), &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->header), &ok).toPoint();
+ 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->header), &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->header), &ok).toSize();
+ 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->header), &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->header), &ok).toRect();
+ 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->header), &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::Vector3D: {
+ struct {
+ float xp;
+ float yp;
+ float zy;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(&qmlUnit->header), &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->header), &vec, sizeof(vec))) {
+ recordError(binding->valueLocation, tr("Invalid property assignment: 4D vector 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: real or array of reals 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;
+ }
+ break;
+ } else if (property->propType == qMetaTypeId<QList<QString> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ 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->location, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType))));
+ return false;
+ }
+ }
+ break;
+ }
+ return true;
+}
+
+bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding)
+{
+ 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 (isComponent(binding->value.objectIndex))
+ 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()) {
+ // ### TODO: list error handling
+ 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 {
+ // 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.value(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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
new file mode 100644
index 0000000000..2e97d13481
--- /dev/null
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QQMLTYPECOMPILER_P_H
+#define QQMLTYPECOMPILER_P_H
+
+#include <qglobal.h>
+#include <qqmlerror.h>
+#include <qhash.h>
+#include <private/qqmlcompiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEnginePrivate;
+class QQmlCompiledData;
+class QQmlError;
+class QQmlTypeData;
+class QQmlImports;
+
+namespace QtQml {
+struct ParsedQML;
+}
+
+namespace QV4 {
+namespace CompiledData {
+struct QmlUnit;
+struct Location;
+}
+}
+
+struct QQmlTypeCompiler
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
+public:
+ QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QtQml::ParsedQML *parsedQML);
+
+ bool compile();
+
+ QList<QQmlError> compilationErrors() const { return errors; }
+ void recordError(const QQmlError &error);
+
+ QString stringAt(int idx) const;
+ int registerString(const QString &str);
+
+ const QV4::CompiledData::QmlUnit *qmlUnit() const;
+
+ QQmlEnginePrivate *enginePrivate() const { return engine; }
+ const QQmlImports *imports() const;
+ QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes();
+ QList<QtQml::QmlObject*> *qmlObjects();
+ 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, QByteArray> *customParserData();
+ QQmlJS::MemoryPool *memoryPool();
+ const QList<CompiledFunctionOrExpression> &functions() const;
+ void setCustomParserBindings(const QVector<int> &bindings);
+
+private:
+ QList<QQmlError> errors;
+ QQmlEnginePrivate *engine;
+ QQmlCompiledData *compiledData;
+ QQmlTypeData *typeData;
+ QtQml::ParsedQML *parsedQML;
+};
+
+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);
+
+ 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 QtQml::QmlObject *obj, QQmlPropertyCache *baseTypeCache);
+
+ QQmlEnginePrivate *enginePrivate;
+ const QList<QtQml::QmlObject*> &qmlObjects;
+ const QQmlImports *imports;
+ QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes;
+ QVector<QByteArray> vmeMetaObjects;
+ QVector<QQmlPropertyCache*> propertyCaches;
+};
+
+// ### This will go away when the codegen resolves all enums to constant expressions
+// and we replace the constant expression with a literal binding instead of using
+// a script.
+class QQmlEnumTypeResolver : public QQmlCompilePass
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlEnumTypeResolver)
+public:
+ QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler);
+
+ bool resolveEnumBindings();
+
+private:
+ bool tryQualifiedEnumAssignment(const QmlObject *obj, const QQmlPropertyCache *propertyCache,
+ const QQmlPropertyData *prop,
+ QtQml::Binding *binding);
+ int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const;
+
+
+ const QList<QtQml::QmlObject*> &qmlObjects;
+ const QVector<QQmlPropertyCache *> propertyCaches;
+ const QQmlImports *imports;
+ QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes;
+};
+
+// Annotate properties bound to aliases with a flag
+class QQmlAliasAnnotator : public QQmlCompilePass
+{
+public:
+ QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler);
+
+ void annotateBindingsToAliases();
+private:
+ const QList<QtQml::QmlObject*> &qmlObjects;
+ const QVector<QQmlPropertyCache *> propertyCaches;
+};
+
+class QQmlComponentAndAliasResolver : public QQmlCompilePass
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver)
+public:
+ QQmlComponentAndAliasResolver(QQmlTypeCompiler *typeCompiler);
+
+ bool resolve();
+
+protected:
+ void findAndRegisterImplicitComponents(const QtQml::QmlObject *obj, int objectIndex);
+ bool collectIdsAndAliases(int objectIndex);
+ bool resolveAliases();
+
+ QQmlEnginePrivate *enginePrivate;
+ QQmlJS::MemoryPool *pool;
+
+ QList<QtQml::QmlObject*> *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<int> componentBoundaries;
+
+ int _componentIndex;
+ QHash<int, int> _idToObjectIndex;
+ QHash<int, int> *_objectIndexToIdInScope;
+ QList<int> _objectsWithAliases;
+
+ QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes;
+ const QVector<QQmlPropertyCache *> propertyCaches;
+ QVector<QByteArray> *vmeMetaObjectData;
+ QHash<int, int> *objectIndexToIdForRoot;
+ QHash<int, QHash<int, int> > *objectIndexToIdPerComponent;
+};
+
+class QQmlPropertyValidator : public QQmlCompilePass, public QQmlCustomParserCompilerBackend
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
+public:
+ QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QVector<int> &runtimeFunctionIndices);
+
+ bool validate();
+
+ // Re-implemented for QQmlCustomParser
+ virtual const QQmlImports &imports() const;
+ virtual QQmlJS::AST::Node *astForBinding(int scriptIndex) const;
+ virtual QQmlBinding::Identifier bindingIdentifier(const QV4::CompiledData::Binding *binding, QQmlCustomParser *parser);
+
+private:
+ bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding);
+ bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
+ bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding);
+
+ bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
+
+ QQmlEnginePrivate *enginePrivate;
+ const QV4::CompiledData::QmlUnit *qmlUnit;
+ const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ const QVector<QQmlPropertyCache *> &propertyCaches;
+ const QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
+ QHash<int, QByteArray> *customParserData;
+ QVector<int> customParserBindings;
+ const QVector<int> &runtimeFunctionIndices;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTYPECOMPILER_P_H
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index ce0c7abf9e..aa8db06fd5 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -67,15 +67,15 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
assert(!runtimeStrings);
assert(data);
- runtimeStrings = (QV4::SafeString *)malloc(data->stringTableSize * sizeof(QV4::SafeString));
+ runtimeStrings = (QV4::StringValue *)malloc(data->stringTableSize * sizeof(QV4::StringValue));
// memset the strings to 0 in case a GC run happens while we're within the loop below
- memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::SafeString));
+ memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::StringValue));
for (uint i = 0; i < data->stringTableSize; ++i)
runtimeStrings[i] = engine->newIdentifier(data->stringAt(i));
- runtimeRegularExpressions = new QV4::SafeValue[data->regexpTableSize];
+ runtimeRegularExpressions = new QV4::Value[data->regexpTableSize];
// memset the regexps to 0 in case a GC run happens while we're within the loop below
- memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::SafeValue));
+ memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
int flags = 0;
@@ -95,18 +95,25 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup *l = runtimeLookups + i;
- if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_Getter)
+ Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags);
+ if (type == CompiledData::Lookup::Type_Getter)
l->getter = QV4::Lookup::getterGeneric;
- else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_Setter)
+ else if (type == CompiledData::Lookup::Type_Setter)
l->setter = QV4::Lookup::setterGeneric;
- else if (compiledLookups[i].type_and_flags == CompiledData::Lookup::Type_GlobalGetter)
+ else if (type == CompiledData::Lookup::Type_GlobalGetter)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
+ else if (type == CompiledData::Lookup::Type_IndexedGetter)
+ l->indexedGetter = QV4::Lookup::indexedGetterGeneric;
+ else if (type == CompiledData::Lookup::Type_IndexedSetter)
+ l->indexedSetter = QV4::Lookup::indexedSetterGeneric;
for (int j = 0; j < QV4::Lookup::Size; ++j)
l->classList[j] = 0;
l->level = -1;
l->index = UINT_MAX;
l->name = runtimeStrings[compiledLookups[i].nameIndex].asString();
+ if (type == CompiledData::Lookup::Type_IndexedGetter || type == CompiledData::Lookup::Type_IndexedSetter)
+ l->engine = engine;
}
}
@@ -193,6 +200,57 @@ QString Binding::valueAsString(const Unit *unit) const
return QString();
}
+//reverse of Lexer::singleEscape()
+static QString escapedString(const QString &string)
+{
+ QString tmp = QLatin1String("\"");
+ for (int i = 0; i < string.length(); ++i) {
+ const QChar &c = string.at(i);
+ switch (c.unicode()) {
+ case 0x08:
+ tmp += QLatin1String("\\b");
+ break;
+ case 0x09:
+ tmp += QLatin1String("\\t");
+ break;
+ case 0x0A:
+ tmp += QLatin1String("\\n");
+ break;
+ case 0x0B:
+ tmp += QLatin1String("\\v");
+ break;
+ case 0x0C:
+ tmp += QLatin1String("\\f");
+ break;
+ case 0x0D:
+ tmp += QLatin1String("\\r");
+ break;
+ case 0x22:
+ tmp += QLatin1String("\\\"");
+ break;
+ case 0x27:
+ tmp += QLatin1String("\\\'");
+ break;
+ case 0x5C:
+ tmp += QLatin1String("\\\\");
+ break;
+ default:
+ tmp += c;
+ break;
+ }
+ }
+ tmp += QLatin1Char('\"');
+ return tmp;
+}
+
+QString Binding::valueAsScriptString(const Unit *unit) const
+{
+ if (type == Type_String)
+ return escapedString(unit->stringAt(stringIndex));
+ else
+ return valueAsString(unit);
+}
+
}
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 90f27d5f57..94ea4bdc90 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -45,7 +45,7 @@
#include <QVector>
#include <QStringList>
#include <QHash>
-#include <private/qv4value_def_p.h>
+#include <private/qv4value_p.h>
#include <private/qv4executableallocator_p.h>
QT_BEGIN_NAMESPACE
@@ -70,17 +70,32 @@ struct RegExp;
struct Location
{
- int line;
- int column;
+ qint32 line;
+ qint32 column;
+ inline bool operator<(const Location &other) const {
+ return line < other.line ||
+ (line == other.line && column < other.column);
+ }
+};
+
+struct TypeReference
+{
+ TypeReference(const Location &loc)
+ : location(loc)
+ , needsCreation(false)
+ {}
+ Location location; // first use
+ bool needsCreation; // whether the type needs to be creatable or not
};
// map from name index to location of first use
-struct TypeReferenceMap : QHash<int, Location>
+struct TypeReferenceMap : QHash<int, TypeReference>
{
- void add(int nameIndex, const Location &loc) {
- if (contains(nameIndex))
- return;
- insert(nameIndex, loc);
+ TypeReference &add(int nameIndex, const Location &loc) {
+ Iterator it = find(nameIndex);
+ if (it != end())
+ return *it;
+ return *insert(nameIndex, loc);
}
};
@@ -102,7 +117,9 @@ struct Lookup
enum Type {
Type_Getter = 0x0,
Type_Setter = 0x1,
- Type_GlobalGetter = 2
+ Type_GlobalGetter = 2,
+ Type_IndexedGetter = 3,
+ Type_IndexedSetter = 4
};
quint32 type_and_flags;
@@ -188,8 +205,8 @@ struct Unit
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
}
- const QV4::SafeValue *constants() const {
- return reinterpret_cast<const QV4::SafeValue*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
+ const QV4::Value *constants() const {
+ return reinterpret_cast<const QV4::Value*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
}
const JSClassMember *jsClassAt(int idx, int *nMembers) const {
@@ -264,7 +281,7 @@ struct Function
// Qml data structures
-struct Binding
+struct Q_QML_EXPORT Binding
{
quint32 propertyNameIndex;
@@ -280,7 +297,13 @@ struct Binding
};
enum Flags {
- IsSignalHandlerExpression = 0x1
+ IsSignalHandlerExpression = 0x1,
+ IsSignalHandlerObject = 0x2,
+ IsOnAssignment = 0x4,
+ InitializerForReadOnlyDeclaration = 0x8,
+ IsResolvedEnum = 0x10,
+ IsListItem = 0x20,
+ IsBindingToAlias = 0x40
};
quint32 flags : 16;
@@ -294,8 +317,57 @@ struct Binding
quint32 stringIndex; // Set for Type_String and Type_Script (the latter because of script strings)
Location location;
+ Location valueLocation;
+
+ bool isValueBinding() const
+ {
+ if (type == Type_AttachedProperty
+ || type == Type_GroupProperty)
+ return false;
+ if (flags & IsSignalHandlerExpression
+ || flags & IsSignalHandlerObject)
+ return false;
+ return true;
+ }
+
+ bool isValueBindingNoAlias() const { return isValueBinding() && !(flags & IsBindingToAlias); }
+ bool isValueBindingToAlias() const { return isValueBinding() && (flags & IsBindingToAlias); }
+
+ bool isSignalHandler() const
+ {
+ if (flags & IsSignalHandlerExpression || flags & IsSignalHandlerObject) {
+ Q_ASSERT(!isValueBinding());
+ Q_ASSERT(!isAttachedProperty());
+ Q_ASSERT(!isGroupProperty());
+ return true;
+ }
+ return false;
+ }
+
+ bool isAttachedProperty() const
+ {
+ if (type == Type_AttachedProperty) {
+ Q_ASSERT(!isValueBinding());
+ Q_ASSERT(!isSignalHandler());
+ Q_ASSERT(!isGroupProperty());
+ return true;
+ }
+ return false;
+ }
+
+ bool isGroupProperty() const
+ {
+ if (type == Type_GroupProperty) {
+ Q_ASSERT(!isValueBinding());
+ Q_ASSERT(!isSignalHandler());
+ Q_ASSERT(!isAttachedProperty());
+ return true;
+ }
+ return false;
+ }
QString valueAsString(const Unit *unit) const;
+ QString valueAsScriptString(const Unit *unit) const;
double valueAsNumber() const
{
if (type == Type_Number)
@@ -369,7 +441,7 @@ struct Object
// it will be the name of the attached type.
quint32 inheritedTypeNameIndex;
quint32 idIndex;
- quint32 indexOfDefaultProperty;
+ qint32 indexOfDefaultProperty; // -1 means no default property declared in this object
quint32 nFunctions;
quint32 offsetToFunctions;
quint32 nProperties;
@@ -491,9 +563,9 @@ struct Q_QML_EXPORT CompilationUnit
QString fileName() const { return data->stringAt(data->sourceFileIndex); }
- QV4::SafeString *runtimeStrings; // Array
+ QV4::StringValue *runtimeStrings; // Array
QV4::Lookup *runtimeLookups;
- QV4::SafeValue *runtimeRegularExpressions;
+ QV4::Value *runtimeRegularExpressions;
QV4::InternalClass **runtimeClasses;
QVector<QV4::Function *> runtimeFunctions;
// QVector<QV4::Function *> runtimeFunctionsSortedByAddress;
@@ -516,6 +588,8 @@ protected:
}
+Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
+
QT_END_NAMESPACE
#endif
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 9041b04837..bc47b815f2 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -53,6 +53,8 @@ QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::V4IR::Module *module, in
if (headerSize == -1)
headerSize = sizeof(QV4::CompiledData::Unit);
this->headerSize = headerSize;
+ // Make sure the empty string always gets index 0
+ registerString(QString());
}
int QV4::Compiler::JSUnitGenerator::registerString(const QString &str)
@@ -72,6 +74,24 @@ int QV4::Compiler::JSUnitGenerator::getStringId(const QString &string) const
return stringToId.value(string);
}
+uint QV4::Compiler::JSUnitGenerator::registerIndexedGetterLookup()
+{
+ CompiledData::Lookup l;
+ l.type_and_flags = CompiledData::Lookup::Type_IndexedGetter;
+ l.nameIndex = 0;
+ lookups << l;
+ return lookups.size() - 1;
+}
+
+uint QV4::Compiler::JSUnitGenerator::registerIndexedSetterLookup()
+{
+ CompiledData::Lookup l;
+ l.type_and_flags = CompiledData::Lookup::Type_IndexedSetter;
+ l.nameIndex = 0;
+ lookups << l;
+ return lookups.size() - 1;
+}
+
uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
{
CompiledData::Lookup l;
@@ -81,6 +101,7 @@ uint QV4::Compiler::JSUnitGenerator::registerGetterLookup(const QString &name)
return lookups.size() - 1;
}
+
uint QV4::Compiler::JSUnitGenerator::registerSetterLookup(const QString &name)
{
CompiledData::Lookup l;
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 1596fcb622..6baefae7b7 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -70,6 +70,8 @@ struct Q_QML_EXPORT JSUnitGenerator {
uint registerGetterLookup(const QString &name);
uint registerSetterLookup(const QString &name);
uint registerGlobalGetterLookup(const QString &name);
+ uint registerIndexedGetterLookup();
+ uint registerIndexedSetterLookup();
int registerRegExp(QQmlJS::V4IR::RegExp *regexp);
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 5b5667abb4..e3e227796e 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -43,7 +43,7 @@
#define QV4INSTR_MOTH_P_H
#include <QtCore/qglobal.h>
-#include <private/qv4value_def_p.h>
+#include <private/qv4value_p.h>
#include <private/qv4function_p.h>
#include <private/qv4runtime_p.h>
@@ -51,16 +51,20 @@ QT_BEGIN_NAMESPACE
#define FOR_EACH_MOTH_INSTR(F) \
F(Ret, ret) \
+ F(Debug, debug) \
F(LoadRuntimeString, loadRuntimeString) \
F(LoadRegExp, loadRegExp) \
F(LoadClosure, loadClosure) \
F(Move, move) \
+ F(MoveConst, moveConst) \
F(SwapTemps, swapTemps) \
F(LoadName, loadName) \
F(GetGlobalLookup, getGlobalLookup) \
F(StoreName, storeName) \
F(LoadElement, loadElement) \
+ F(LoadElementLookup, loadElementLookup) \
F(StoreElement, storeElement) \
+ F(StoreElementLookup, storeElementLookup) \
F(LoadProperty, loadProperty) \
F(GetLookup, getLookup) \
F(StoreProperty, storeProperty) \
@@ -103,7 +107,8 @@ QT_BEGIN_NAMESPACE
F(CreateActivationProperty, createActivationProperty) \
F(ConstructGlobalLookup, constructGlobalLookup) \
F(Jump, jump) \
- F(CJump, cjump) \
+ F(JumpEq, jumpEq) \
+ F(JumpNe, jumpNe) \
F(UNot, unot) \
F(UNotBool, unotBool) \
F(UPlus, uplus) \
@@ -117,15 +122,16 @@ QT_BEGIN_NAMESPACE
F(BitAnd, bitAnd) \
F(BitOr, bitOr) \
F(BitXor, bitXor) \
+ F(Shr, shr) \
+ F(Shl, shl) \
F(BitAndConst, bitAndConst) \
F(BitOrConst, bitOrConst) \
F(BitXorConst, bitXorConst) \
+ F(ShrConst, shrConst) \
+ F(ShlConst, shlConst) \
F(Mul, mul) \
F(Sub, sub) \
F(BinopContext, binopContext) \
- F(AddNumberParams, addNumberParams) \
- F(MulNumberParams, mulNumberParams) \
- F(SubNumberParams, subNumberParams) \
F(LoadThis, loadThis) \
F(LoadQmlIdArray, loadQmlIdArray) \
F(LoadQmlImportedScripts, loadQmlImportedScripts) \
@@ -140,11 +146,9 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1)
#ifdef MOTH_THREADED_INTERPRETER
-# define MOTH_INSTR_HEADER void *code; \
- unsigned int breakPoint : 1;
+# define MOTH_INSTR_HEADER void *code;
#else
-# define MOTH_INSTR_HEADER quint8 instructionType; \
- unsigned int breakPoint : 1;
+# define MOTH_INSTR_HEADER quint32 instructionType;
#endif
#define MOTH_INSTR_ENUM(I, FMT) I,
@@ -231,7 +235,11 @@ union Instr
struct instr_ret {
MOTH_INSTR_HEADER
Param result;
- };
+ };
+ struct instr_debug {
+ MOTH_INSTR_HEADER
+ quint32 breakPoint;
+ };
struct instr_loadRuntimeString {
MOTH_INSTR_HEADER
int stringId;
@@ -247,6 +255,11 @@ union Instr
Param source;
Param result;
};
+ struct instr_moveConst {
+ MOTH_INSTR_HEADER
+ QV4::ReturnedValue source;
+ Param result;
+ };
struct instr_swapTemps {
MOTH_INSTR_HEADER
Param left;
@@ -322,12 +335,26 @@ union Instr
Param index;
Param result;
};
+ struct instr_loadElementLookup {
+ MOTH_INSTR_HEADER
+ uint lookup;
+ Param base;
+ Param index;
+ Param result;
+ };
struct instr_storeElement {
MOTH_INSTR_HEADER
Param base;
Param index;
Param source;
};
+ struct instr_storeElementLookup {
+ MOTH_INSTR_HEADER
+ uint lookup;
+ Param base;
+ Param index;
+ Param source;
+ };
struct instr_push {
MOTH_INSTR_HEADER
quint32 value;
@@ -525,13 +552,17 @@ union Instr
};
struct instr_jump {
MOTH_INSTR_HEADER
- ptrdiff_t offset;
+ ptrdiff_t offset;
+ };
+ struct instr_jumpEq {
+ MOTH_INSTR_HEADER
+ ptrdiff_t offset;
+ Param condition;
};
- struct instr_cjump {
+ struct instr_jumpNe {
MOTH_INSTR_HEADER
ptrdiff_t offset;
Param condition;
- bool invert;
};
struct instr_unot {
MOTH_INSTR_HEADER
@@ -604,57 +635,63 @@ union Instr
Param rhs;
Param result;
};
- struct instr_bitAndConst {
+ struct instr_shr {
MOTH_INSTR_HEADER
Param lhs;
- int rhs;
+ Param rhs;
Param result;
};
- struct instr_bitOrConst {
+ struct instr_shl {
+ MOTH_INSTR_HEADER
+ Param lhs;
+ Param rhs;
+ Param result;
+ };
+ struct instr_bitAndConst {
MOTH_INSTR_HEADER
Param lhs;
int rhs;
Param result;
};
- struct instr_bitXorConst {
+ struct instr_bitOrConst {
MOTH_INSTR_HEADER
Param lhs;
int rhs;
Param result;
};
- struct instr_mul {
+ struct instr_bitXorConst {
MOTH_INSTR_HEADER
Param lhs;
- Param rhs;
+ int rhs;
Param result;
};
- struct instr_sub {
+ struct instr_shrConst {
MOTH_INSTR_HEADER
Param lhs;
- Param rhs;
+ int rhs;
Param result;
};
- struct instr_binopContext {
+ struct instr_shlConst {
MOTH_INSTR_HEADER
- QV4::BinOpContext alu;
Param lhs;
- Param rhs;
+ int rhs;
Param result;
};
- struct instr_addNumberParams {
+ struct instr_mul {
MOTH_INSTR_HEADER
Param lhs;
Param rhs;
Param result;
};
- struct instr_mulNumberParams {
+ struct instr_sub {
MOTH_INSTR_HEADER
Param lhs;
Param rhs;
Param result;
};
- struct instr_subNumberParams {
+ struct instr_binopContext {
MOTH_INSTR_HEADER
+ QV4::BinOpContext alu;
Param lhs;
Param rhs;
Param result;
@@ -687,16 +724,20 @@ union Instr
instr_common common;
instr_ret ret;
+ instr_debug debug;
instr_loadRuntimeString loadRuntimeString;
instr_loadRegExp loadRegExp;
instr_move move;
+ instr_moveConst moveConst;
instr_swapTemps swapTemps;
instr_loadClosure loadClosure;
instr_loadName loadName;
instr_getGlobalLookup getGlobalLookup;
instr_storeName storeName;
instr_loadElement loadElement;
+ instr_loadElementLookup loadElementLookup;
instr_storeElement storeElement;
+ instr_storeElementLookup storeElementLookup;
instr_loadProperty loadProperty;
instr_getLookup getLookup;
instr_loadQObjectProperty loadQObjectProperty;
@@ -739,7 +780,8 @@ union Instr
instr_createActivationProperty createActivationProperty;
instr_constructGlobalLookup constructGlobalLookup;
instr_jump jump;
- instr_cjump cjump;
+ instr_jumpEq jumpEq;
+ instr_jumpNe jumpNe;
instr_unot unot;
instr_unotBool unotBool;
instr_uplus uplus;
@@ -753,15 +795,16 @@ union Instr
instr_bitAnd bitAnd;
instr_bitOr bitOr;
instr_bitXor bitXor;
+ instr_shr shr;
+ instr_shl shl;
instr_bitAndConst bitAndConst;
instr_bitOrConst bitOrConst;
instr_bitXorConst bitXorConst;
+ instr_shrConst shrConst;
+ instr_shlConst shlConst;
instr_mul mul;
instr_sub sub;
instr_binopContext binopContext;
- instr_addNumberParams addNumberParams;
- instr_mulNumberParams mulNumberParams;
- instr_subNumberParams subNumberParams;
instr_loadThis loadThis;
instr_loadQmlIdArray loadQmlIdArray;
instr_loadQmlImportedScripts loadQmlImportedScripts;
@@ -782,7 +825,7 @@ struct InstrMeta {
typedef Instr::instr_##FMT DataType; \
static const DataType &data(const Instr &instr) { return instr.FMT; } \
static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \
- };
+ };
FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp
index 0fcd770584..c89b108309 100644
--- a/src/qml/compiler/qv4isel_masm.cpp
+++ b/src/qml/compiler/qv4isel_masm.cpp
@@ -245,12 +245,12 @@ Assembler::Pointer Assembler::loadTempAddress(RegisterID baseReg, V4IR::Temp *t)
case V4IR::Temp::Formal:
case V4IR::Temp::ScopedFormal: {
loadPtr(Address(context, qOffsetOf(ExecutionContext, callData)), baseReg);
- offset = sizeof(CallData) + (t->index - 1) * sizeof(SafeValue);
+ offset = sizeof(CallData) + (t->index - 1) * sizeof(Value);
} break;
case V4IR::Temp::Local:
case V4IR::Temp::ScopedLocal: {
loadPtr(Address(context, qOffsetOf(CallContext, locals)), baseReg);
- offset = t->index * sizeof(SafeValue);
+ offset = t->index * sizeof(Value);
} break;
case V4IR::Temp::StackSlot: {
return stackSlotPointer(t);
@@ -266,7 +266,7 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s
loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), Assembler::ScratchRegister);
loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg);
const int id = _isel->registerString(string);
- return Pointer(reg, id * sizeof(QV4::SafeString));
+ return Pointer(reg, id * sizeof(QV4::StringValue));
}
void Assembler::loadStringRef(RegisterID reg, const QString &string)
@@ -274,7 +274,7 @@ void Assembler::loadStringRef(RegisterID reg, const QString &string)
loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), reg);
loadPtr(Address(reg, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg);
const int id = _isel->registerString(string);
- addPtr(TrustedImmPtr(id * sizeof(QV4::SafeString)), reg);
+ addPtr(TrustedImmPtr(id * sizeof(QV4::StringValue)), reg);
}
template <typename Result, typename Source>
@@ -615,7 +615,7 @@ void InstructionSelection::run(int functionIndex)
const int locals = _as->stackLayout().calculateJSStackFrameSize();
_as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister);
_as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)), Assembler::LocalsRegister);
- _as->addPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister);
+ _as->addPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister);
_as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
int lastLine = -1;
@@ -1015,99 +1015,13 @@ void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *ta
void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target)
{
-#if QT_POINTER_SIZE == 8
- V4IR::Temp *tbase = base->asTemp();
- V4IR::Temp *tindex = index->asTemp();
- if (tbase && tindex &&
- tbase->kind != V4IR::Temp::PhysicalRegister) {
- Assembler::Pointer addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
- _as->load64(addr, Assembler::ScratchRegister);
- _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsManaged_Shift), Assembler::ReturnValueRegister);
- Assembler::Jump notManaged = _as->branch64(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm64(0));
- // check whether we have an object with a simple array
- Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Managed, flags));
- _as->load8(managedType, Assembler::ReturnValueRegister);
- _as->and32(Assembler::TrustedImm32(QV4::Managed::SimpleArray), Assembler::ReturnValueRegister);
- Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
-
- bool needNegativeCheck = false;
- Assembler::Jump fallback, fallback2;
- if (tindex->kind == V4IR::Temp::PhysicalRegister) {
- if (tindex->type == V4IR::SInt32Type) {
- fallback = _as->branch32(Assembler::LessThan, (Assembler::RegisterID)tindex->index, Assembler::TrustedImm32(0));
- _as->move((Assembler::RegisterID) tindex->index, Assembler::ScratchRegister);
- needNegativeCheck = true;
- } else {
- // double, convert and check if it's a int
- fallback2 = _as->branchTruncateDoubleToUint32((Assembler::FPRegisterID) tindex->index, Assembler::ScratchRegister);
- _as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr0);
- fallback = _as->branchDouble(Assembler::DoubleNotEqual, Assembler::FPGpr0, (Assembler::FPRegisterID) tindex->index);
- }
- } else {
- Assembler::Pointer indexAddr = _as->loadTempAddress(Assembler::ReturnValueRegister, tindex);
- _as->load64(indexAddr, Assembler::ScratchRegister);
- _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ReturnValueRegister);
- Assembler::Jump isInteger = _as->branch64(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm64(1));
-
- // other type, convert to double and check if it's a int
- // this check is ok to do even if the type is something else than a double, as
- // that would result in a NaN
- _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ReturnValueRegister);
- _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0);
- fallback2 = _as->branchTruncateDoubleToUint32(Assembler::FPGpr0, Assembler::ScratchRegister);
- _as->convertInt32ToDouble(Assembler::ScratchRegister, Assembler::FPGpr1);
- fallback = _as->branchDouble(Assembler::DoubleNotEqualOrUnordered, Assembler::FPGpr0, Assembler::FPGpr1);
-
- isInteger.link(_as);
- _as->or32(Assembler::TrustedImm32(0), Assembler::ScratchRegister);
- needNegativeCheck = true;
- }
-
- // get data, ScratchRegister holds index
- addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
- _as->load64(addr, Assembler::ReturnValueRegister);
- Address arrayDataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayDataLen));
- Assembler::Jump outOfRange;
- if (needNegativeCheck)
- outOfRange = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
- Assembler::Jump outOfRange2 = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, arrayDataLen);
- Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData));
- _as->load64(arrayData, Assembler::ReturnValueRegister);
- Q_ASSERT(sizeof(Property) == (1<<4));
- _as->lshift64(Assembler::TrustedImm32(4), Assembler::ScratchRegister);
- _as->add64(Assembler::ReturnValueRegister, Assembler::ScratchRegister);
- Address value(Assembler::ScratchRegister, qOffsetOf(Property, value));
- _as->load64(value, Assembler::ReturnValueRegister);
-
- // check that the value is not empty
- _as->move(Assembler::ReturnValueRegister, Assembler::ScratchRegister);
- _as->urshift64(Assembler::TrustedImm32(32), Assembler::ScratchRegister);
- Assembler::Jump emptyValue = _as->branch32(Assembler::Equal, Assembler::TrustedImm32(QV4::Value::Empty_Type), Assembler::ScratchRegister);
- _as->storeReturnValue(target);
-
- Assembler::Jump done = _as->jump();
-
- emptyValue.link(_as);
- if (outOfRange.isSet())
- outOfRange.link(_as);
- outOfRange2.link(_as);
- if (fallback.isSet())
- fallback.link(_as);
- if (fallback2.isSet())
- fallback2.link(_as);
- notSimple.link(_as);
- notManaged.link(_as);
-
- generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister,
- Assembler::PointerToValue(base), Assembler::PointerToValue(index));
-
- done.link(_as);
+ if (useFastLookups) {
+ uint lookup = registerIndexedGetterLookup();
+ generateLookupCall(target, lookup, qOffsetOf(QV4::Lookup, indexedGetter),
+ Assembler::PointerToValue(base),
+ Assembler::PointerToValue(index));
return;
}
-#endif
generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister,
Assembler::PointerToValue(base), Assembler::PointerToValue(index));
@@ -1115,6 +1029,13 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
void InstructionSelection::setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex)
{
+ if (useFastLookups) {
+ uint lookup = registerIndexedSetterLookup();
+ generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter),
+ Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
+ Assembler::PointerToValue(source));
+ return;
+ }
generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister,
Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
Assembler::PointerToValue(source));
@@ -2010,7 +1931,7 @@ void InstructionSelection::visitRet(V4IR::Ret *s)
_as->exceptionReturnLabel = _as->label();
const int locals = _as->stackLayout().calculateJSStackFrameSize();
- _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::SafeValue)*locals), Assembler::LocalsRegister);
+ _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister);
_as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister);
_as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h
index 7dfe6a820a..76fe6c6391 100644
--- a/src/qml/compiler/qv4isel_masm_p.h
+++ b/src/qml/compiler/qv4isel_masm_p.h
@@ -45,7 +45,7 @@
#include "qv4jsir_p.h"
#include "qv4isel_p.h"
#include "qv4isel_util_p.h"
-#include "private/qv4value_def_p.h"
+#include "private/qv4value_p.h"
#include "private/qv4lookup_p.h"
#include <QtCore/QHash>
@@ -351,7 +351,7 @@ public:
// space for the callee saved registers
int frameSize = RegisterSize * calleeSavedRegisterCount;
- frameSize += savedRegCount * sizeof(QV4::SafeValue); // these get written out as Values, not as native registers
+ frameSize += savedRegCount * sizeof(QV4::Value); // these get written out as Values, not as native registers
frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise);
frameSize -= stackSpaceAllocatedOtherwise;
@@ -361,8 +361,8 @@ public:
int calculateJSStackFrameSize() const
{
- const int locals = (localCount + sizeof(QV4::CallData)/sizeof(QV4::SafeValue) - 1 + maxOutgoingArgumentCount) + 1;
- int frameSize = locals * sizeof(QV4::SafeValue);
+ const int locals = (localCount + sizeof(QV4::CallData)/sizeof(QV4::Value) - 1 + maxOutgoingArgumentCount) + 1;
+ int frameSize = locals * sizeof(QV4::Value);
return frameSize;
}
@@ -372,7 +372,7 @@ public:
Q_ASSERT(idx < localCount);
Pointer addr = callDataAddress(0);
- addr.offset -= sizeof(QV4::SafeValue) * (idx + 1);
+ addr.offset -= sizeof(QV4::Value) * (idx + 1);
return addr;
}
@@ -384,11 +384,11 @@ public:
Q_ASSERT(argument < maxOutgoingArgumentCount);
const int index = maxOutgoingArgumentCount - argument;
- return Pointer(Assembler::LocalsRegister, sizeof(QV4::SafeValue) * (-index));
+ return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index));
}
Pointer callDataAddress(int offset = 0) const {
- return Pointer(Assembler::LocalsRegister, -(sizeof(QV4::CallData) + sizeof(QV4::SafeValue) * (maxOutgoingArgumentCount - 1)) + offset);
+ return Pointer(Assembler::LocalsRegister, -(sizeof(QV4::CallData) + sizeof(QV4::Value) * (maxOutgoingArgumentCount - 1)) + offset);
}
Address savedRegPointer(int offset) const
@@ -396,7 +396,7 @@ public:
Q_ASSERT(offset >= 0);
Q_ASSERT(offset < savedRegCount);
- const int off = offset * sizeof(QV4::SafeValue);
+ const int off = offset * sizeof(QV4::Value);
return Address(Assembler::StackFrameRegister, - calleeSavedRegisterSpace() - off);
}
@@ -657,7 +657,7 @@ public:
void storeUInt32ReturnValue(RegisterID dest)
{
- Pointer tmp(StackPointerRegister, -int(sizeof(QV4::SafeValue)));
+ Pointer tmp(StackPointerRegister, -int(sizeof(QV4::Value)));
storeReturnValue(tmp);
toUInt32Register(tmp, dest);
}
@@ -669,7 +669,7 @@ public:
xor64(ScratchRegister, ReturnValueRegister);
move64ToDouble(ReturnValueRegister, dest);
#else
- Pointer tmp(StackPointerRegister, -int(sizeof(QV4::SafeValue)));
+ Pointer tmp(StackPointerRegister, -int(sizeof(QV4::Value)));
storeReturnValue(tmp);
loadDouble(tmp, dest);
#endif
@@ -1584,8 +1584,8 @@ private:
int prepareVariableArguments(V4IR::ExprList* args);
int prepareCallData(V4IR::ExprList* args, V4IR::Expr *thisObject);
- template <typename Retval, typename Arg1, typename Arg2>
- void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2)
+ template <typename Retval, typename Arg1, typename Arg2, typename Arg3>
+ void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3)
{
Assembler::RegisterID lookupRegister;
#if CPU(ARM)
@@ -1599,13 +1599,13 @@ private:
getterSetter.offset += getterSetterOffset;
_as->generateFunctionCallImp(retval, "lookup getter/setter",
- RelativeCall(getterSetter), lookupAddr, arg1, arg2);
+ RelativeCall(getterSetter), lookupAddr, arg1, arg2, arg3);
}
- template <typename Arg1>
- void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1)
+ template <typename Retval, typename Arg1, typename Arg2>
+ void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2)
{
- generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType());
+ generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, Assembler::VoidType());
}
/// This is a temporary method, and will be removed when registers are fully supported.
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index bf9af178fe..130725f3ed 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -47,6 +47,7 @@
#include <private/qv4function_p.h>
#include <private/qv4regexpobject_p.h>
#include <private/qv4compileddata_p.h>
+#include <private/qqmlengine_p.h>
#undef USE_TYPE_INFO
@@ -150,6 +151,167 @@ inline bool isBoolType(V4IR::Expr *e)
return (e->type == V4IR::BoolType);
}
+/*
+ * stack slot allocation:
+ *
+ * foreach bb do
+ * foreach stmt do
+ * if the current statement is not a phi-node:
+ * purge ranges that end before the current statement
+ * check for life ranges to activate, and if they don't have a stackslot associated then allocate one
+ * renumber temps to stack
+ * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated
+ * if it's a jump:
+ * foreach phi node in the successor:
+ * allocate slots for each temp (both sources and targets) if they don't have one allocated already
+ * insert moves before the jump
+ */
+class AllocateStackSlots: protected ConvertTemps
+{
+ const QVector<V4IR::LifeTimeInterval> _intervals;
+ QVector<const V4IR::LifeTimeInterval *> _unhandled;
+ QVector<const V4IR::LifeTimeInterval *> _live;
+ QBitArray _slotIsInUse;
+ V4IR::Function *_function;
+
+public:
+ AllocateStackSlots(const QVector<V4IR::LifeTimeInterval> &intervals)
+ : _intervals(intervals)
+ , _slotIsInUse(intervals.size(), false)
+ , _function(0)
+ {
+ _live.reserve(8);
+ _unhandled.reserve(_intervals.size());
+ for (int i = _intervals.size() - 1; i >= 0; --i)
+ _unhandled.append(&_intervals.at(i));
+ }
+
+ void forFunction(V4IR::Function *function)
+ {
+ V4IR::Optimizer::showMeTheCode(function);
+ _function = function;
+ toStackSlots(function);
+
+// QTextStream os(stdout, QIODevice::WriteOnly);
+// os << "Frame layout:" << endl;
+// foreach (int t, _stackSlotForTemp.keys()) {
+// os << "\t" << t << " -> " << _stackSlotForTemp[t] << endl;
+// }
+ }
+
+protected:
+ virtual int allocateFreeSlot()
+ {
+ for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) {
+ if (!_slotIsInUse[i]) {
+ if (_nextUnusedStackSlot <= i) {
+ Q_ASSERT(_nextUnusedStackSlot == i);
+ _nextUnusedStackSlot = i + 1;
+ }
+ _slotIsInUse[i] = true;
+ return i;
+ }
+ }
+
+ Q_UNREACHABLE();
+ return -1;
+ }
+
+ virtual void process(V4IR::Stmt *s)
+ {
+ Q_ASSERT(s->id > 0);
+
+// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id);
+
+ if (V4IR::Phi *phi = s->asPhi()) {
+ visitPhi(phi);
+ } else {
+ // purge ranges no longer alive:
+ for (int i = 0; i < _live.size(); ) {
+ const V4IR::LifeTimeInterval *lti = _live.at(i);
+ if (lti->end() < s->id) {
+// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index];
+ _live.remove(i);
+ Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]);
+ _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false;
+ continue;
+ } else {
+ ++i;
+ }
+ }
+
+ // active new ranges:
+ while (!_unhandled.isEmpty()) {
+ const V4IR::LifeTimeInterval *lti = _unhandled.last();
+ if (lti->start() > s->id)
+ break; // we're done
+ Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index));
+ _stackSlotForTemp[lti->temp().index] = allocateFreeSlot();
+// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index];
+ _live.append(lti);
+ _unhandled.removeLast();
+ }
+
+ s->accept(this);
+ }
+
+ if (V4IR::Jump *jump = s->asJump()) {
+ V4IR::MoveMapping moves;
+ foreach (V4IR::Stmt *succStmt, jump->target->statements) {
+ if (V4IR::Phi *phi = succStmt->asPhi()) {
+ forceActivation(*phi->targetTemp);
+ for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i) {
+ V4IR::Expr *e = phi->d->incoming[i];
+ if (V4IR::Temp *t = e->asTemp()) {
+ forceActivation(*t);
+ }
+ if (jump->target->in[i] == _currentBasicBlock)
+ moves.add(phi->d->incoming[i], phi->targetTemp);
+ }
+ } else {
+ break;
+ }
+ }
+ moves.order();
+ QList<V4IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
+ foreach (V4IR::Move *move, newMoves)
+ move->accept(this);
+ }
+ }
+
+ void forceActivation(const V4IR::Temp &t)
+ {
+ if (_stackSlotForTemp.contains(t.index))
+ return;
+
+ int i = _unhandled.size() - 1;
+ for (; i >= 0; --i) {
+ const V4IR::LifeTimeInterval *lti = _unhandled[i];
+ if (lti->temp() == t) {
+ _live.append(lti);
+ _unhandled.remove(i);
+ break;
+ }
+ }
+ Q_ASSERT(i >= 0); // check that we always found the entry
+
+ _stackSlotForTemp[t.index] = allocateFreeSlot();
+// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index];
+ }
+
+ virtual void visitPhi(V4IR::Phi *phi)
+ {
+ Q_UNUSED(phi);
+#if !defined(QT_NO_DEBUG)
+ Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index));
+ Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]);
+ foreach (V4IR::Expr *e, phi->d->incoming) {
+ if (V4IR::Temp *t = e->asTemp())
+ Q_ASSERT(_stackSlotForTemp.contains(t->index));
+ }
+#endif // defined(QT_NO_DEBUG)
+ }
+};
} // anonymous namespace
InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
@@ -194,10 +356,19 @@ void InstructionSelection::run(int functionIndex)
V4IR::Optimizer opt(_function);
opt.run(qmlEngine);
if (opt.isInSSA()) {
- opt.convertOutOfSSA();
+ static const bool doStackSlotAllocation =
+ qgetenv("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION").isEmpty();
+
+ if (doStackSlotAllocation) {
+ AllocateStackSlots(opt.lifeRanges()).forFunction(_function);
+ } else {
+ opt.convertOutOfSSA();
+ ConvertTemps().toStackSlots(_function);
+ }
opt.showMeTheCode(_function);
+ } else {
+ ConvertTemps().toStackSlots(_function);
}
- ConvertTemps().toStackSlots(_function);
QSet<V4IR::Jump *> removableJumps = opt.calculateOptionalJumps();
qSwap(_removableJumps, removableJumps);
@@ -217,7 +388,13 @@ void InstructionSelection::run(int functionIndex)
QVector<uint> lineNumberMappings;
lineNumberMappings.reserve(_function->basicBlocks.size() * 2);
+ uint currentLine = -1;
for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) {
+ if (irModule->debugMode) {
+ Instruction::Debug debug;
+ debug.breakPoint = 0;
+ addInstruction(debug);
+ }
_block = _function->basicBlocks[i];
_nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0;
_addrs.insert(_block, _codeNext - _codeStart);
@@ -237,8 +414,15 @@ void InstructionSelection::run(int functionIndex)
foreach (V4IR::Stmt *s, _block->statements) {
_currentStatement = s;
- if (s->location.isValid())
+ if (s->location.isValid()) {
lineNumberMappings << _codeNext - _codeStart << s->location.startLine;
+ if (irModule->debugMode && s->location.startLine != currentLine) {
+ Instruction::Debug debug;
+ debug.breakPoint = 0;
+ addInstruction(debug);
+ currentLine = s->location.startLine;
+ }
+ }
s->accept(this);
}
@@ -323,7 +507,7 @@ void InstructionSelection::callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4
void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target)
{
// FIXME: do something more useful with this info
- if (target->type & V4IR::NumberType)
+ if (target->type & V4IR::NumberType && !(source->type & V4IR::NumberType))
unop(V4IR::OpUPlus, source, target);
else
copyValue(source, target);
@@ -428,8 +612,8 @@ void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targe
{
assert(sourceConst);
- Instruction::Move move;
- move.source = getParam(sourceConst);
+ Instruction::MoveConst move;
+ move.source = convertToValue(sourceConst).asReturnedValue();
move.result = getResultParam(targetTemp);
addInstruction(move);
}
@@ -546,6 +730,15 @@ void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyInde
void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target)
{
+ if (useFastLookups) {
+ Instruction::LoadElementLookup load;
+ load.lookup = registerIndexedGetterLookup();
+ load.base = getParam(base);
+ load.index = getParam(index);
+ load.result = getResultParam(target);
+ addInstruction(load);
+ return;
+ }
Instruction::LoadElement load;
load.base = getParam(base);
load.index = getParam(index);
@@ -556,6 +749,15 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
void InstructionSelection::setElement(V4IR::Expr *source, V4IR::Expr *targetBase,
V4IR::Expr *targetIndex)
{
+ if (useFastLookups) {
+ Instruction::StoreElementLookup store;
+ store.lookup = registerIndexedSetterLookup();
+ store.base = getParam(targetBase);
+ store.index = getParam(targetIndex);
+ store.source = getParam(source);
+ addInstruction(store);
+ return;
+ }
Instruction::StoreElement store;
store.base = getParam(targetBase);
store.index = getParam(targetIndex);
@@ -613,7 +815,8 @@ void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::
Instruction::Move move;
move.source = getParam(sourceTemp);
move.result = getResultParam(targetTemp);
- addInstruction(move);
+ if (move.source != move.result)
+ addInstruction(move);
return;
}
Instruction::UPlus uplus;
@@ -664,37 +867,6 @@ void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR:
Param InstructionSelection::binopHelper(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
{
- if (isNumberType(leftSource) && isNumberType(rightSource)) {
- switch (oper) {
- case V4IR::OpAdd: {
- Instruction::AddNumberParams instr;
- instr.lhs = getParam(leftSource);
- instr.rhs = getParam(rightSource);
- instr.result = getResultParam(target);
- addInstruction(instr);
- return instr.result;
- }
- case V4IR::OpMul: {
- Instruction::MulNumberParams instr;
- instr.lhs = getParam(leftSource);
- instr.rhs = getParam(rightSource);
- instr.result = getResultParam(target);
- addInstruction(instr);
- return instr.result;
- }
- case V4IR::OpSub: {
- Instruction::SubNumberParams instr;
- instr.lhs = getParam(leftSource);
- instr.rhs = getParam(rightSource);
- instr.result = getResultParam(target);
- addInstruction(instr);
- return instr.result;
- }
- default:
- break;
- }
- }
-
if (oper == V4IR::OpAdd) {
Instruction::Add add;
add.lhs = getParam(leftSource);
@@ -773,6 +945,38 @@ Param InstructionSelection::binopHelper(V4IR::AluOp oper, V4IR::Expr *leftSource
addInstruction(bitXor);
return bitXor.result;
}
+ if (oper == V4IR::OpRShift) {
+ if (V4IR::Const *c = rightSource->asConst()) {
+ Instruction::ShrConst shr;
+ shr.lhs = getParam(leftSource);
+ shr.rhs = convertToValue(c).Value::toInt32() & 0x1f;
+ shr.result = getResultParam(target);
+ addInstruction(shr);
+ return shr.result;
+ }
+ Instruction::Shr shr;
+ shr.lhs = getParam(leftSource);
+ shr.rhs = getParam(rightSource);
+ shr.result = getResultParam(target);
+ addInstruction(shr);
+ return shr.result;
+ }
+ if (oper == V4IR::OpLShift) {
+ if (V4IR::Const *c = rightSource->asConst()) {
+ Instruction::ShlConst shl;
+ shl.lhs = getParam(leftSource);
+ shl.rhs = convertToValue(c).Value::toInt32() & 0x1f;
+ shl.result = getResultParam(target);
+ addInstruction(shl);
+ return shl.result;
+ }
+ Instruction::Shl shl;
+ shl.lhs = getParam(leftSource);
+ shl.rhs = getParam(rightSource);
+ shl.result = getResultParam(target);
+ addInstruction(shl);
+ return shl.result;
+ }
if (oper == V4IR::OpInstanceof || oper == V4IR::OpIn || oper == V4IR::OpAdd) {
Instruction::BinopContext binop;
@@ -810,10 +1014,17 @@ void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, qui
// We need to move all the temps into the function arg array
assert(argLocation >= 0);
while (e) {
- Instruction::Move move;
- move.source = getParam(e->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
+ if (V4IR::Const *c = e->expr->asConst()) {
+ Instruction::MoveConst move;
+ move.source = convertToValue(c).asReturnedValue();
+ move.result = Param::createTemp(argLocation);
+ addInstruction(move);
+ } else {
+ Instruction::Move move;
+ move.source = getParam(e->expr);
+ move.result = Param::createTemp(argLocation);
+ addInstruction(move);
+ }
++argLocation;
++argc;
e = e->next;
@@ -846,16 +1057,16 @@ void InstructionSelection::visitCJump(V4IR::CJump *s)
Q_UNIMPLEMENTED();
}
- Instruction::CJump jump;
- jump.offset = 0;
- jump.condition = condition;
-
if (s->iftrue == _nextBlock) {
- jump.invert = true;
+ Instruction::JumpNe jump;
+ jump.offset = 0;
+ jump.condition = condition;
ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
_patches[s->iffalse].append(falseLoc);
} else {
- jump.invert = false;
+ Instruction::JumpEq jump;
+ jump.offset = 0;
+ jump.condition = condition;
ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
_patches[s->iftrue].append(trueLoc);
@@ -959,9 +1170,8 @@ void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp
void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result)
{
- Instruction::Move move;
- int idx = jsUnitGenerator()->registerConstant(QV4::Encode(false));
- move.source = Param::createConstant(idx);
+ Instruction::MoveConst move;
+ move.source = QV4::Encode(false);
move.result = getResultParam(result);
addInstruction(move);
}
@@ -1082,10 +1292,17 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4
bool isData = it->expr->asConst()->value;
it = it->next;
- Instruction::Move move;
- move.source = getParam(it->expr);
- move.result = Param::createTemp(argLocation);
- addInstruction(move);
+ if (V4IR::Const *c = it->expr->asConst()) {
+ Instruction::MoveConst move;
+ move.source = convertToValue(c).asReturnedValue();
+ move.result = Param::createTemp(argLocation);
+ addInstruction(move);
+ } else {
+ Instruction::Move move;
+ move.source = getParam(it->expr);
+ move.result = Param::createTemp(argLocation);
+ addInstruction(move);
+ }
++argLocation;
if (!isData) {
@@ -1124,12 +1341,12 @@ void QQmlJS::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
- instr.common.breakPoint = 0;
int instructionSize = Instr::size(type);
if (_codeEnd - _codeNext < instructionSize) {
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
index d8a85ff249..b43868d186 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -46,7 +46,7 @@
#include <private/qv4isel_p.h>
#include <private/qv4isel_util_p.h>
#include <private/qv4jsir_p.h>
-#include <private/qv4value_def_p.h>
+#include <private/qv4value_p.h>
#include "qv4instr_moth_p.h"
QT_BEGIN_NAMESPACE
@@ -160,7 +160,7 @@ private:
int scratchTempIndex() const { return _function->tempCount; }
int callDataStart() const { return scratchTempIndex() + 1; }
- int outgoingArgumentTempStart() const { return callDataStart() + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue); }
+ int outgoingArgumentTempStart() const { return callDataStart() + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); }
int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; }
template <int Instr>
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
index 5ddafc07ab..57222672b9 100644
--- a/src/qml/compiler/qv4isel_p.h
+++ b/src/qml/compiler/qv4isel_p.h
@@ -72,6 +72,8 @@ public:
void setUseFastLookups(bool b) { useFastLookups = b; }
int registerString(const QString &str) { return jsGenerator->registerString(str); }
+ uint registerIndexedGetterLookup() { return jsGenerator->registerIndexedGetterLookup(); }
+ uint registerIndexedSetterLookup() { return jsGenerator->registerIndexedSetterLookup(); }
uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); }
uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); }
uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); }
diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h
index 38ea682d3b..637746dce1 100644
--- a/src/qml/compiler/qv4isel_util_p.h
+++ b/src/qml/compiler/qv4isel_util_p.h
@@ -42,7 +42,7 @@
#ifndef QV4ISEL_UTIL_P_H
#define QV4ISEL_UTIL_P_H
-#include "private/qv4value_def_p.h"
+#include "private/qv4value_p.h"
#include "qv4jsir_p.h"
QT_BEGIN_NAMESPACE
@@ -97,38 +97,52 @@ inline QV4::Primitive convertToValue(V4IR::Const *c)
class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor
{
- int _nextFreeStackSlot;
- QHash<V4IR::Temp, int> _stackSlotForTemp;
-
void renumber(V4IR::Temp *t)
{
if (t->kind != V4IR::Temp::VirtualRegister)
return;
- int stackSlot = _stackSlotForTemp.value(*t, -1);
+ int stackSlot = _stackSlotForTemp.value(t->index, -1);
if (stackSlot == -1) {
- stackSlot = _nextFreeStackSlot++;
- _stackSlotForTemp[*t] = stackSlot;
+ stackSlot = allocateFreeSlot();
+ _stackSlotForTemp[t->index] = stackSlot;
}
t->kind = V4IR::Temp::StackSlot;
t->index = stackSlot;
}
+protected:
+ int _nextUnusedStackSlot;
+ QHash<int, int> _stackSlotForTemp;
+ V4IR::BasicBlock *_currentBasicBlock;
+ virtual int allocateFreeSlot()
+ {
+ return _nextUnusedStackSlot++;
+ }
+
+ virtual void process(V4IR::Stmt *s)
+ {
+ s->accept(this);
+ }
+
public:
ConvertTemps()
- : _nextFreeStackSlot(0)
+ : _nextUnusedStackSlot(0)
+ , _currentBasicBlock(0)
{}
void toStackSlots(V4IR::Function *function)
{
_stackSlotForTemp.reserve(function->tempCount);
- foreach (V4IR::BasicBlock *bb, function->basicBlocks)
+ foreach (V4IR::BasicBlock *bb, function->basicBlocks) {
+ _currentBasicBlock = bb;
foreach (V4IR::Stmt *s, bb->statements)
- s->accept(this);
+ process(s);
+ }
- function->tempCount = _nextFreeStackSlot;
+ function->tempCount = _nextUnusedStackSlot;
}
protected:
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
index deb1af51b4..ea92bb468d 100644
--- a/src/qml/compiler/qv4jsir.cpp
+++ b/src/qml/compiler/qv4jsir.cpp
@@ -740,7 +740,7 @@ Temp *BasicBlock::LOCAL(unsigned index, unsigned scope)
}
Expr *BasicBlock::CONST(Type type, double value)
-{
+{
Const *e = function->New<Const>();
if (type == NumberType) {
int ival = (int)value;
@@ -769,7 +769,7 @@ Expr *BasicBlock::REGEXP(const QString *value, int flags)
}
Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
-{
+{
Name *e = function->New<Name>();
e->init(function->newString(id), line, column);
return e;
@@ -805,7 +805,7 @@ Expr *BasicBlock::CONVERT(Expr *expr, Type type)
}
Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
-{
+{
Unop *e = function->New<Unop>();
e->init(op, expr);
return e;
@@ -819,7 +819,7 @@ Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
}
Expr *BasicBlock::CALL(Expr *base, ExprList *args)
-{
+{
Call *e = function->New<Call>();
e->init(base, args);
int argc = 0;
@@ -851,7 +851,7 @@ Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *prop
}
Stmt *BasicBlock::EXP(Expr *expr)
-{
+{
if (isTerminated())
return 0;
@@ -862,7 +862,7 @@ Stmt *BasicBlock::EXP(Expr *expr)
}
Stmt *BasicBlock::MOVE(Expr *target, Expr *source)
-{
+{
if (isTerminated())
return 0;
@@ -872,7 +872,7 @@ Stmt *BasicBlock::MOVE(Expr *target, Expr *source)
return s;
}
-Stmt *BasicBlock::JUMP(BasicBlock *target)
+Stmt *BasicBlock::JUMP(BasicBlock *target)
{
if (isTerminated())
return 0;
@@ -890,7 +890,7 @@ Stmt *BasicBlock::JUMP(BasicBlock *target)
return s;
}
-Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
+Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
{
if (isTerminated())
return 0;
diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp
index ea2338acad..cc9ce4fc96 100644
--- a/src/qml/compiler/qv4regalloc.cpp
+++ b/src/qml/compiler/qv4regalloc.cpp
@@ -40,7 +40,7 @@
****************************************************************************/
#include "qv4regalloc_p.h"
-#include <private/qv4value_p.h>
+#include <private/qv4value_inl_p.h>
#include <algorithm>
@@ -60,6 +60,9 @@ struct Use {
}
QT_BEGIN_NAMESPACE
+
+Q_DECLARE_TYPEINFO(Use, Q_MOVABLE_TYPE);
+
using namespace QQmlJS::V4IR;
namespace QQmlJS {
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 594c38d109..8f3e186cc7 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -3552,7 +3552,13 @@ private:
for (int i = bb->statements.size() - 1; i >= 0; --i) {
Stmt *s = bb->statements[i];
if (Phi *phi = s->asPhi()) {
- live.remove(*phi->targetTemp);
+ LiveRegs::iterator it = live.find(*phi->targetTemp);
+ if (it == live.end()) {
+ // a phi node target that is only defined, but never used
+ _intervals[*phi->targetTemp].setFrom(s);
+ } else {
+ live.erase(it);
+ }
continue;
}
collector.collect(s);
@@ -3991,15 +3997,21 @@ void MoveMapping::order()
qSwap(_moves, output);
}
-void MoveMapping::insertMoves(BasicBlock *bb, Function *function, bool atEnd) const
+QList<V4IR::Move *> MoveMapping::insertMoves(BasicBlock *bb, Function *function, bool atEnd) const
{
+ QList<V4IR::Move *> newMoves;
+ newMoves.reserve(_moves.size());
+
int insertionPoint = atEnd ? bb->statements.size() - 1 : 0;
foreach (const Move &m, _moves) {
V4IR::Move *move = function->New<V4IR::Move>();
- move->init(m.to, m.from);
+ move->init(clone(m.to, function), clone(m.from, function));
move->swap = m.needsSwap;
bb->statements.insert(insertionPoint++, move);
+ newMoves.append(move);
}
+
+ return newMoves;
}
void MoveMapping::dump() const
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index 2ec81b9577..95bed40a27 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -188,7 +188,7 @@ class MoveMapping
public:
void add(Expr *from, Temp *to);
void order();
- void insertMoves(BasicBlock *bb, Function *function, bool atEnd) const;
+ QList<V4IR::Move *> insertMoves(BasicBlock *bb, Function *function, bool atEnd) const;
void dump() const;