diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2013-09-20 19:30:06 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-20 19:30:06 +0200 |
commit | 60ecb7bfafe85736ab3294a3ba03255f6fdac257 (patch) | |
tree | f69a3c19cf795a94f110623d181fecb187a29169 | |
parent | 59efbaac6838ff27fc56d4dd3f041b86ce44a6da (diff) | |
parent | 57919fe200c0ecbd4cf1873ed2c6c95732c2dd3b (diff) |
Merge "Merge branch 'wip/v4' of qtdeclarative into dev" into refs/staging/dev
43 files changed, 4269 insertions, 189 deletions
diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h index 4e3be7ca4a..a8f54df39f 100644 --- a/src/3rdparty/masm/stubs/ExecutableAllocator.h +++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h @@ -67,7 +67,7 @@ struct ExecutableMemoryHandle : public RefCounted<ExecutableMemoryHandle> { } ~ExecutableMemoryHandle() { - m_allocator->free(m_allocation); + m_allocation->deallocate(m_allocator); } inline void shrink(size_t) { diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index ef1a0cb4d0..3a1af30b88 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -13,7 +13,8 @@ HEADERS += \ $$PWD/qv4isel_moth_p.h \ $$PWD/qv4isel_util_p.h \ $$PWD/qv4ssa_p.h \ - $$PWD/qv4regalloc_p.h + $$PWD/qv4regalloc_p.h \ + $$PWD/qqmlcodegenerator_p.h SOURCES += \ $$PWD/qv4compileddata.cpp \ @@ -24,7 +25,8 @@ SOURCES += \ $$PWD/qv4isel_p.cpp \ $$PWD/qv4jsir.cpp \ $$PWD/qv4ssa.cpp \ - $$PWD/qv4regalloc.cpp + $$PWD/qv4regalloc.cpp \ + $$PWD/qqmlcodegenerator.cpp contains(DEFINES, V4_ENABLE_JIT) { HEADERS += $$PWD/qv4isel_masm_p.h diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp new file mode 100644 index 0000000000..381f5bfec9 --- /dev/null +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -0,0 +1,1375 @@ +/**************************************************************************** +** +** 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 "qqmlcodegenerator_p.h" + +#include <private/qv4compileddata_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmlcompiler_p.h> +#include <QCoreApplication> + +QT_USE_NAMESPACE + +using namespace QtQml; + +#define COMPILE_EXCEPTION(location, desc) \ + { \ + recordError(location, desc); \ + return false; \ + } + +void QmlObject::dump(DebugStream &out) +{ + out << inheritedTypeNameIndex << " {" << endl; + out.indent++; + + out.indent--; + out << "}" << endl; +} + +QStringList Signal::parameterStringList(const QStringList &stringPool) const +{ + QStringList result; + result.reserve(parameters->count); + for (SignalParameter *param = parameters->first; param; param = param->next) + result << stringPool.at(param->nameIndex); + return result; +} + +QQmlCodeGenerator::QQmlCodeGenerator() + : _object(0) + , jsGenerator(0) +{ +} + +bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output) +{ + this->url = url; + AST::UiProgram *program = 0; + { + QQmlJS::Lexer lexer(&output->jsParserEngine); + lexer.setCode(code, /*line = */ 1); + + QQmlJS::Parser parser(&output->jsParserEngine); + + if (! parser.parse() || !parser.diagnosticMessages().isEmpty()) { + + // Extract errors from the parser + foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { + + if (m.isWarning()) { + qWarning("%s:%d : %s", qPrintable(urlString), m.loc.startLine, qPrintable(m.message)); + continue; + } + + QQmlError error; + error.setUrl(url); + error.setDescription(m.message); + error.setLine(m.loc.startLine); + error.setColumn(m.loc.startColumn); + errors << error; + } + return false; + } + program = parser.ast(); + Q_ASSERT(program); + } + + output->code = code; + output->program = program; + + qSwap(_imports, output->imports); + qSwap(_objects, output->objects); + qSwap(_functions, output->functions); + qSwap(_typeReferences, output->typeReferences); + this->pool = output->jsParserEngine.pool(); + this->jsGenerator = &output->jsGenerator; + + emptyStringIndex = registerString(QString()); + + sourceCode = code; + + accept(program->imports); + + if (program->members->next) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser", "Unexpected object definition")); + AST::SourceLocation loc = program->members->next->firstSourceLocation(); + error.setLine(loc.startLine); + error.setColumn(loc.startColumn); + errors << error; + return false; + } + + // Reserve space for pseudo context-scope function + _functions << program; + + AST::UiObjectDefinition *rootObject = AST::cast<AST::UiObjectDefinition*>(program->members->member); + Q_ASSERT(rootObject); + output->indexOfRootObject = defineQMLObject(rootObject); + + collectTypeReferences(); + + qSwap(_imports, output->imports); + qSwap(_objects, output->objects); + qSwap(_functions, output->functions); + qSwap(_typeReferences, output->typeReferences); + return errors.isEmpty(); +} + +bool QQmlCodeGenerator::isSignalPropertyName(const QString &name) +{ + if (name.length() < 3) return false; + if (!name.startsWith(QStringLiteral("on"))) return false; + int ns = name.length(); + for (int i = 2; i < ns; ++i) { + const QChar curr = name.at(i); + if (curr.unicode() == '_') continue; + if (curr.isUpper()) return true; + return false; + } + return false; // consists solely of underscores - invalid. +} + +bool QQmlCodeGenerator::visit(AST::UiArrayMemberList *ast) +{ + return AST::Visitor::visit(ast); +} + +bool QQmlCodeGenerator::visit(AST::UiProgram *) +{ + Q_ASSERT(!"should not happen"); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node) +{ + // The grammar can't distinguish between two different definitions here: + // Item { ... } + // versus + // font { ... } + // The former is a new binding with no property name and "Item" as type name, + // and the latter is a binding to the font property with no type name but + // only initializer. + + AST::UiQualifiedId *lastId = node->qualifiedTypeNameId; + while (lastId->next) + lastId = lastId->next; + bool isType = lastId->name.unicode()->isUpper(); + if (isType) { + int idx = defineQMLObject(node); + appendBinding(AST::SourceLocation(), emptyStringIndex, idx); + } else { + int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer); + appendBinding(node->qualifiedTypeNameId, idx); + } + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node) +{ + int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer); + appendBinding(node->qualifiedId, idx); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiScriptBinding *node) +{ + appendBinding(node->qualifiedId, node->statement); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiArrayBinding *node) +{ + QmlObject *object = 0; + AST::UiQualifiedId *name = node->qualifiedId; + if (!resolveQualifiedId(&name, &object)) + return false; + + qSwap(_object, object); + + AST::UiArrayMemberList *member = node->members; + while (member) { + AST::UiObjectDefinition *def = AST::cast<AST::UiObjectDefinition*>(member->member); + + int idx = defineQMLObject(def); + appendBinding(name->identifierToken, registerString(name->name.toString()), idx, /*isListItem*/ true); + + member = member->next; + } + + qSwap(_object, object); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiImportList *list) +{ + return AST::Visitor::visit(list); +} + +bool QQmlCodeGenerator::visit(AST::UiObjectInitializer *ast) +{ + return AST::Visitor::visit(ast); +} + +bool QQmlCodeGenerator::visit(AST::UiObjectMemberList *ast) +{ + return AST::Visitor::visit(ast); +} + +bool QQmlCodeGenerator::visit(AST::UiParameterList *ast) +{ + return AST::Visitor::visit(ast); +} + +bool QQmlCodeGenerator::visit(AST::UiQualifiedId *id) +{ + return AST::Visitor::visit(id); +} + +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)); + 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) +{ + QmlObject *obj = New<QmlObject>(); + _objects.append(obj); + const int objectIndex = _objects.size() - 1; + qSwap(_object, obj); + + _object->inheritedTypeNameIndex = registerString(asString(qualifiedTypeNameId)); + + 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); + + accept(initializer); + + sanityCheckFunctionNames(); + + qSwap(_propertyNames, propertyNames); + qSwap(_signalNames, signalNames); + qSwap(_object, obj); + return objectIndex; +} + +bool QQmlCodeGenerator::visit(AST::UiImport *node) +{ + QString uri; + QV4::CompiledData::Import *import = New<QV4::CompiledData::Import>(); + + if (!node->fileName.isNull()) { + uri = node->fileName.toString(); + + if (uri.endsWith(QLatin1String(".js"))) { + import->type = QV4::CompiledData::Import::ImportScript; + } else { + import->type = QV4::CompiledData::Import::ImportFile; + } + } else { + import->type = QV4::CompiledData::Import::ImportLibrary; + uri = asString(node->importUri); + } + + import->qualifierIndex = emptyStringIndex; + + // Qualifier + if (!node->importId.isNull()) { + QString qualifier = node->importId.toString(); + if (!qualifier.at(0).isUpper()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + errors << error; + return false; + } + if (qualifier == QLatin1String("Qt")) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + errors << error; + return false; + } + import->qualifierIndex = registerString(qualifier); + + // Check for script qualifier clashes + bool isScript = import->type == QV4::CompiledData::Import::ImportScript; + for (int ii = 0; ii < _imports.count(); ++ii) { + QV4::CompiledData::Import *other = _imports.at(ii); + bool otherIsScript = other->type == QV4::CompiledData::Import::ImportScript; + + if ((isScript || otherIsScript) && qualifier == jsGenerator->strings.at(other->qualifierIndex)) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique.")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + errors << error; + return false; + } + } + + } else if (import->type == QV4::CompiledData::Import::ImportScript) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier")); + error.setLine(node->fileNameToken.startLine); + error.setColumn(node->fileNameToken.startColumn); + errors << error; + return false; + } + + if (node->versionToken.isValid()) { + extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion); + } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version")); + error.setLine(node->importIdToken.startLine); + error.setColumn(node->importIdToken.startColumn); + errors << error; + return false; + } + + import->location.line = node->importIdToken.startLine; + import->location.column = node->importIdToken.startColumn; + + import->uriIndex = registerString(uri); + + _imports.append(import); + + return false; +} + +static QStringList astNodeToStringList(QQmlJS::AST::Node *node) +{ + if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { + QString name = + static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString(); + return QStringList() << name; + } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { + QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node); + + QStringList rv = astNodeToStringList(expr->base); + if (rv.isEmpty()) + return rv; + rv.append(expr->name.toString()); + return rv; + } + return QStringList(); +} + +bool QQmlCodeGenerator::visit(AST::UiPublicMember *node) +{ + static const struct TypeNameToType { + const char *name; + size_t nameLength; + QV4::CompiledData::Property::Type type; + } propTypeNameToTypes[] = { + { "int", strlen("int"), QV4::CompiledData::Property::Int }, + { "bool", strlen("bool"), QV4::CompiledData::Property::Bool }, + { "double", strlen("double"), QV4::CompiledData::Property::Real }, + { "real", strlen("real"), QV4::CompiledData::Property::Real }, + { "string", strlen("string"), QV4::CompiledData::Property::String }, + { "url", strlen("url"), QV4::CompiledData::Property::Url }, + { "color", strlen("color"), QV4::CompiledData::Property::Color }, + // Internally QTime, QDate and QDateTime are all supported. + // To be more consistent with JavaScript we expose only + // QDateTime as it matches closely with the Date JS type. + // We also call it "date" to match. + // { "time", strlen("time"), Property::Time }, + // { "date", strlen("date"), Property::Date }, + { "date", strlen("date"), QV4::CompiledData::Property::DateTime }, + { "rect", strlen("rect"), QV4::CompiledData::Property::Rect }, + { "point", strlen("point"), QV4::CompiledData::Property::Point }, + { "size", strlen("size"), QV4::CompiledData::Property::Size }, + { "font", strlen("font"), QV4::CompiledData::Property::Font }, + { "vector2d", strlen("vector2d"), QV4::CompiledData::Property::Vector2D }, + { "vector3d", strlen("vector3d"), QV4::CompiledData::Property::Vector3D }, + { "vector4d", strlen("vector4d"), QV4::CompiledData::Property::Vector4D }, + { "quaternion", strlen("quaternion"), QV4::CompiledData::Property::Quaternion }, + { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::Property::Matrix4x4 }, + { "variant", strlen("variant"), QV4::CompiledData::Property::Variant }, + { "var", strlen("var"), QV4::CompiledData::Property::Var } + }; + static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) / + sizeof(propTypeNameToTypes[0]); + + if (node->type == AST::UiPublicMember::Signal) { + Signal *signal = New<Signal>(); + QString signalName = node->name.toString(); + signal->nameIndex = registerString(signalName); + + AST::SourceLocation loc = node->firstSourceLocation(); + signal->location.line = loc.startLine; + signal->location.column = loc.startColumn; + + signal->parameters = New<PoolList<SignalParameter> >(); + + AST::UiParameterList *p = node->parameters; + while (p) { + const QStringRef &memberType = p->type; + + if (memberType.isEmpty()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type")); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + errors << error; + return false; + } + + const TypeNameToType *type = 0; + for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { + const TypeNameToType *t = propTypeNameToTypes + typeIndex; + if (t->nameLength == size_t(memberType.length()) && + QHashedString::compare(memberType.constData(), t->name, t->nameLength)) { + type = t; + break; + } + } + + SignalParameter *param = New<SignalParameter>(); + + if (!type) { + if (memberType.at(0).isUpper()) { + // Must be a QML object type. + // Lazily determine type during compilation. + param->type = QV4::CompiledData::Property::Custom; + param->customTypeNameIndex = registerString(p->type.toString()); + } else { + QQmlError error; + QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); + errStr.append(memberType.toString()); + error.setDescription(errStr); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + errors << error; + return false; + } + } else { + // the parameter is a known basic type + param->type = type->type; + param->customTypeNameIndex = emptyStringIndex; + } + + param->nameIndex = registerString(p->name.toString()); + param->location.line = p->identifierToken.startLine; + param->location.column = p->identifierToken.startColumn; + signal->parameters->append(param); + 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 + + _object->qmlSignals->append(signal); + } else { + const QStringRef &memberType = node->memberType; + const QStringRef &name = node->name; + + bool typeFound = false; + QV4::CompiledData::Property::Type type; + + if ((unsigned)memberType.length() == strlen("alias") && + QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) { + type = QV4::CompiledData::Property::Alias; + typeFound = true; + } + + for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { + const TypeNameToType *t = propTypeNameToTypes + ii; + if (t->nameLength == size_t(memberType.length()) && + QHashedString::compare(memberType.constData(), t->name, t->nameLength)) { + type = t->type; + typeFound = true; + } + } + + if (!typeFound && memberType.at(0).isUpper()) { + const QStringRef &typeModifier = node->typeModifier; + + if (typeModifier.isEmpty()) { + type = QV4::CompiledData::Property::Custom; + } else if ((unsigned)typeModifier.length() == strlen("list") && + QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) { + type = QV4::CompiledData::Property::CustomList; + } else { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier")); + error.setLine(node->typeModifierToken.startLine); + error.setColumn(node->typeModifierToken.startColumn); + errors << error; + return false; + } + typeFound = true; + } else if (!node->typeModifier.isNull()) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier")); + error.setLine(node->typeModifierToken.startLine); + error.setColumn(node->typeModifierToken.startColumn); + errors << error; + return false; + } + + if (!typeFound) { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type")); + error.setLine(node->typeToken.startLine); + error.setColumn(node->typeToken.startColumn); + errors << error; + return false; + } + + QmlProperty *property = New<QmlProperty>(); + property->flags = 0; + if (node->isReadonlyMember) + property->flags |= QV4::CompiledData::Property::IsReadOnly; + property->type = type; + if (type >= QV4::CompiledData::Property::Custom) + property->customTypeNameIndex = registerString(memberType.toString()); + else + property->customTypeNameIndex = emptyStringIndex; + + property->nameIndex = registerString(name.toString()); + + AST::SourceLocation loc = node->firstSourceLocation(); + property->location.line = loc.startLine; + property->location.column = loc.startColumn; + + property->aliasPropertyValueIndex = emptyStringIndex; + + if (type == QV4::CompiledData::Property::Alias) { + if (!node->statement && !node->binding) + COMPILE_EXCEPTION(loc, tr("No property alias location")); + + QStringList alias; + if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement*>(node->statement)) + alias = astNodeToStringList(stmt->expression); + + if (node->binding || alias.isEmpty()) + COMPILE_EXCEPTION(loc, tr("Invalid alias location")); + + if (alias.count() < 1 || alias.count() > 3) + COMPILE_EXCEPTION(loc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); + + property->aliasIdValueIndex = registerString(alias.first()); + + QString propertyValue = alias.value(1); + if (alias.count() == 3) { + propertyValue += QLatin1Char('.'); + propertyValue += alias.at(2); + } + property->aliasPropertyValueIndex = registerString(propertyValue); + } else if (node->statement) + appendBinding(node->identifierToken, property->nameIndex, node->statement); + + _object->properties->append(property); + + 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; + } + + // process QML-like initializers (e.g. property Object o: Object {}) + AST::Node::accept(node->binding, this); + } + + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiSourceElement *node) +{ + if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) { + _functions << funDecl; + Function *f = New<Function>(); + f->index = _functions.size() - 1; + _object->functions->append(f); + } else { + QQmlError error; + error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); + error.setLine(node->firstSourceLocation().startLine); + error.setColumn(node->firstSourceLocation().startColumn); + errors << error; + } + return false; +} + +QString QQmlCodeGenerator::asString(AST::UiQualifiedId *node) +{ + QString s; + + for (AST::UiQualifiedId *it = node; it; it = it->next) { + s.append(it->name); + + if (it->next) + s.append(QLatin1Char('.')); + } + + return s; +} + +QStringRef QQmlCodeGenerator::asStringRef(AST::Node *node) +{ + if (!node) + return QStringRef(); + + return textRefAt(node->firstSourceLocation(), node->lastSourceLocation()); +} + +void QQmlCodeGenerator::extractVersion(QStringRef string, int *maj, int *min) +{ + *maj = -1; *min = -1; + + if (!string.isEmpty()) { + + int dot = string.indexOf(QLatin1Char('.')); + + if (dot < 0) { + *maj = string.toString().toInt(); + *min = 0; + } else { + const QString *s = string.string(); + int p = string.position(); + *maj = QStringRef(s, p, dot).toString().toInt(); + *min = QStringRef(s, p + dot + 1, string.size() - dot - 1).toString().toInt(); + } + } +} + +QStringRef QQmlCodeGenerator::textRefAt(const AST::SourceLocation &first, const AST::SourceLocation &last) const +{ + return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset); +} + +void QQmlCodeGenerator::setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement) +{ + binding->type = QV4::CompiledData::Binding::Type_Invalid; + + if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(statement)) { + AST::ExpressionNode *expr = stmt->expression; + if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) { + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = registerString(lit->value.toString()); + } else if (expr->kind == AST::Node::Kind_TrueLiteral) { + binding->type = QV4::CompiledData::Binding::Type_Boolean; + binding->value.b = true; + } else if (expr->kind == AST::Node::Kind_FalseLiteral) { + binding->type = QV4::CompiledData::Binding::Type_Boolean; + binding->value.b = false; + } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) { + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->value.d = lit->value; + } else { + + if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) { + if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) { + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->value.d = -lit->value; + } + } + } + } + + // Do binding instead + if (binding->type == QV4::CompiledData::Binding::Type_Invalid) { + binding->type = QV4::CompiledData::Binding::Type_Script; + _functions << statement; + binding->value.compiledScriptIndex = _functions.size() - 1; + binding->stringIndex = registerString(asStringRef(statement).toString()); + } +} + +void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement *value) +{ + QmlObject *object = 0; + if (!resolveQualifiedId(&name, &object)) + return; + qSwap(_object, object); + appendBinding(name->identifierToken, registerString(name->name.toString()), value); + qSwap(_object, object); +} + +void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex) +{ + QmlObject *object = 0; + if (!resolveQualifiedId(&name, &object)) + return; + qSwap(_object, object); + appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex); + qSwap(_object, object); +} + +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value) +{ + if (!sanityCheckPropertyName(nameLocation, propertyNameIndex)) + return; + + if (stringAt(propertyNameIndex) == QStringLiteral("id")) { + setId(value); + return; + } + + Binding *binding = New<Binding>(); + binding->propertyNameIndex = propertyNameIndex; + binding->location.line = nameLocation.startLine; + binding->location.column = nameLocation.startColumn; + binding->flags = 0; + setBindingValue(binding, value); + _object->bindings->append(binding); +} + +void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem) +{ + if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem)) + return; + + if (stringAt(propertyNameIndex) == QStringLiteral("id")) { + recordError(nameLocation, tr("Invalid component id specification")); + return; + } + + Binding *binding = New<Binding>(); + binding->propertyNameIndex = propertyNameIndex; + binding->location.line = nameLocation.startLine; + binding->location.column = nameLocation.startColumn; + binding->flags = 0; + binding->type = QV4::CompiledData::Binding::Type_Object; + binding->value.objectIndex = objectIndex; + _object->bindings->append(binding); +} + +bool QQmlCodeGenerator::setId(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)) + str = lit->value; + else + node = stmt->expression; + } + + if (str.isEmpty()) + str = asStringRef(node); + + if (str.isEmpty()) + COMPILE_EXCEPTION(loc, tr( "Invalid empty ID")); + + QChar ch = str.at(0); + if (ch.isLetter() && !ch.isLower()) + COMPILE_EXCEPTION(loc, tr( "IDs cannot start with an uppercase letter")); + + QChar u(QLatin1Char('_')); + if (!ch.isLetter() && ch != u) + COMPILE_EXCEPTION(loc, tr( "IDs must start with a letter or underscore")); + + for (int ii = 1; ii < str.count(); ++ii) { + ch = str.at(ii); + if (!ch.isLetterOrNumber() && ch != u) + 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 + + _object->idIndex = registerString(str.toString()); + _object->locationOfIdProperty.line = loc.startLine; + _object->locationOfIdProperty.column = loc.startColumn; + + return true; +} + +bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object) +{ + AST::UiQualifiedId *name = *nameToResolve; + + if (name->name == QStringLiteral("id") && name->next) + COMPILE_EXCEPTION(name->identifierToken, tr( "Invalid use of id property")); + + *object = _object; + while (name->next) { + Binding *binding = New<Binding>(); + binding->propertyNameIndex = registerString(name->name.toString()); + binding->location.line = name->identifierToken.startLine; + binding->location.column = name->identifierToken.startColumn; + binding->flags = 0; + + if (name->name.unicode()->isUpper()) + binding->type = QV4::CompiledData::Binding::Type_AttachedProperty; + else + binding->type = QV4::CompiledData::Binding::Type_GroupProperty; + + int objIndex = defineQMLObject(0, 0); + binding->value.objectIndex = objIndex; + + (*object)->bindings->append(binding); + *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); + } + + 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 + + return true; +} + +void QQmlCodeGenerator::recordError(const AST::SourceLocation &location, const QString &description) +{ + QQmlError error; + error.setUrl(url); + error.setLine(location.startLine); + error.setColumn(location.startColumn); + error.setDescription(description); + errors << error; +} + +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); + } + + 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 (Binding *binding = obj->bindings->first; binding; binding = binding->next) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) + _typeReferences.add(binding->propertyNameIndex, binding->location); + } + } +} + +QQmlScript::LocationSpan QQmlCodeGenerator::location(AST::SourceLocation start, AST::SourceLocation end) +{ + QQmlScript::LocationSpan rv; + rv.start.line = start.startLine; + rv.start.column = start.startColumn; + rv.end.line = end.startLine; + rv.end.column = end.startColumn + end.length - 1; + rv.range.offset = start.offset; + rv.range.length = end.offset + end.length - start.offset; + return rv; +} + +QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output) +{ + jsUnitGenerator = &output.jsGenerator; + const QmlObject *rootObject = output.objects.at(output.indexOfRootObject); + int unitSize = 0; + QV4::CompiledData::Unit *jsUnit = jsUnitGenerator->generateUnit(&unitSize); + + const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); + const int objectOffsetTableSize = output.objects.count() * sizeof(quint32); + + QHash<QmlObject*, quint32> objectOffsets; + + 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); + + int signalTableSize = 0; + for (Signal *s = o->qmlSignals->first; s; s = s->next) + signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count); + + objectsSize += signalTableSize; + } + + const int totalSize = unitSize + importSize + objectOffsetTableSize + objectsSize; + char *data = (char*)malloc(totalSize); + memcpy(data, jsUnit, unitSize); + free(jsUnit); + jsUnit = 0; + + QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data); + qmlUnit->header.flags |= QV4::CompiledData::Unit::IsQml; + qmlUnit->offsetToImports = unitSize; + qmlUnit->nImports = output.imports.count(); + qmlUnit->offsetToObjects = unitSize + importSize; + qmlUnit->nObjects = output.objects.count(); + qmlUnit->indexOfRootObject = output.indexOfRootObject; + + // write imports + char *importPtr = data + qmlUnit->offsetToImports; + foreach (QV4::CompiledData::Import *imp, output.imports) { + QV4::CompiledData::Import *importToWrite = reinterpret_cast<QV4::CompiledData::Import*>(importPtr); + *importToWrite = *imp; + importPtr += sizeof(QV4::CompiledData::Import); + } + + // write objects + quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects); + char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize; + foreach (QmlObject *o, output.objects) { + *objectTable++ = objectOffsets.value(o); + + QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr); + objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex; + objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty; + objectToWrite->idIndex = o->idIndex; + objectToWrite->location = o->location; + objectToWrite->locationOfIdProperty = o->locationOfIdProperty; + + quint32 nextOffset = sizeof(QV4::CompiledData::Object); + + objectToWrite->nFunctions = o->functions->count; + objectToWrite->offsetToFunctions = nextOffset; + nextOffset += objectToWrite->nFunctions * sizeof(quint32); + + objectToWrite->nProperties = o->properties->count; + objectToWrite->offsetToProperties = nextOffset; + nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property); + + objectToWrite->nSignals = o->qmlSignals->count; + objectToWrite->offsetToSignals = nextOffset; + nextOffset += objectToWrite->nSignals * sizeof(quint32); + + objectToWrite->nBindings = o->bindings->count; + 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) + *functionsTable++ = f->index; + + char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties; + for (QmlProperty *p = o->properties->first; 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; + bindingPtr += sizeof(QV4::CompiledData::Binding); + } + + 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) { + *signalOffsetTable++ = signalPtr - objectPtr; + QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr); + + signalToWrite->nameIndex = s->nameIndex; + signalToWrite->location = s->location; + signalToWrite->nParameters = s->parameters->count; + + QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite)); + for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) + *parameterToWrite = *param; + + int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count); + signalTableSize += size; + signalPtr += size; + } + + objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count); + objectPtr += signalTableSize; + } + + return qmlUnit; +} + +int QmlUnitGenerator::getStringId(const QString &str) const +{ + return jsUnitGenerator->getStringId(str); +} + +void JSCodeGen::generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output) +{ + _module = &output->jsModule; + _module->setFileName(fileName); + + QmlScanner scan(this, output->code); + scan.begin(output->program); + foreach (AST::Node *node, output->functions) { + if (node == output->program) + continue; + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node); + + scan.enterEnvironment(node); + scan(function ? function->body : node); + scan.leaveEnvironment(); + } + scan.end(); + + _env = 0; + _function = defineFunction(QString("context scope"), output->program, 0, 0, QmlBinding); + + foreach (AST::Node *node, output->functions) { + if (node == output->program) + continue; + + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node); + + QString name; + if (function) + name = function->name.toString(); + else + name = QStringLiteral("%qml-expression-entry"); + + AST::SourceElements *body; + if (function) + body = function->body ? function->body->elements : 0; + else { + // Synthesize source elements. + QQmlJS::MemoryPool *pool = output->jsParserEngine.pool(); + AST::SourceElement *element = new (pool) AST::StatementSourceElement(static_cast<AST::Statement*>(node)); + body = new (output->jsParserEngine.pool()) AST::SourceElements(element); + body = body->finish(); + } + + defineFunction(name, node, + function ? function->formals : 0, + body, function ? FunctionCode : QmlBinding); + + } + + qDeleteAll(_envMap); + _envMap.clear(); +} + + +void JSCodeGen::QmlScanner::begin(AST::Node *rootNode) +{ + enterEnvironment(0); + enterFunction(rootNode, "context scope", 0, 0, 0, /*isExpression*/false); +} + +void JSCodeGen::QmlScanner::end() +{ + leaveEnvironment(); +} + +SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, ParsedQML *parsedQML, + QQmlCompiledData *unit) + : enginePrivate(enginePrivate) + , parsedQML(parsedQML) + , unit(unit) +{ +} + +bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations() +{ + foreach (QmlObject *obj, parsedQML->objects) { + QString elementName = stringAt(obj->inheritedTypeNameIndex); + if (elementName.isEmpty()) + continue; + QQmlPropertyCache *cache = unit->resolvedTypes[obj->inheritedTypeNameIndex].createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache)) + return false; + } + return true; +} + +bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(QmlObject *obj, const QString &typeName, QQmlPropertyCache *propertyCache) +{ + // 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) { + 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; + const QMetaObject *attachedType = type ? type->attachedPropertiesType() : 0; + if (!attachedType) + COMPILE_EXCEPTION(binding->location, tr("Non-existent attached object")); + QQmlPropertyCache *cache = enginePrivate->cache(attachedType); + if (!convertSignalHandlerExpressionsToFunctionDeclarations(attachedObj, propertyName, cache)) + return false; + continue; + } + + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + + if (!QQmlCodeGenerator::isSignalPropertyName(propertyName)) + continue; + + PropertyResolver resolver(propertyCache); + + Q_ASSERT(propertyName.startsWith(QStringLiteral("on"))); + propertyName.remove(0, 2); + + // Note that the property name could start with any alpha or '_' or '$' character, + // so we need to do the lower-casing of the first alpha character. + for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) { + if (propertyName.at(firstAlphaIndex).isUpper()) { + propertyName[firstAlphaIndex] = propertyName.at(firstAlphaIndex).toLower(); + break; + } + } + + QList<QString> parameters; + + bool notInRevision = false; + QQmlPropertyData *signal = resolver.signal(propertyName, ¬InRevision); + if (signal) { + int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex); + foreach (const QByteArray ¶m, propertyCache->signalParameterNames(sigIndex)) + parameters << QString::fromUtf8(param); + } else { + if (notInRevision) { + // Try assinging it as a property later + if (resolver.property(propertyName, /*notInRevision ptr*/0)) + continue; + + const QString &originalPropertyName = stringAt(binding->propertyNameIndex); + + const QQmlType *type = unit->resolvedTypes.value(obj->inheritedTypeNameIndex).type; + 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 { + COMPILE_EXCEPTION(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName)); + } + } + + // Try to look up the signal parameter names in the object itself + + // build cache if necessary + if (customSignals.isEmpty()) { + for (Signal *signal = obj->qmlSignals->first; signal; signal = signal->next) { + const QString &signalName = stringAt(signal->nameIndex); + customSignals.insert(signalName, signal->parameterStringList(parsedQML->jsGenerator.strings)); + } + } + + QHash<QString, QStringList>::ConstIterator entry = customSignals.find(propertyName); + if (entry == customSignals.constEnd() && propertyName.endsWith(QStringLiteral("Changed"))) { + QString alternateName = propertyName.mid(0, propertyName.length() - strlen("Changed")); + entry = customSignals.find(alternateName); + } + + if (entry == customSignals.constEnd()) { + // Can't find even a custom signal, then just don't do anything and try + // keeping the binding as a regular property assignment. + continue; + } + + parameters = entry.value(); + } + + QQmlJS::Engine &jsEngine = parsedQML->jsParserEngine; + QQmlJS::MemoryPool *pool = jsEngine.pool(); + + AST::FormalParameterList *paramList = 0; + foreach (const QString ¶m, parameters) { + QStringRef paramNameRef = jsEngine.newStringRef(param); + + if (paramList) + paramList = new (pool) AST::FormalParameterList(paramList, paramNameRef); + else + paramList = new (pool) AST::FormalParameterList(paramNameRef); + } + + if (paramList) + paramList = paramList->finish(); + + AST::Statement *statement = static_cast<AST::Statement*>(parsedQML->functions[binding->value.compiledScriptIndex]); + AST::SourceElement *sourceElement = new (pool) AST::StatementSourceElement(statement); + AST::SourceElements *elements = new (pool) AST::SourceElements(sourceElement); + elements = elements->finish(); + + AST::FunctionBody *body = new (pool) AST::FunctionBody(elements); + + AST::FunctionDeclaration *functionDeclaration = new (pool) AST::FunctionDeclaration(jsEngine.newStringRef(propertyName), paramList, body); + + parsedQML->functions[binding->value.compiledScriptIndex] = functionDeclaration; + binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression; + binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName); + } + return true; +} + +void SignalHandlerConverter::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setUrl(unit->url); + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + errors << error; +} + +QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision) +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, 0, 0); + + // Find the first property + while (d && d->isFunction()) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return 0; + } else { + return d; + } +} + + +QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) +{ + if (notInRevision) *notInRevision = false; + + QQmlPropertyData *d = cache->property(name, 0, 0); + if (notInRevision) *notInRevision = false; + + while (d && !(d->isFunction())) + d = cache->overrideData(d); + + if (d && !cache->isAllowedInRevision(d)) { + if (notInRevision) *notInRevision = true; + return 0; + } else if (d && d->isSignal()) { + return d; + } + + if (name.endsWith(QStringLiteral("Changed"))) { + QString propName = name.mid(0, name.length() - strlen("Changed")); + + d = property(propName, notInRevision); + if (d) + return cache->signal(d->notifyIndex); + } + + return 0; +} diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h new file mode 100644 index 0000000000..c3a3a8e4c3 --- /dev/null +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** 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 QQMLCODEGENERATOR_P_H +#define QQMLCODEGENERATOR_P_H + +#include <private/qqmljsast_p.h> +#include <private/qqmlpool_p.h> +#include <private/qqmlscript_p.h> +#include <private/qqmljsengine_p.h> +#include <private/qv4compiler_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qqmljsmemorypool_p.h> +#include <private/qv4codegen_p.h> +#include <private/qv4compiler_p.h> +#include <QTextStream> +#include <QCoreApplication> + +QT_BEGIN_NAMESPACE + +namespace QtQml { + +using namespace QQmlJS; + +struct DebugStream +{ + DebugStream(QTextStream &stream) + : stream(stream) + , indent(0) + {} + + template <typename T> + QTextStream &operator<<(const T &value) + { + return stream << QByteArray(indent * 4, ' ') << value; + } + + QTextStream &noindent() { return stream; } + + QTextStream &stream; + int indent; +}; + +template <typename T> +struct PoolList +{ + PoolList() + : first(0) + , last(0) + , count(0) + {} + + T *first; + T *last; + int count; + + void append(T *item) { + item->next = 0; + if (last) + last->next = item; + else + first = item; + last = item; + ++count; + } +}; + +struct QmlObject; + +struct SignalParameter : public QV4::CompiledData::Parameter +{ + SignalParameter *next; +}; + +struct Signal +{ + int nameIndex; + QV4::CompiledData::Location location; + PoolList<SignalParameter> *parameters; + + QStringList parameterStringList(const QStringList &stringPool) const; + + Signal *next; +}; + +struct QmlProperty : public QV4::CompiledData::Property +{ + QmlProperty *next; +}; + +struct Binding : public QV4::CompiledData::Binding +{ + Binding *next; +}; + +struct Function +{ + int index; + Function *next; +}; + +struct QmlObject +{ + int inheritedTypeNameIndex; + int idIndex; + int indexOfDefaultProperty; + + QV4::CompiledData::Location location; + QV4::CompiledData::Location locationOfIdProperty; + + PoolList<QmlProperty> *properties; + PoolList<Signal> *qmlSignals; + PoolList<Binding> *bindings; + PoolList<Function> *functions; + + void dump(DebugStream &out); +}; + +struct ParsedQML +{ + ParsedQML() + : jsGenerator(&jsModule, sizeof(QV4::CompiledData::QmlUnit)) + {} + QString code; + QQmlJS::Engine jsParserEngine; + V4IR::Module jsModule; + QList<QV4::CompiledData::Import*> imports; + AST::UiProgram *program; + int indexOfRootObject; + QList<QmlObject*> objects; + QList<AST::Node*> functions; // FunctionDeclaration, Statement or Expression + QV4::Compiler::JSUnitGenerator jsGenerator; + + QV4::CompiledData::TypeReferenceMap typeReferences; + + QString stringAt(int index) const { return jsGenerator.strings.value(index); } +}; + +// Doesn't really generate code per-se, but more the data structure +struct Q_QML_EXPORT QQmlCodeGenerator : public AST::Visitor +{ + Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) +public: + QQmlCodeGenerator(); + bool generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output); + + static bool isSignalPropertyName(const QString &name); + + using AST::Visitor::visit; + using AST::Visitor::endVisit; + + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + + 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); } + + static QString asString(AST::UiQualifiedId *node); + QStringRef asStringRef(AST::Node *node); + static void extractVersion(QStringRef string, int *maj, int *min); + QStringRef textRefAt(const AST::SourceLocation &loc) const + { return QStringRef(&sourceCode, loc.offset, loc.length); } + QStringRef textRefAt(const AST::SourceLocation &first, + const AST::SourceLocation &last) const; + static QQmlScript::LocationSpan location(AST::UiQualifiedId *id) + { + return location(id->identifierToken, id->identifierToken); + } + + 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); + + bool setId(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(); } + + QString stringAt(int index) const { return jsGenerator->strings.at(index); } + + QList<QQmlError> errors; + + QList<QV4::CompiledData::Import*> _imports; + QList<QmlObject*> _objects; + QList<AST::Node*> _functions; + + QV4::CompiledData::TypeReferenceMap _typeReferences; + + QmlObject *_object; + QSet<QString> _propertyNames; + QSet<QString> _signalNames; + + QQmlJS::MemoryPool *pool; + QString sourceCode; + QUrl url; + QV4::Compiler::JSUnitGenerator *jsGenerator; + int emptyStringIndex; + bool sanityCheckFunctionNames(); +}; + +struct Q_QML_EXPORT QmlUnitGenerator +{ + QmlUnitGenerator() + : jsUnitGenerator(0) + { + } + + QV4::CompiledData::QmlUnit *generate(ParsedQML &output); + +private: + int getStringId(const QString &str) const; + + QV4::Compiler::JSUnitGenerator *jsUnitGenerator; +}; + +struct PropertyResolver +{ + PropertyResolver(QQmlPropertyCache *cache) + : cache(cache) + {} + + QQmlPropertyData *property(int index) + { + return cache->property(index); + } + + QQmlPropertyData *property(const QString &name, bool *notInRevision = 0); + + // This code must match the semantics of QQmlPropertyPrivate::findSignalByName + QQmlPropertyData *signal(const QString &name, bool *notInRevision); + + QQmlPropertyCache *cache; +}; + +// "Converts" signal expressions to full-fleged function declarations with +// parameters taken from the signal declarations +// It also updates the QV4::CompiledData::Binding objects to set the property name +// to the final signal name (onTextChanged -> textChanged) and sets the IsSignalExpression flag. +struct SignalHandlerConverter +{ + Q_DECLARE_TR_FUNCTIONS(QQmlCodeGenerator) +public: + SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, ParsedQML *parsedQML, + QQmlCompiledData *unit); + + bool convertSignalHandlerExpressionsToFunctionDeclarations(); + + QList<QQmlError> errors; + +private: + bool convertSignalHandlerExpressionsToFunctionDeclarations(QmlObject *obj, const QString &typeName, QQmlPropertyCache *propertyCache); + + const QString &stringAt(int index) const { return parsedQML->jsGenerator.strings.at(index); } + void recordError(const QV4::CompiledData::Location &location, const QString &description); + + QQmlEnginePrivate *enginePrivate; + ParsedQML *parsedQML; + QQmlCompiledData *unit; +}; + +struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen +{ + JSCodeGen() + : QQmlJS::Codegen(/*strict mode*/false) + {} + + void generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output); + +private: + struct QmlScanner : public ScanFunctions + { + QmlScanner(JSCodeGen *cg, const QString &sourceCode) + : ScanFunctions(cg, sourceCode) + , codeGen(cg) + {} + + void begin(AST::Node *rootNode); + void end(); + + JSCodeGen *codeGen; + }; + + V4IR::Module jsModule; +}; + +} // namespace QtQml + +QT_END_NAMESPACE + +#endif // QQMLCODEGENERATOR_P_H diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 4b2c0245a4..303d001246 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1767,6 +1767,10 @@ V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, function->isStrict = _env->isStrict; function->isNamedExpression = _env->isNamedFunctionExpression; + AST::SourceLocation loc = ast->firstSourceLocation(); + function->line = loc.startLine; + function->column = loc.startColumn; + if (function->usesArgumentsObject) _env->enter("arguments", Environment::VariableDeclaration); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 2d2ca55f8d..3d5d92452b 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -435,7 +435,7 @@ protected: QList<QQmlError> _errors; - class ScanFunctions: Visitor + class ScanFunctions: protected Visitor { typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; public: diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 74793d69fd..991fc922c3 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -65,13 +65,7 @@ namespace { CompilationUnit::~CompilationUnit() { - engine->compilationUnits.erase(engine->compilationUnits.find(this)); - free(data); - free(runtimeStrings); - delete [] runtimeLookups; - delete [] runtimeRegularExpressions; - free(runtimeClasses); - qDeleteAll(runtimeFunctions); + unlink(); } QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) @@ -145,6 +139,26 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) return runtimeFunctions[data->indexOfRootFunction]; } +void CompilationUnit::unlink() +{ + if (engine) + engine->compilationUnits.erase(engine->compilationUnits.find(this)); + engine = 0; + if (ownsData) + free(data); + data = 0; + free(runtimeStrings); + runtimeStrings = 0; + delete [] runtimeLookups; + runtimeLookups = 0; + delete [] runtimeRegularExpressions; + runtimeRegularExpressions = 0; + free(runtimeClasses); + runtimeClasses = 0; + qDeleteAll(runtimeFunctions); + runtimeFunctions.clear(); +} + void CompilationUnit::markObjects() { for (int i = 0; i < data->stringTableSize; ++i) @@ -155,6 +169,24 @@ void CompilationUnit::markObjects() runtimeFunctions[i]->mark(); } +QString Binding::valueAsString(const Unit *unit) const +{ + switch (type) { + case Type_Script: + case Type_String: + return unit->stringAt(stringIndex); + case Type_Boolean: + return value.b ? QStringLiteral("true") : QStringLiteral("false"); + case Type_Number: + return QString::number(value.d); + case Type_Invalid: + return QString(); + default: + break; + } + return QString(); +} + } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 235202a832..8481e17857 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -44,6 +44,7 @@ #include <QtCore/qstring.h> #include <QVector> #include <QStringList> +#include <QHash> #include <private/qv4value_def_p.h> #include <private/qv4executableallocator_p.h> @@ -67,6 +68,22 @@ struct Function; struct Lookup; struct RegExp; +struct Location +{ + int line; + int column; +}; + +// map from name index to location of first use +struct TypeReferenceMap : QHash<int, Location> +{ + void add(int nameIndex, const Location &loc) { + if (contains(nameIndex)) + return; + insert(nameIndex, loc); + } +}; + struct RegExp { enum Flags { @@ -178,9 +195,9 @@ struct Unit return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass)); } - static int calculateSize(uint nStrings, uint nFunctions, uint nRegExps, + static int calculateSize(uint headerSize, uint nStrings, uint nFunctions, uint nRegExps, uint nLookups, uint nClasses) { - return (sizeof(Unit) + return (headerSize + (nStrings + nFunctions + nClasses) * sizeof(uint) + nRegExps * RegExp::calculateSize() + nLookups * Lookup::calculateSize() @@ -207,6 +224,7 @@ struct Function quint32 lineNumberMappingOffset; // Array of uint pairs (offset and line number) quint32 nInnerFunctions; quint32 innerFunctionsOffset; + Location location; // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] // quint32 offsetForInnerFunctions[nInnerFunctions] @@ -223,79 +241,197 @@ struct Function // Qml data structures -struct Value +struct Binding { - quint32 type; // Invalid, Boolean, Number, String, Function, Object, ListOfObjects + quint32 propertyNameIndex; + + enum ValueType { + Type_Invalid, + Type_Boolean, + Type_Number, + Type_String, + Type_Script, + Type_Object, + Type_AttachedProperty, + Type_GroupProperty + }; + + enum Flags { + IsSignalHandlerExpression = 0x1 + }; + + quint32 flags : 16; + quint32 type : 16; union { bool b; - int i; double d; - quint32 offsetToString; - quint32 offsetToFunction; - quint32 offsetToObject; - }; -}; + quint32 compiledScriptIndex; // used when Type_Script + quint32 objectIndex; + } value; + quint32 stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) + + Location location; + + QString valueAsString(const Unit *unit) const; + double valueAsNumber() const + { + if (type == Type_Number) + return value.d; + return 0.0; + + } + bool valueAsBoolean() const + { + if (type == Type_Boolean) + return value.b; + return false; + } -struct Binding -{ - quint32 offsetToPropertyName; - Value value; }; struct Parameter { - quint32 offsetToName; + quint32 nameIndex; quint32 type; - quint32 offsetToCustomTypeName; + quint32 customTypeNameIndex; quint32 reserved; + Location location; }; struct Signal { - quint32 offsetToName; + quint32 nameIndex; quint32 nParameters; - Parameter parameters[1]; + Location location; + // Parameter parameters[1]; + + const Parameter *parameterAt(int idx) const { + return reinterpret_cast<const Parameter*>(this + 1) + idx; + } + + static int calculateSize(int nParameters) { + return (sizeof(Signal) + + nParameters * sizeof(Parameter) + + 7) & ~0x7; + } }; struct Property { - quint32 offsetToName; + enum Type { Var = 0, Variant, Int, Bool, Real, String, Url, Color, + Font, Time, Date, DateTime, Rect, Point, Size, + Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion, + Alias, Custom, CustomList }; + + enum Flags { + IsReadOnly = 0x1 + }; + + quint32 nameIndex; quint32 type; - quint32 offsetToCustomTypeName; - quint32 flags; // default, readonly - Value value; + union { + quint32 customTypeNameIndex; // If type >= Custom + quint32 aliasIdValueIndex; // If type == Alias + }; + quint32 aliasPropertyValueIndex; + quint32 flags; // readonly + Location location; }; struct Object { - quint32 offsetToInheritedTypeName; - quint32 offsetToId; - quint32 offsetToDefaultProperty; + // Depending on the use, this may be the type name to instantiate before instantiating this + // object. For grouped properties the type name will be empty and for attached properties + // it will be the name of the attached type. + quint32 inheritedTypeNameIndex; + quint32 idIndex; + quint32 indexOfDefaultProperty; quint32 nFunctions; quint32 offsetToFunctions; quint32 nProperties; quint32 offsetToProperties; quint32 nSignals; - quint32 offsetToSignals; + quint32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects quint32 nBindings; quint32 offsetToBindings; + Location location; + Location locationOfIdProperty; // Function[] // Property[] // Signal[] // Binding[] + + static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nSignals, int nBindings) + { + return ( sizeof(Object) + + nFunctions * sizeof(quint32) + + nProperties * sizeof(Property) + + nSignals * sizeof(quint32) + + nBindings * sizeof(Binding) + + 0x7 + ) & ~0x7; + } + + const quint32 *functionOffsetTable() const + { + return reinterpret_cast<const quint32*>(reinterpret_cast<const char *>(this) + offsetToFunctions); + } + + const Property *propertyTable() const + { + return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties); + } + + const Binding *bindingTable() const + { + return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings); + } + + const Signal *signalAt(int idx) const + { + const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToSignals); + const uint offset = offsetTable[idx]; + return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); + } }; -struct Imports +struct Import { + enum ImportType { + ImportLibrary = 0x1, + ImportFile = 0x2, + ImportScript = 0x3 + }; + quint32 type; + + quint32 uriIndex; + quint32 qualifierIndex; + + qint32 majorVersion; + qint32 minorVersion; + + Location location; }; struct QmlUnit { Unit header; - int offsetToTypeName; - Imports imports; - Object object; + quint32 nImports; + quint32 offsetToImports; + quint32 nObjects; + quint32 offsetToObjects; + quint32 indexOfRootObject; + + const Import *importAt(int idx) const { + return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import)); + } + + const Object *objectAt(int idx) const { + const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToObjects); + const uint offset = offsetTable[idx]; + return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset); + } }; // This is how this hooks into the existing structures: @@ -304,14 +440,16 @@ struct QmlUnit // CompilationUnit * (for functions that need to clean up) // CompiledData::Function *compiledFunction -struct CompilationUnit +struct Q_QML_EXPORT CompilationUnit { CompilationUnit() : refCount(0) , engine(0) , data(0) + , ownsData(false) , runtimeStrings(0) , runtimeLookups(0) + , runtimeRegularExpressions(0) , runtimeClasses(0) {} virtual ~CompilationUnit(); @@ -322,6 +460,7 @@ struct CompilationUnit int refCount; ExecutionEngine *engine; Unit *data; + bool ownsData; QString fileName() const { return data->stringAt(data->sourceFileIndex); } @@ -333,6 +472,7 @@ struct CompilationUnit // QVector<QV4::Function *> runtimeFunctionsSortedByAddress; QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); + void unlink(); virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int /*functionIndex*/) { return 0; } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 322a39932a..8cd4c8e2d8 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -44,11 +44,14 @@ #include <qv4isel_p.h> #include <qv4engine_p.h> -QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::V4IR::Module *module) +QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QQmlJS::V4IR::Module *module, int headerSize) : irModule(module) , stringDataSize(0) , jsClassDataSize(0) { + if (headerSize == -1) + headerSize = sizeof(QV4::CompiledData::Unit); + this->headerSize = headerSize; } int QV4::Compiler::JSUnitGenerator::registerString(const QString &str) @@ -148,7 +151,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(QQmlJS::V4IR::ExprList *args return jsClasses.size() - 1; } -QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit() +QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(int *totalUnitSize) { registerString(irModule->fileName); foreach (QQmlJS::V4IR::Function *f, irModule->functions) { @@ -159,7 +162,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit() registerString(*f->locals.at(i)); } - int unitSize = QV4::CompiledData::Unit::calculateSize(strings.size(), irModule->functions.size(), regexps.size(), lookups.size(), jsClasses.count()); + int unitSize = QV4::CompiledData::Unit::calculateSize(headerSize, strings.size(), irModule->functions.size(), regexps.size(), lookups.size(), jsClasses.count()); uint functionDataSize = 0; for (int i = 0; i < irModule->functions.size(); ++i) { @@ -174,7 +177,11 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit() functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount); } - char *data = (char *)malloc(unitSize + functionDataSize + stringDataSize + jsClassDataSize); + const int totalSize = unitSize + functionDataSize + stringDataSize + jsClassDataSize; + if (totalUnitSize) + *totalUnitSize = totalSize; + char *data = (char *)malloc(totalSize); + memset(data, 0, totalSize); QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data; memcpy(unit->magic, QV4::CompiledData::magic_str, sizeof(unit->magic)); @@ -182,7 +189,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit() unit->flags = QV4::CompiledData::Unit::IsJavascript; unit->version = 1; unit->stringTableSize = strings.size(); - unit->offsetToStringTable = sizeof(QV4::CompiledData::Unit); + unit->offsetToStringTable = headerSize; unit->functionTableSize = irModule->functions.size(); unit->offsetToFunctionTable = unit->offsetToStringTable + unit->stringTableSize * sizeof(uint); unit->lookupTableSize = lookups.count(); @@ -286,6 +293,9 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QQmlJS::V4 function->nInnerFunctions = irFunction->nestedFunctions.size(); function->innerFunctionsOffset = function->lineNumberMappingOffset + function->nLineNumberMappingEntries * 2 * sizeof(quint32); + function->location.line = irFunction->line; + function->location.column = irFunction->column; + // write formals quint32 *formals = (quint32 *)(f + function->formalsOffset); for (int i = 0; i < irFunction->formals.size(); ++i) diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 6c50073a24..b875833463 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -57,8 +57,8 @@ struct JSClassMember; namespace Compiler { -struct JSUnitGenerator { - JSUnitGenerator(QQmlJS::V4IR::Module *module); +struct Q_QML_EXPORT JSUnitGenerator { + JSUnitGenerator(QQmlJS::V4IR::Module *module, int headerSize = -1); QQmlJS::V4IR::Module *irModule; @@ -75,19 +75,20 @@ struct JSUnitGenerator { int registerJSClass(QQmlJS::V4IR::ExprList *args); - QV4::CompiledData::Unit *generateUnit(); + QV4::CompiledData::Unit *generateUnit(int *totalUnitSize = 0); // Returns bytes written int writeFunction(char *f, int index, QQmlJS::V4IR::Function *irFunction); QHash<QString, int> stringToId; QStringList strings; - int stringDataSize; + uint stringDataSize; QHash<QQmlJS::V4IR::Function *, uint> functionOffsets; QList<CompiledData::Lookup> lookups; QVector<CompiledData::RegExp> regexps; QHash<QQmlJS::V4IR::Function *, QVector<uint> > lineNumberMappingsPerFunction; QList<QList<CompiledData::JSClassMember> > jsClasses; - int jsClassDataSize; + uint jsClassDataSize; + uint headerSize; }; } diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index b753b875de..a6a245e5ca 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -502,7 +502,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) lineNumberMapping[i * 2] = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); lineNumberMapping[i * 2 + 1] = codeLineNumberMappings.at(i).lineNumber; } - _isel->registerLineNumberMapping(_function, lineNumberMapping); + _isel->jsUnitGenerator()->registerLineNumberMapping(_function, lineNumberMapping); QHash<void*, const char*> functions; foreach (CallToLink ctl, _callsToLink) { @@ -584,8 +584,8 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } -InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module) - : EvalInstructionSelection(execAllocator, module) +InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) + : EvalInstructionSelection(execAllocator, module, jsGenerator) , _block(0) , _function(0) , _as(0) @@ -702,7 +702,6 @@ void *InstructionSelection::addConstantTable(QVector<Value> *values) QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep() { - compilationUnit->data = generateUnit(); return compilationUnit; } diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index adb426d674..c74dcd362d 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -1315,7 +1315,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module); + InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); ~InstructionSelection(); virtual void run(int functionIndex); @@ -1529,8 +1529,8 @@ class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module) - { return new InstructionSelection(execAllocator, module); } + virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + { return new InstructionSelection(execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return true; } }; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 99ca3c5d4e..b3e837d158 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -205,8 +205,8 @@ private: } }; -InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module) - : EvalInstructionSelection(execAllocator, module) +InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + : EvalInstructionSelection(execAllocator, module, jsGenerator) , _function(0) , _block(0) , _codeStart(0) @@ -281,7 +281,7 @@ void InstructionSelection::run(int functionIndex) } } - registerLineNumberMapping(_function, lineNumberMappings); + jsGenerator->registerLineNumberMapping(_function, lineNumberMappings); // TODO: patch stack size (the push instruction) patchJumpAddresses(); @@ -305,7 +305,6 @@ void InstructionSelection::run(int functionIndex) QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep() { - compilationUnit->data = generateUnit(); compilationUnit->codeRefs.resize(irModule->functions.size()); int i = 0; foreach (V4IR::Function *irFunction, irModule->functions) diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index df6095db5b..ee0b7276cb 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -71,7 +71,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module); + InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); ~InstructionSelection(); virtual void run(int functionIndex); @@ -188,8 +188,8 @@ class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module) - { return new InstructionSelection(execAllocator, module); } + virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + { return new InstructionSelection(execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return false; } }; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 7989232926..2f66628fea 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -58,11 +58,16 @@ QTextStream qout(stderr, QIODevice::WriteOnly); using namespace QQmlJS; using namespace QQmlJS::V4IR; -EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module) - : QV4::Compiler::JSUnitGenerator(module) - , useFastLookups(true) +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + : useFastLookups(true) , executableAllocator(execAllocator) + , irModule(module) { + if (!jsGenerator) { + jsGenerator = new QV4::Compiler::JSUnitGenerator(module); + ownJSGenerator.reset(jsGenerator); + } + this->jsGenerator = jsGenerator; assert(execAllocator); assert(module); } @@ -73,7 +78,7 @@ EvalInstructionSelection::~EvalInstructionSelection() EvalISelFactory::~EvalISelFactory() {} -QV4::CompiledData::CompilationUnit *EvalInstructionSelection::compile() +QV4::CompiledData::CompilationUnit *EvalInstructionSelection::compile(bool generateUnitData) { Function *rootFunction = irModule->rootFunction; if (!rootFunction) @@ -81,7 +86,12 @@ QV4::CompiledData::CompilationUnit *EvalInstructionSelection::compile() for (int i = 0; i < irModule->functions.size(); ++i) run(i); - return backendCompileStep(); + QV4::CompiledData::CompilationUnit *unit = backendCompileStep(); + if (generateUnitData) { + unit->data = jsGenerator->generateUnit(); + unit->ownsData = true; + } + return unit; } void IRDecoder::visitMove(V4IR::Move *s) diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index e1cccedd2c..4b82d154d9 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -59,30 +59,40 @@ struct Function; namespace QQmlJS { -class Q_QML_EXPORT EvalInstructionSelection : public QV4::Compiler::JSUnitGenerator +class Q_QML_EXPORT EvalInstructionSelection { public: - EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module); + EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); virtual ~EvalInstructionSelection() = 0; - QV4::CompiledData::CompilationUnit *compile(); + QV4::CompiledData::CompilationUnit *compile(bool generateUnitData = true); void setUseFastLookups(bool b) { useFastLookups = b; } + int registerString(const QString &str) { return jsGenerator->registerString(str); } + uint registerGetterLookup(const QString &name) { return jsGenerator->registerGetterLookup(name); } + uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); } + uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); } + int registerRegExp(QQmlJS::V4IR::RegExp *regexp) { return jsGenerator->registerRegExp(regexp); } + int registerJSClass(QQmlJS::V4IR::ExprList *args) { return jsGenerator->registerJSClass(args); } + QV4::Compiler::JSUnitGenerator *jsUnitGenerator() const { return jsGenerator; } + protected: virtual void run(int functionIndex) = 0; virtual QV4::CompiledData::CompilationUnit *backendCompileStep() = 0; -protected: bool useFastLookups; QV4::ExecutableAllocator *executableAllocator; + QV4::Compiler::JSUnitGenerator *jsGenerator; + QScopedPointer<QV4::Compiler::JSUnitGenerator> ownJSGenerator; + V4IR::Module *irModule; }; class Q_QML_EXPORT EvalISelFactory { public: virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module) = 0; + virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; }; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 224b55a1a4..69771837fe 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -622,9 +622,7 @@ Function *Module::newFunction(const QString &name, Function *outer) Module::~Module() { - foreach (Function *f, functions) { - delete f; - } + qDeleteAll(functions); } void Module::setFileName(const QString &name) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index d849b50ea2..67168ef951 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -714,6 +714,10 @@ struct Function { uint hasWith: 1; uint unused : 26; + // Location of declaration in source code (-1 if not specified) + int line; + int column; + template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } Function(Module *module, Function *outer, const QString &name) @@ -730,6 +734,8 @@ struct Function { , hasTry(false) , hasWith(false) , unused(0) + , line(-1) + , column(-1) { this->name = newString(name); } ~Function(); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 56701457e4..6a97b9cfab 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -271,10 +271,12 @@ void CallContext::initQmlContext(ExecutionContext *parentContext, Object *qml, F activation = qml; - compilationUnit = function->function->compilationUnit; - compiledFunction = function->function->compiledFunction; - lookups = compilationUnit->runtimeLookups; - runtimeStrings = compilationUnit->runtimeStrings; + if (function->function) { + compilationUnit = function->function->compilationUnit; + compiledFunction = function->function->compiledFunction; + lookups = compilationUnit->runtimeLookups; + runtimeStrings = compilationUnit->runtimeStrings; + } locals = (Value *)(this + 1); if (function->varCount) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 1a084905ae..daa58d5e81 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -296,6 +296,12 @@ ExecutionEngine::~ExecutionEngine() m_multiplyWrappedQObjects = 0; delete identifierTable; delete memoryManager; + + QSet<QV4::CompiledData::CompilationUnit*> remainingUnits; + qSwap(compilationUnits, remainingUnits); + foreach (QV4::CompiledData::CompilationUnit *unit, remainingUnits) + unit->unlink(); + delete m_qmlExtensions; emptyClass->destroy(); delete bumperPointerAllocator; diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp index a754663556..e539d62d54 100644 --- a/src/qml/jsruntime/qv4executableallocator.cpp +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -52,6 +52,14 @@ void *ExecutableAllocator::Allocation::start() const return reinterpret_cast<void*>(addr); } +void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator) +{ + if (isValid()) + allocator->free(this); + else + delete this; +} + ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize) { Allocation *remainder = new Allocation; @@ -117,7 +125,8 @@ ExecutableAllocator::ChunkOfPages::~ChunkOfPages() Allocation *alloc = firstAllocation; while (alloc) { Allocation *next = alloc->next; - delete alloc; + if (alloc->isValid()) + delete alloc; alloc = next; } pages->deallocate(); @@ -135,13 +144,25 @@ bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const return false; } +ExecutableAllocator::ExecutableAllocator() + : mutex(QMutex::NonRecursive) +{ +} + ExecutableAllocator::~ExecutableAllocator() { + foreach (ChunkOfPages *chunk, chunks) { + for (Allocation *allocation = chunk->firstAllocation; allocation; allocation = allocation->next) + if (!allocation->free) + allocation->invalidate(); + } + qDeleteAll(chunks); } ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) { + QMutexLocker locker(&mutex); Allocation *allocation = 0; // Code is best aligned to 16-byte boundaries. @@ -182,6 +203,8 @@ ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) void ExecutableAllocator::free(Allocation *allocation) { + QMutexLocker locker(&mutex); + assert(allocation); allocation->free = true; @@ -210,6 +233,8 @@ void ExecutableAllocator::free(Allocation *allocation) ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const { + QMutexLocker locker(&mutex); + QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(allocation->addr); if (it != chunks.begin()) --it; diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index 2a304baf9c..b4fb2fb0e5 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -48,6 +48,7 @@ #include <QHash> #include <QVector> #include <QByteArray> +#include <QMutex> namespace WTF { class PageAllocation; @@ -63,6 +64,7 @@ public: struct ChunkOfPages; struct Allocation; + ExecutableAllocator(); ~ExecutableAllocator(); Allocation *allocate(size_t size); @@ -79,8 +81,13 @@ public: {} void *start() const; + void invalidate() { addr = 0; } + bool isValid() const { return addr != 0; } + void deallocate(ExecutableAllocator *allocator); private: + ~Allocation() {} + friend class ExecutableAllocator; Allocation *split(size_t dividingSize); @@ -125,6 +132,7 @@ public: private: QMultiMap<size_t, Allocation*> freeAllocations; QMap<quintptr, ChunkOfPages*> chunks; + mutable QMutex mutex; }; } diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 2e03df4871..b93eded3f3 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -47,7 +47,6 @@ #include <QtCore/QByteArray> #include <QtCore/qurl.h> -#include <config.h> #include "qv4value_def_p.h" #include <private/qv4compileddata_p.h> #include <private/qv4engine_p.h> diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index a13aa2c29d..0cf235475e 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -249,7 +249,8 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) QQmlJS::RuntimeCodegen cg(v4->current, f->strictMode); cg.generateFromFunctionExpression(QString(), function, fe, &module); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module)); + QV4::Compiler::JSUnitGenerator jsGenerator(&module); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module, &jsGenerator)); QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); QV4::Function *vmf = compilationUnit->linkToEngine(v4); diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 1834e28be6..7dd3bbeb66 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -60,39 +60,59 @@ using namespace QV4; -struct QmlBindingWrapper : FunctionObject +QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml) + : FunctionObject(scope, scope->engine->id_eval) + , qml(qml) { - Q_MANAGED + vtbl = &static_vtbl; + function = f; + function->compilationUnit->ref(); + usesArgumentsObject = function->usesArgumentsObject(); + needsActivation = function->needsActivation(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); + + qmlContext = scope->engine->current->newQmlContext(this, qml); + scope->engine->popContext(); +} - QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml) - : FunctionObject(scope, scope->engine->id_eval) - , qml(qml) - { - vtbl = &static_vtbl; - function = f; - function->compilationUnit->ref(); - usesArgumentsObject = function->usesArgumentsObject(); - needsActivation = function->needsActivation(); - defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); - - qmlContext = scope->engine->current->newQmlContext(this, qml); - scope->engine->popContext(); - } +QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Object *qml) + : FunctionObject(scope, scope->engine->id_eval) + , qml(qml) +{ + vtbl = &static_vtbl; + function = 0; + usesArgumentsObject = false; + needsActivation = false; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); + + qmlContext = scope->engine->current->newQmlContext(this, qml); + scope->engine->popContext(); +} - static ReturnedValue call(Managed *that, CallData *); - static void markObjects(Managed *m) - { - QmlBindingWrapper *wrapper = static_cast<QmlBindingWrapper*>(m); - if (wrapper->qml) - wrapper->qml->mark(); - FunctionObject::markObjects(m); - wrapper->qmlContext->mark(); - } +ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *) +{ + ExecutionEngine *engine = that->engine(); + Scope scope(engine); + QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); + Q_ASSERT(This->function); -private: - Object *qml; - CallContext *qmlContext; -}; + CallContext *ctx = This->qmlContext; + std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue()); + engine->pushContext(ctx); + ScopedValue result(scope, This->function->code(ctx, This->function->codeData)); + engine->popContext(); + + return result.asReturnedValue(); +} + +void QmlBindingWrapper::markObjects(Managed *m) +{ + QmlBindingWrapper *wrapper = static_cast<QmlBindingWrapper*>(m); + if (wrapper->qml) + wrapper->qml->mark(); + FunctionObject::markObjects(m); + wrapper->qmlContext->mark(); +} DEFINE_MANAGED_VTABLE(QmlBindingWrapper); @@ -122,23 +142,6 @@ struct CompilationUnitHolder : public QV4::Object DEFINE_MANAGED_VTABLE(CompilationUnitHolder); -ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *) -{ - ExecutionEngine *engine = that->engine(); - Scope scope(engine); - QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); - - CallContext *ctx = This->qmlContext; - std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue()); - engine->pushContext(ctx); - ScopedValue result(scope, This->function->code(ctx, This->function->codeData)); - engine->popContext(); - - return result.asReturnedValue(); - -} - - Script::~Script() { } @@ -192,7 +195,8 @@ void Script::parse() RuntimeCodegen cg(scope, strictMode); cg.generateFromProgram(sourceFile, sourceCode, program, &module, parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::EvalCode, inheritedLocals); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module)); + QV4::Compiler::JSUnitGenerator jsGenerator(&module); + QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module, &jsGenerator)); if (inheritContext) isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 46f28ccb1e..752cb2ac53 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -43,6 +43,7 @@ #include "qv4global_p.h" #include "qv4engine_p.h" +#include "qv4functionobject_p.h" QT_BEGIN_NAMESPACE @@ -50,6 +51,23 @@ namespace QV4 { struct ExecutionContext; +struct QmlBindingWrapper : FunctionObject { + Q_MANAGED + + QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml); + // Constructor for QML functions and signal handlers, resulting binding wrapper is not callable! + QmlBindingWrapper(ExecutionContext *scope, Object *qml); + + static ReturnedValue call(Managed *that, CallData *); + static void markObjects(Managed *m); + + CallContext *context() const { return qmlContext; } + +private: + Object *qml; + CallContext *qmlContext; +}; + struct Q_QML_EXPORT Script { Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 29c007ad07..3bba6f8e83 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -55,7 +55,8 @@ SOURCES += \ $$PWD/qqmlcontextwrapper.cpp \ $$PWD/qqmlvaluetypewrapper.cpp \ $$PWD/qqmltypewrapper.cpp \ - $$PWD/qqmlfileselector.cpp + $$PWD/qqmlfileselector.cpp \ + $$PWD/qqmlobjectcreator.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -133,7 +134,8 @@ HEADERS += \ $$PWD/qqmlvaluetypewrapper_p.h \ $$PWD/qqmltypewrapper_p.h \ $$PWD/qqmlfileselector_p.h \ - $$PWD/qqmlfileselector.h + $$PWD/qqmlfileselector.h \ + $$PWD/qqmlobjectcreator_p.h include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index 40d42d8284..ebe81d647a 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE +class QmlObjectCreator; + class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding { public: @@ -150,6 +152,7 @@ private: friend class QQmlVME; friend class QtSharedPointer::ExternalRefCount<QQmlAbstractBinding>; friend class QV4Bindings; + friend class QmlObjectCreator; typedef QSharedPointer<QQmlAbstractBinding> SharedPointer; // To save memory, we also store the rarely used weakPointer() instance in here diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 3242ab9831..36cbc39d40 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -86,6 +86,19 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, m_expression = expression; } +QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, const QV4::PersistentValue &function) + : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + m_v8function(function), + m_line(-1), + m_column(-1), + m_target(target), + m_index(index), + m_expressionFunctionValid(true), + m_invalidParameterName(false) +{ + init(ctxt, scope); +} + void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) { setNotifyOnValueChanged(false); diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h index ffb3d06770..86e3dc20ea 100644 --- a/src/qml/qml/qqmlboundsignal_p.h +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -75,6 +75,9 @@ public: const QString &handlerName = QString(), const QString ¶meterString = QString()); + QQmlBoundSignalExpression(QObject *target, int index, + QQmlContextData *ctxt, QObject *scope, const QV4::PersistentValue &function); + // "inherited" from QQmlJavaScriptExpression. static QString expressionIdentifier(QQmlJavaScriptExpression *); diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index abe278f570..35de0ac358 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -87,7 +87,7 @@ int QQmlCompiledData::indexForUrl(const QUrl &data) QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) : engine(engine), importCache(0), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false), - rootPropertyCache(0) + rootPropertyCache(0), compilationUnit(0), qmlUnit(0) { Q_ASSERT(engine); @@ -116,8 +116,17 @@ QQmlCompiledData::~QQmlCompiledData() types.at(ii).typePropertyCache->release(); } + for (QHash<int, TypeReference>::Iterator resolvedType = resolvedTypes.begin(), end = resolvedTypes.end(); + resolvedType != end; ++resolvedType) { + if (resolvedType->component) + resolvedType->component->release(); + if (resolvedType->typePropertyCache) + resolvedType->typePropertyCache->release(); + } + for (int ii = 0; ii < propertyCaches.count(); ++ii) - propertyCaches.at(ii)->release(); + if (propertyCaches.at(ii)) + propertyCaches.at(ii)->release(); for (int ii = 0; ii < scripts.count(); ++ii) scripts.at(ii)->release(); @@ -127,6 +136,10 @@ QQmlCompiledData::~QQmlCompiledData() if (rootPropertyCache) rootPropertyCache->release(); + + if (compilationUnit) + compilationUnit->deref(); + free(qmlUnit); } void QQmlCompiledData::clear() diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index bc013358f5..9e63ecb22c 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -70,6 +70,13 @@ QT_BEGIN_NAMESPACE +namespace QV4 { +namespace CompiledData { +struct CompilationUnit; +struct QmlUnit; +} +} + class QQmlEngine; class QQmlComponent; class QQmlContext; @@ -103,7 +110,12 @@ public: QQmlPropertyCache *propertyCache() const; QQmlPropertyCache *createPropertyCache(QQmlEngine *); }; + // --- old compiler: QList<TypeReference> types; + // --- new compiler: + // map from name index + QHash<int, TypeReference> resolvedTypes; + // --- struct V8Program { V8Program(const QByteArray &p, QQmlCompiledData *c) @@ -125,6 +137,16 @@ public: QList<QQmlScriptData *> scripts; QList<QUrl> urls; + // --- new compiler + QV4::CompiledData::CompilationUnit *compilationUnit; + QV4::CompiledData::QmlUnit *qmlUnit; + // index in first hash is component index, hash inside maps from object index in that scope to integer id + QHash<int, QHash<int, int> > objectIndexToIdPerComponent; + QHash<int, int> objectIndexToIdForRoot; + + bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } + // --- + struct Instruction { #define QML_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlInstructionData<QQmlInstruction::I> I; FOR_EACH_QML_INSTR(QML_INSTR_DATA_TYPEDEF) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index ce8f71f9ba..3c8a50ad4f 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -894,8 +894,20 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) state.completePending = true; enginePriv->referenceScarceResources(); - state.vme.init(context, cc, start, creationContext); - QObject *rv = state.vme.execute(&state.errors); + QObject *rv = 0; + if (enginePriv->useNewCompiler) { + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (cc->compilationUnit && !cc->compilationUnit->engine) + cc->compilationUnit->linkToEngine(v4); + + state.creator = new QmlObjectCreator(context, cc); + rv = state.creator->create(start); + if (!rv) + state.errors = state.creator->errors; + } else { + state.vme.init(context, cc, start, creationContext); + rv = state.vme.execute(&state.errors); + } enginePriv->dereferenceScarceResources(); if (rv) { @@ -935,14 +947,22 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, state->errors.clear(); state->completePending = true; - state->vme.initDeferred(object); - state->vme.execute(&state->errors); + if (enginePriv->useNewCompiler) { + // ### + } else { + state->vme.initDeferred(object); + state->vme.execute(&state->errors); + } } void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state) { if (state->completePending) { - state->vme.complete(); + if (enginePriv->useNewCompiler) { + state->creator->finalize(); + } else { + state->vme.complete(); + } state->completePending = false; @@ -1015,9 +1035,11 @@ QQmlComponentAttached *QQmlComponent::qmlAttachedProperties(QObject *obj) if (!engine) return a; - if (QQmlEnginePrivate::get(engine)->activeVME) { // XXX should only be allowed during begin - QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine); + QQmlEnginePrivate *p = QQmlEnginePrivate::get(engine); + if (p->activeVME) { // XXX should only be allowed during begin a->add(&p->activeVME->componentAttached); + } else if (p->activeObjectCreator) { + a->add(&p->activeObjectCreator->componentAttached); } else { QQmlData *d = QQmlData::get(obj); Q_ASSERT(d); diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 9877f59fb6..fe376d0e4a 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -129,6 +129,7 @@ private: Q_DISABLE_COPY(QQmlComponent) friend class QQmlVME; friend class QQmlTypeData; + friend class QmlObjectCreator; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 439f1fcdd9..08e4dcea7d 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -62,6 +62,7 @@ #include "qqmlerror.h" #include "qqml.h" #include <private/qqmlprofilerservice_p.h> +#include <private/qqmlobjectcreator_p.h> #include <QtCore/QString> #include <QtCore/QStringList> @@ -105,9 +106,20 @@ public: QQmlCompiledData *cc; struct ConstructionState { - ConstructionState() : completePending(false) {} - + ConstructionState() + : creator(0) + , completePending(false) + {} + ~ConstructionState() + { + delete creator; + } + + // --- new compiler + QmlObjectCreator *creator; + // --- old compiler QQmlVME vme; + // --- QList<QQmlError> errors; bool completePending; }; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index f3a1867d18..afb993499d 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -547,16 +547,19 @@ the same object as is returned from the Qt.include() call. */ // Qt.include() is implemented in qv4include.cpp +DEFINE_BOOL_CONFIG_OPTION(qmlUseNewCompiler, QML_NEW_COMPILER) QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) : propertyCapture(0), rootContext(0), isDebugging(false), outputWarningsToStdErr(true), cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), activeVME(0), + activeObjectCreator(0), networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) { + useNewCompiler = qmlUseNewCompiler(); } QQmlEnginePrivate::~QQmlEnginePrivate() @@ -1028,6 +1031,8 @@ void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) { if (activeVME) { activeVME->finalizeCallbacks.append(qMakePair(QPointer<QObject>(obj), index)); + } else if (activeObjectCreator) { + activeObjectCreator->finalizeCallbacks.append(qMakePair(QPointer<QObject>(obj), index)); } else { void *args[] = { 0 }; QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index c57c70fa40..c67e45a833 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -99,6 +99,7 @@ class QQmlCleanup; class QQmlDelayedError; class QQuickWorkerScriptEngine; class QQmlVME; +class QmlObjectCreator; class QDir; class QQmlIncubator; @@ -144,6 +145,7 @@ public: QQmlContext *rootContext; bool isDebugging; + bool useNewCompiler; bool outputWarningsToStdErr; @@ -165,7 +167,11 @@ public: typedef QPair<QPointer<QObject>,int> FinalizeCallback; void registerFinalizeCallback(QObject *obj, int index); + // --- old compiler: QQmlVME *activeVME; + // --- new compiler: + QmlObjectCreator *activeObjectCreator; + // --- QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; QNetworkAccessManager *getNetworkAccessManager() const; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp new file mode 100644 index 0000000000..b4201fd10a --- /dev/null +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -0,0 +1,1575 @@ +/**************************************************************************** +** +** 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 "qqmlobjectcreator_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlabstractbinding_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qv4function_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qqmlcontextwrapper_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qqmlstringconverters_p.h> +#include <private/qqmlboundsignal_p.h> +#include <private/qqmltrace_p.h> +#include <private/qqmlcomponentattached_p.h> +#include <QQmlComponent> +#include <private/qqmlcomponent_p.h> +#include <private/qqmlcodegenerator_p.h> + +QT_USE_NAMESPACE + +namespace { +struct ActiveOCRestorer +{ + ActiveOCRestorer(QmlObjectCreator *creator, QQmlEnginePrivate *ep) + : ep(ep), oldCreator(ep->activeObjectCreator) { ep->activeObjectCreator = creator; } + ~ActiveOCRestorer() { ep->activeObjectCreator = oldCreator; } + + QQmlEnginePrivate *ep; + QmlObjectCreator *oldCreator; +}; +} + +QQmlCompilePass::QQmlCompilePass(const QUrl &url, const QV4::CompiledData::QmlUnit *unit) + : url(url) + , qmlUnit(unit) +{ +} + +void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) +{ + QQmlError error; + error.setUrl(url); + error.setLine(location.line); + error.setColumn(location.column); + error.setDescription(description); + errors << error; +} + +#define COMPILE_EXCEPTION(token, desc) \ + { \ + recordError((token)->location, desc); \ + return false; \ + } + +static QAtomicInt classIndexCounter(0); + +QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *unit, const QUrl &url, const QQmlImports *imports, + QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes) + : QQmlCompilePass(url, unit) + , enginePrivate(enginePrivate) + , imports(imports) + , resolvedTypes(resolvedTypes) +{ +} + +bool QQmlPropertyCacheCreator::create(const QV4::CompiledData::Object *obj, QQmlPropertyCache **resultCache, QByteArray *vmeMetaObjectData) +{ + Q_ASSERT(!stringAt(obj->inheritedTypeNameIndex).isEmpty()); + + QQmlCompiledData::TypeReference typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex); + QQmlPropertyCache *baseTypeCache = typeRef.createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); + Q_ASSERT(baseTypeCache); + if (obj->nProperties == 0 && obj->nSignals == 0 && obj->nFunctions == 0) { + *resultCache = baseTypeCache; + vmeMetaObjectData->clear(); + return true; + } + + QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(QQmlEnginePrivate::get(enginePrivate), + obj->nProperties, + obj->nFunctions + obj->nProperties + obj->nSignals, + obj->nSignals + obj->nProperties); + *resultCache = cache; + + vmeMetaObjectData->clear(); + + struct TypeData { + QV4::CompiledData::Property::Type dtype; + int metaType; + } builtinTypes[] = { + { QV4::CompiledData::Property::Var, QMetaType::QVariant }, + { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, + { QV4::CompiledData::Property::Int, QMetaType::Int }, + { QV4::CompiledData::Property::Bool, QMetaType::Bool }, + { QV4::CompiledData::Property::Real, QMetaType::Double }, + { QV4::CompiledData::Property::String, QMetaType::QString }, + { QV4::CompiledData::Property::Url, QMetaType::QUrl }, + { QV4::CompiledData::Property::Color, QMetaType::QColor }, + { QV4::CompiledData::Property::Font, QMetaType::QFont }, + { QV4::CompiledData::Property::Time, QMetaType::QTime }, + { QV4::CompiledData::Property::Date, QMetaType::QDate }, + { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, + { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, + { QV4::CompiledData::Property::Point, QMetaType::QPointF }, + { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, + { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, + { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, + { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, + { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, + { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } + }; + static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (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; + + const QV4::CompiledData::Property *p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + + if (p->type == QV4::CompiledData::Property::Alias) + aliasCount++; + else if (p->type == QV4::CompiledData::Property::Var) + varPropCount++; + +#if 0 // ### Do this elsewhere + // No point doing this for both the alias and non alias cases + QQmlPropertyData *d = property(obj, p->name); + if (d && d->isFinal()) + COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); +#endif + } + + typedef QQmlVMEMetaData VMD; + + QByteArray &dynamicData = *vmeMetaObjectData = QByteArray(sizeof(QQmlVMEMetaData) + + obj->nProperties * sizeof(VMD::PropertyData) + + obj->nFunctions * 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; + + const QV4::CompiledData::Property *p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + 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 (uint i = 0; i < obj->nSignals; ++i) { + const QV4::CompiledData::Signal *s = obj->signalAt(i); + const int paramCount = s->nParameters; + + QList<QByteArray> names; + QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + for (int i = 0; i < paramCount; ++i) { + const QV4::CompiledData::Parameter *param = s->parameterAt(i); + names.append(stringAt(param->nameIndex).toUtf8()); + if (param->type < builtinTypeCount) { + // built-in type + paramTypes[i + 1] = builtinTypes[param->type].metaType; + } else { + // lazily resolved type + Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); + const QString customTypeName = stringAt(param->customTypeNameIndex); + QQmlType *qmltype = 0; + if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0)) + 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 + const quint32 *functionIndex = obj->functionOffsetTable(); + for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIndex) { + const QV4::CompiledData::Function *s = qmlUnit->header.functionAt(*functionIndex); + int paramCount = s->nFormals; + + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString slotName = stringAt(s->nameIndex); + 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. + + const quint32 *formalsIndices = s->formalsTable(); + QList<QByteArray> parameterNames; + parameterNames.reserve(paramCount); + for (int i = 0; i < paramCount; ++i) + parameterNames << stringAt(formalsIndices[i]).toUtf8(); + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties (except var and aliases) + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + /* const QV4::CompiledData::Property* */ p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + + 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 (i == 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 + /* const QV4::CompiledData::Property* */ p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + + 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 (i == 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 + /*const quint32* */functionIndex = obj->functionOffsetTable(); + for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIndex) { + const QV4::CompiledData::Function *s = qmlUnit->header.functionAt(*functionIndex); + + VMD::MethodData methodData = { int(s->nFormals), + /* body offset*/0, + /* body length*/0, + /* s->location.start.line */0 }; // ### + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); + vmd->methodCount++; + md = methodData; + } + + return true; +} + +static void removeBindingOnProperty(QObject *o, int index) +{ + int coreIndex = index & 0x0000FFFF; + int valueTypeIndex = (index & 0xFFFF0000 ? index >> 16 : -1); + + QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0); + if (binding) binding->destroy(); +} + +QmlObjectCreator::QmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData) + : QQmlCompilePass(compiledData->url, compiledData->qmlUnit) + , componentAttached(0) + , engine(parentContext->engine) + , jsUnit(compiledData->compilationUnit) + , parentContext(parentContext) + , context(0) + , resolvedTypes(compiledData->resolvedTypes) + , propertyCaches(compiledData->propertyCaches) + , vmeMetaObjectData(compiledData->datas) + , compiledData(compiledData) + , _qobject(0) + , _compiledObject(0) + , _ddata(0) + , _propertyCache(0) + , _vmeMetaObject(0) + , _qmlContext(0) +{ +} + +QObject *QmlObjectCreator::create(int subComponentIndex, QObject *parent) +{ + int objectToCreate; + + if (subComponentIndex == -1) { + objectIndexToId = compiledData->objectIndexToIdForRoot; + objectToCreate = qmlUnit->indexOfRootObject; + } else { + objectIndexToId = compiledData->objectIndexToIdPerComponent[subComponentIndex]; + const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex); + objectToCreate = compObj->bindingTable()->value.objectIndex; + } + + context = new QQmlContextData; + context->isInternal = true; + context->url = compiledData->url; + context->urlString = compiledData->name; + context->imports = compiledData->importCache; + context->imports->addref(); + context->setParent(parentContext); + + QVector<QQmlContextData::ObjectIdMapping> mapping(objectIndexToId.count()); + for (QHash<int, int>::ConstIterator it = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); + it != end; ++it) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(it.key()); + + QQmlContextData::ObjectIdMapping m; + m.id = it.value(); + m.name = stringAt(obj->idIndex); + mapping[m.id] = m; + } + context->setIdPropertyData(mapping); + + QObject *instance = createInstance(objectToCreate, parent); + if (instance) { + QQmlData *ddata = QQmlData::get(instance); + Q_ASSERT(ddata); + ddata->compiledData = compiledData; + ddata->compiledData->addref(); + + QQmlEnginePrivate::get(engine)->registerInternalCompositeType(compiledData); + } + return instance; +} + +void QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) +{ + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + int propertyWriteStatus = -1; + void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; + + // ### enums + + switch (property->propType) { + case QMetaType::QVariant: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double n = binding->valueAsNumber(); + if (double(int(n)) == n) { + if (property->isVarProperty()) { + _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Value::fromInt32(int(n))); + } else { + int i = int(n); + QVariant value(i); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + } else { + if (property->isVarProperty()) { + _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Value::fromDouble(n)); + } else { + QVariant value(n); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + } + } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + if (property->isVarProperty()) { + _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Value::fromBoolean(binding->valueAsBoolean())); + } else { + QVariant value(binding->valueAsBoolean()); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + } else { + QString stringValue = binding->valueAsString(&qmlUnit->header); + if (property->isVarProperty()) { + _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Value::fromString(QV8Engine::getV4(engine), stringValue)); + } else { + QVariant value = QQmlStringConverters::variantFromString(stringValue); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + } + } + break; + case QVariant::String: { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + QString value = binding->valueAsString(&qmlUnit->header); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: string expected")); + } + } + break; + case QVariant::StringList: { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + QStringList value(binding->valueAsString(&qmlUnit->header)); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: string or string list expected")); + } + } + break; + case QVariant::ByteArray: { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + QByteArray value(binding->valueAsString(&qmlUnit->header).toUtf8()); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: byte array expected")); + } + } + break; + case QVariant::Url: { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + QString string = binding->valueAsString(&qmlUnit->header); + // Encoded dir-separators defeat QUrl processing - decode them first + string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); + QUrl value = string.isEmpty() ? QUrl() : this->url.resolved(QUrl(string)); + // Apply URL interceptor + if (engine->urlInterceptor()) + value = engine->urlInterceptor()->intercept(value, QQmlAbstractUrlInterceptor::UrlString); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: url expected")); + } + } + break; + case QVariant::UInt: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = binding->valueAsNumber(); + if (double(uint(d)) == d) { + uint value = uint(d); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + break; + } + } + recordError(binding->location, tr("Invalid property assignment: unsigned int expected")); + } + break; + case QVariant::Int: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double d = binding->valueAsNumber(); + if (double(int(d)) == d) { + int value = int(d); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + break; + } + } + recordError(binding->location, tr("Invalid property assignment: int expected")); + } + break; + case QMetaType::Float: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + float value = float(binding->valueAsNumber()); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Double: { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double value = binding->valueAsNumber(); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: number expected")); + } + } + break; + case QVariant::Color: { + bool ok = false; + uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(&qmlUnit->header), &ok); + + if (ok) { + struct { void *data[4]; } buffer; + if (QQml_valueTypeProvider()->storeValueType(property->propType, &colorValue, &buffer, sizeof(buffer))) { + argv[0] = reinterpret_cast<void *>(&buffer); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + } else { + recordError(binding->location, tr("Invalid property assignment: color expected")); + } + } + break; +#ifndef QT_NO_DATESTRING + case QVariant::Date: { + bool ok = false; + QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(&qmlUnit->header), &ok); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: date expected")); + } + } + break; + case QVariant::Time: { + bool ok = false; + QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(&qmlUnit->header), &ok); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: time expected")); + } + } + break; + case QVariant::DateTime: { + bool ok = false; + QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(&qmlUnit->header), &ok); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: datetime expected")); + } + } + break; +#endif // QT_NO_DATESTRING + case QVariant::Point: { + bool ok = false; + QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(&qmlUnit->header), &ok).toPoint(); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::PointF: { + bool ok = false; + QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(&qmlUnit->header), &ok); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Size: { + bool ok = false; + QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(&qmlUnit->header), &ok).toSize(); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::SizeF: { + bool ok = false; + QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(&qmlUnit->header), &ok); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: size expected")); + } + } + break; + case QVariant::Rect: { + bool ok = false; + QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(&qmlUnit->header), &ok).toRect(); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::RectF: { + bool ok = false; + QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(&qmlUnit->header), &ok); + if (ok) { + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: point expected")); + } + } + break; + case QVariant::Bool: { + if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + bool value = binding->valueAsBoolean(); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: boolean expected")); + } + } + break; + case QVariant::Vector3D: { + struct { + float xp; + float yp; + float zy; + } vec; + if (QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(&qmlUnit->header), &vec, sizeof(vec))) { + argv[0] = reinterpret_cast<void *>(&vec); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: 3D vector expected")); + } + } + break; + case QVariant::Vector4D: { + struct { + float xp; + float yp; + float zy; + float wp; + } vec; + if (QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(&qmlUnit->header), &vec, sizeof(vec))) { + argv[0] = reinterpret_cast<void *>(&vec); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: 4D vector expected")); + } + } + break; + case QVariant::RegExp: + recordError(binding->location, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + break; + 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) { + QList<qreal> value; + value.append(binding->valueAsNumber()); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: real or array of reals expected")); + } + break; + } else if (property->propType == qMetaTypeId<QList<int> >()) { + if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double n = binding->valueAsNumber(); + if (double(int(n)) == n) { + QList<int> value; + value.append(int(n)); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + break; + } else { + recordError(binding->location, 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) { + QList<bool> value; + value.append(binding->valueAsBoolean()); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: bool or array of bools expected")); + } + break; + } else if (property->propType == qMetaTypeId<QList<QUrl> >()) { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + QString urlString = binding->valueAsString(&qmlUnit->header); + QUrl u = urlString.isEmpty() ? QUrl() : this->url.resolved(QUrl(urlString)); + QList<QUrl> value; + value.append(u); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: url or array of urls expected")); + } + break; + } else if (property->propType == qMetaTypeId<QList<QString> >()) { + if (binding->type == QV4::CompiledData::Binding::Type_String) { + QList<QString> value; + value.append(binding->valueAsString(&qmlUnit->header)); + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: string or array of strings expected")); + } + break; + } else if (property->propType == qMetaTypeId<QJSValue>()) { + QJSValue value; + if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + value = QJSValue(binding->valueAsBoolean()); + } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { + double n = binding->valueAsNumber(); + if (double(int(n)) == n) { + value = QJSValue(int(n)); + } else + value = QJSValue(n); + } else { + value = QJSValue(binding->valueAsString(&qmlUnit->header)); + } + argv[0] = reinterpret_cast<void *>(&value); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + break; + } + + // otherwise, try a custom type assignment + QString stringValue = binding->valueAsString(&qmlUnit->header); + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType); + if (converter) { + QVariant value = (*converter)(stringValue); + + QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex); + if (value.isNull() || ((int)metaProperty.type() != property->propType && metaProperty.userType() != property->propType)) { + recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name()))); + break; + } + + argv[0] = value.data(); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType)))); + } + } + break; + } +} + +void QmlObjectCreator::setupBindings() +{ + QQmlListProperty<void> savedList; + qSwap(_currentList, savedList); + + QQmlPropertyData *property = 0; + + const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); + for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { + + if (!property || (i > 0 && (binding - 1)->propertyNameIndex != binding->propertyNameIndex)) { + QString name = stringAt(binding->propertyNameIndex); + if (!name.isEmpty()) + property = _propertyCache->property(name, _qobject, context); + else + property = 0; + + if (property && property->isQList()) { + void *argv[1] = { (void*)&_currentList }; + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv); + } else if (_currentList.object) + _currentList = QQmlListProperty<void>(); + + } + + if (!setPropertyValue(property, i, binding)) + return; + } + + qSwap(_currentList, savedList); +} + +bool QmlObjectCreator::setPropertyValue(QQmlPropertyData *property, int bindingIndex, const QV4::CompiledData::Binding *binding) +{ + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(binding->value.objectIndex); + Q_ASSERT(stringAt(obj->inheritedTypeNameIndex).isEmpty()); + QQmlType *attachedType = resolvedTypes.value(binding->propertyNameIndex).type; + const int id = attachedType->attachedPropertiesId(); + QObject *qmlObject = qmlAttachedPropertiesObjectById(id, _qobject); + QQmlRefPointer<QQmlPropertyCache> cache = QQmlEnginePrivate::get(engine)->cache(qmlObject); + if (!populateInstance(binding->value.objectIndex, qmlObject, cache, _qobject)) + return false; + return true; + } + + QObject *createdSubObject = 0; + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + createdSubObject = createInstance(binding->value.objectIndex, _qobject); + if (!createdSubObject) + return false; + } + + // Child item: + // ... + // Item { + // ... + // } + if (!property) + return true; + + if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(binding->value.objectIndex); + if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) { + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property->propType); + if (!valueType) { + recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); + return false; + } + + valueType->read(_qobject, property->coreIndex); + + QQmlRefPointer<QQmlPropertyCache> cache = QQmlEnginePrivate::get(engine)->cache(valueType); + if (!populateInstance(binding->value.objectIndex, valueType, cache, _qobject)) + return false; + + valueType->write(_qobject, property->coreIndex, QQmlPropertyPrivate::BypassInterceptor); + return true; + } + } + + if (_ddata->hasBindingBit(property->coreIndex)) + removeBindingOnProperty(_qobject, property->coreIndex); + + if (binding->type == QV4::CompiledData::Binding::Type_Script) { + QV4::Function *runtimeFunction = jsUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QV4::Value function = QV4::Value::fromObject(QV4::FunctionObject::creatScriptFunction(_qmlContext, runtimeFunction)); + + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { + int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex); + QQmlBoundSignal *bs = new QQmlBoundSignal(_qobject, signalIndex, _qobject, engine); + QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_qobject, signalIndex, + context, _qobject, function); + + bs->takeExpression(expr); + } else { + QQmlBinding *qmlBinding = new QQmlBinding(function, _qobject, context, + QString(), 0, 0); // ### + qmlBinding->setTarget(_qobject, *property, context); + qmlBinding->addToObject(); + + _createdBindings[bindingIndex] = qmlBinding; + qmlBinding->m_mePtr = &_createdBindings[bindingIndex]; + } + return true; + } + + if (binding->type == QV4::CompiledData::Binding::Type_Object) { + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + int propertyWriteStatus = -1; + void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; + + if (const char *iid = QQmlMetaType::interfaceIId(property->propType)) { + void *ptr = createdSubObject->qt_metacast(iid); + if (ptr) { + argv[0] = &ptr; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Cannot assign object to interface property")); + return false; + } + } else if (property->propType == QMetaType::QVariant) { + if (property->isVarProperty()) { + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::Scope scope(v4); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), createdSubObject)); + _vmeMetaObject->setVMEProperty(property->coreIndex, wrappedObject); + } else { + QVariant value = QVariant::fromValue(createdSubObject); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } + } else if (property->isQList()) { + Q_ASSERT(_currentList.object); + + void *itemToAdd = createdSubObject; + + const char *iid = 0; + int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType); + if (listItemType != -1) + iid = QQmlMetaType::interfaceIId(listItemType); + if (iid) + itemToAdd = createdSubObject->qt_metacast(iid); + + if (_currentList.append) + _currentList.append(&_currentList, itemToAdd); + } else { + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + + // 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 = enginePrivate->cache(createdSubObject); + while (c && !isAssignable) { + isAssignable |= c == propertyMetaObject; + c = c->parent(); + } + } + + if (isAssignable) { + argv[0] = &createdSubObject; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv); + } else { + recordError(binding->location, tr("Cannot assign object to property")); + return false; + } + } + return true; + } + + if (property->isQList()) { + recordError(binding->location, tr("Cannot assign primitives to lists")); + return false; + } + + setPropertyValue(property, binding); + return true; +} + +void QmlObjectCreator::setupFunctions() +{ + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(_qobject); + + const quint32 *functionIdx = _compiledObject->functionOffsetTable(); + for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) { + QV4::Function *runtimeFunction = jsUnit->runtimeFunctions[*functionIdx]; + const QString name = runtimeFunction->name->toQString(); + + QQmlPropertyData *property = _propertyCache->property(name, _qobject, context); + if (!property->isVMEFunction()) + continue; + + QV4::FunctionObject *function = QV4::FunctionObject::creatScriptFunction(_qmlContext, runtimeFunction); + vme->setVmeMethod(property->coreIndex, QV4::Value::fromObject(function)); + } +} + +QObject *QmlObjectCreator::createInstance(int index, QObject *parent) +{ + ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); + + bool isComponent = false; + QObject *instance = 0; + + if (compiledData->isComponent(index)) { + isComponent = true; + QQmlComponent *component = new QQmlComponent(engine, compiledData, index, parent); + QQmlComponentPrivate::get(component)->creationContext = context; + instance = component; + } else { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + + QQmlCompiledData::TypeReference typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + QQmlType *type = typeRef.type; + if (type) { + instance = type->create(); + } else { + Q_ASSERT(typeRef.component); + QmlObjectCreator subCreator(context, typeRef.component); + instance = subCreator.create(); + } + // ### use no-event variant + if (parent) + instance->setParent(parent); + } + + QQmlData *ddata = QQmlData::get(instance, /*create*/true); + if (index == qmlUnit->indexOfRootObject) { + if (ddata->context) { + Q_ASSERT(ddata->context != context); + Q_ASSERT(ddata->outerContext); + Q_ASSERT(ddata->outerContext != context); + QQmlContextData *c = ddata->context; + while (c->linkedContext) c = c->linkedContext; + c->linkedContext = context; + } else + context->addObject(instance); + ddata->ownContext = true; + } else if (!ddata->context) + context->addObject(instance); + + ddata->outerContext = context; + + QHash<int, int>::ConstIterator idEntry = objectIndexToId.find(index); + if (idEntry != objectIndexToId.constEnd()) + context->setIdProperty(idEntry.value(), instance); + + if (!isComponent) { + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(index); + Q_ASSERT(!cache.isNull()); + + if (!populateInstance(index, instance, cache, _qobject)) + return 0; + } + + return instance; +} + +void QmlObjectCreator::finalize() +{ + { + QQmlTrace trace("VME Binding Enable"); + trace.event("begin binding eval"); + + Q_ASSERT(allCreatedBindings.isEmpty() || allCreatedBindings.isDetached()); + + for (QLinkedList<QVector<QQmlAbstractBinding*> >::Iterator it = allCreatedBindings.begin(), end = allCreatedBindings.end(); + it != end; ++it) { + const QVector<QQmlAbstractBinding *> &bindings = *it; + for (int i = 0; i < bindings.count(); ++i) { + QQmlAbstractBinding *b = bindings.at(i); + if (!b) + continue; + b->m_mePtr = 0; + QQmlData *data = QQmlData::get(b->object()); + Q_ASSERT(data); + data->clearPendingBindingBit(b->propertyIndex()); + b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::DontRemoveBinding); + } + } + } + + { + QQmlTrace trace("VME Component.onCompleted Callbacks"); + while (componentAttached) { + QQmlComponentAttached *a = componentAttached; + a->rem(); + QQmlData *d = QQmlData::get(a->parent()); + Q_ASSERT(d); + Q_ASSERT(d->context); + a->add(&d->context->componentAttached); + // ### designer if (componentCompleteEnabled()) + emit a->completed(); + +#if 0 // ### + if (watcher.hasRecursed() || interrupt.shouldInterrupt()) + return 0; +#endif + } + } +} + +bool QmlObjectCreator::populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache, QObject *scopeObjectForJavaScript) +{ + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + + if (!scopeObjectForJavaScript) + scopeObjectForJavaScript = instance; + + QQmlData *declarativeData = QQmlData::get(instance, /*create*/true); + + qSwap(_propertyCache, cache); + qSwap(_qobject, instance); + qSwap(_compiledObject, obj); + qSwap(_ddata, declarativeData); + + QQmlVMEMetaObject *vmeMetaObject = 0; + const QByteArray data = vmeMetaObjectData.value(index); + if (!data.isEmpty()) { + // install on _object + vmeMetaObject = new QQmlVMEMetaObject(_qobject, _propertyCache, reinterpret_cast<const QQmlVMEMetaData*>(data.constData())); + if (_ddata->propertyCache) + _ddata->propertyCache->release(); + _ddata->propertyCache = _propertyCache; + _ddata->propertyCache->addref(); + } else { + vmeMetaObject = QQmlVMEMetaObject::get(_qobject); + } + + _ddata->lineNumber = _compiledObject->location.line; + _ddata->columnNumber = _compiledObject->location.column; + + qSwap(_vmeMetaObject, vmeMetaObject); + + QVector<QQmlAbstractBinding*> createdBindings(_compiledObject->nBindings, 0); + qSwap(_createdBindings, createdBindings); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::Scope valueScope(v4); + QV4::ScopedValue scopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, scopeObjectForJavaScript)); + QV4::QmlBindingWrapper *qmlBindingWrapper = new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, scopeObject->asObject()); + QV4::ScopedValue qmlScopeFunction(valueScope, QV4::Value::fromObject(qmlBindingWrapper)); + QV4::ExecutionContext *qmlContext = qmlBindingWrapper->context(); + + qSwap(_qmlContext, qmlContext); + + setupBindings(); + setupFunctions(); + + qSwap(_qmlContext, qmlContext); + + qSwap(_createdBindings, createdBindings); + qSwap(_vmeMetaObject, vmeMetaObject); + qSwap(_propertyCache, cache); + qSwap(_ddata, declarativeData); + qSwap(_compiledObject, obj); + qSwap(_qobject, instance); + + allCreatedBindings.append(_createdBindings); + + return errors.isEmpty(); +} + + +QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, + const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, + const QList<QQmlPropertyCache *> &propertyCaches, QList<QByteArray> *vmeMetaObjectData, + QHash<int, int> *objectIndexToIdForRoot, + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent) + : QQmlCompilePass(url, qmlUnit) + , _componentIndex(-1) + , _objectIndexToIdInScope(0) + , resolvedTypes(resolvedTypes) + , propertyCaches(propertyCaches) + , vmeMetaObjectData(vmeMetaObjectData) + , objectIndexToIdForRoot(objectIndexToIdForRoot) + , objectIndexToIdPerComponent(objectIndexToIdPerComponent) +{ +} + +bool QQmlComponentAndAliasResolver::resolve() +{ + Q_ASSERT(componentRoots.isEmpty()); + + // Find objects that are Components. This is missing an extra pass + // that finds implicitly defined components, i.e. + // someProperty: Item { ... } + // when someProperty _is_ a QQmlComponent. In that case the Item {} + // should be implicitly surrounded by Component {} + + for (int i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); + if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) + continue; + + QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.value(i); + if (!cache || cache->metaObject() != &QQmlComponent::staticMetaObject) + continue; + + componentRoots.append(i); + // Sanity checks: There can be only an (optional) id property and + // a default property, that defines the component tree. + } + + std::sort(componentRoots.begin(), componentRoots.end()); + + // For each component's tree, remember to which component the children + // belong to + for (int i = 0; i < componentRoots.count(); ++i) { + const QV4::CompiledData::Object *component = qmlUnit->objectAt(componentRoots.at(i)); + + if (component->nFunctions > 0) + COMPILE_EXCEPTION(component, tr("Component objects cannot declare new functions.")); + if (component->nProperties > 0) + COMPILE_EXCEPTION(component, tr("Component objects cannot declare new properties.")); + if (component->nSignals > 0) + COMPILE_EXCEPTION(component, tr("Component objects cannot declare new signals.")); + + if (component->nBindings == 0) + COMPILE_EXCEPTION(component, tr("Cannot create empty component specification")); + + const QV4::CompiledData::Binding *rootBinding = component->bindingTable(); + if (component->nBindings > 1 || rootBinding->type != QV4::CompiledData::Binding::Type_Object) + COMPILE_EXCEPTION(rootBinding, tr("Component elements may not contain properties other than id")); + + _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(qmlUnit->indexOfRootObject); + + resolveAliases(); + + return errors.isEmpty(); +} + +bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) +{ + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); + + // Only include creatable types. Everything else is synthetic, such as group property + // objects. + if (_componentIndex != -1 && !stringAt(obj->inheritedTypeNameIndex).isEmpty()) + objectIndexToComponentIndex.insert(objectIndex, _componentIndex); + + QString id = stringAt(obj->idIndex); + if (!id.isEmpty()) { + 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()); + } + + const QV4::CompiledData::Property *property = obj->propertyTable(); + for (int i = 0; i < obj->nProperties; ++i, ++property) + if (property->type == QV4::CompiledData::Property::Alias) { + _objectsWithAliases.append(objectIndex); + break; + } + + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (int i = 0; i < obj->nBindings; ++i, ++binding) { + 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(componentRoots.constBegin(), componentRoots.constEnd(), binding->value.objectIndex)) + continue; + + if (!collectIdsAndAliases(binding->value.objectIndex)) + return false; + } + + return true; +} + +bool QQmlComponentAndAliasResolver::resolveAliases() +{ + foreach (int objectIndex, _objectsWithAliases) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(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 QV4::CompiledData::Property *p = obj->propertyTable(); + for (quint32 propertyIndex = 0; propertyIndex < obj->nProperties; ++propertyIndex, ++p) { + if (p->type != QV4::CompiledData::Property::Alias) + continue; + + const int idIndex = p->aliasIdValueIndex; + const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1); + if (targetObjectIndex == -1) + COMPILE_EXCEPTION(p, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex))); + 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 QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(targetObjectIndex); + QQmlCompiledData::TypeReference typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex); + + 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) + COMPILE_EXCEPTION(p, tr("Invalid alias location")); + + 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) + COMPILE_EXCEPTION(p, tr("Invalid alias location")); + + propType = type; + + int valueTypeIndex = + valueType->metaObject()->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) + COMPILE_EXCEPTION(p, tr("Invalid alias location")); + 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; +} diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h new file mode 100644 index 0000000000..fe3a4bb00a --- /dev/null +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 QQMLOBJECTCREATOR_P_H +#define QQMLOBJECTCREATOR_P_H + +#include <private/qqmlimport_p.h> +#include <private/qqmltypenamecache_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qqmlcompiler_p.h> +#include <QLinkedList> + +QT_BEGIN_NAMESPACE + +class QQmlAbstractBinding; + +struct QQmlCompilePass +{ + QQmlCompilePass(const QUrl &url, const QV4::CompiledData::QmlUnit *unit); + QList<QQmlError> errors; + +protected: + QString stringAt(int idx) const { return qmlUnit->header.stringAt(idx); } + void recordError(const QV4::CompiledData::Location &location, const QString &description); + + const QUrl url; + const QV4::CompiledData::QmlUnit *qmlUnit; +}; + +class QQmlPropertyCacheCreator : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator) +public: + QQmlPropertyCacheCreator(QQmlEnginePrivate *enginePrivate, const QV4::CompiledData::QmlUnit *qmlUnit, + const QUrl &url, const QQmlImports *imports, + QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes); + + bool create(const QV4::CompiledData::Object *obj, QQmlPropertyCache **cache, QByteArray *vmeMetaObjectData); + +protected: + QQmlEnginePrivate *enginePrivate; + const QQmlImports *imports; + QHash<int, QQmlCompiledData::TypeReference> *resolvedTypes; +}; + +class QQmlComponentAndAliasResolver : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QQmlAnonymousComponentResolver) +public: + QQmlComponentAndAliasResolver(const QUrl &url, const QV4::CompiledData::QmlUnit *qmlUnit, + const QHash<int, QQmlCompiledData::TypeReference> &resolvedTypes, + const QList<QQmlPropertyCache *> &propertyCaches, + QList<QByteArray> *vmeMetaObjectData, + QHash<int, int> *objectIndexToIdForRoot, + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent); + + bool resolve(); + + QVector<int> componentRoots; + QHash<int, int> objectIndexToComponentIndex; + +protected: + bool collectIdsAndAliases(int objectIndex); + bool resolveAliases(); + + int _componentIndex; + QHash<int, int> _idToObjectIndex; + QHash<int, int> *_objectIndexToIdInScope; + QList<int> _objectsWithAliases; + + const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; + const QList<QQmlPropertyCache *> propertyCaches; + QList<QByteArray> *vmeMetaObjectData; + QHash<int, int> *objectIndexToIdForRoot; + QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; +}; + +class QmlObjectCreator : public QQmlCompilePass +{ + Q_DECLARE_TR_FUNCTIONS(QmlObjectCreator) +public: + QmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData); + + QObject *create(int subComponentIndex = -1, QObject *parent = 0); + void finalize(); + + QQmlComponentAttached *componentAttached; + QList<QQmlEnginePrivate::FinalizeCallback> finalizeCallbacks; + +private: + QObject *createInstance(int index, QObject *parent = 0); + + bool populateInstance(int index, QObject *instance, QQmlRefPointer<QQmlPropertyCache> cache, QObject *scopeObjectForJavaScript); + + void setupBindings(); + bool setPropertyValue(QQmlPropertyData *property, int index, const QV4::CompiledData::Binding *binding); + void setPropertyValue(QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); + void setupFunctions(); + + QQmlEngine *engine; + const QV4::CompiledData::CompilationUnit *jsUnit; + QQmlContextData *parentContext; + QQmlContextData *context; + const QHash<int, QQmlCompiledData::TypeReference> resolvedTypes; + const QList<QQmlPropertyCache *> propertyCaches; + const QList<QByteArray> vmeMetaObjectData; + QHash<int, int> objectIndexToId; + QLinkedList<QVector<QQmlAbstractBinding*> > allCreatedBindings; + QQmlCompiledData *compiledData; + + QObject *_qobject; + const QV4::CompiledData::Object *_compiledObject; + QQmlData *_ddata; + QQmlRefPointer<QQmlPropertyCache> _propertyCache; + QQmlVMEMetaObject *_vmeMetaObject; + QVector<QQmlAbstractBinding*> _createdBindings; + QQmlListProperty<void> _currentList; + QV4::ExecutionContext *_qmlContext; +}; + +QT_END_NAMESPACE + +#endif // QQMLOBJECTCREATOR_P_H diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 21fee28b02..bc0cef9f4c 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -74,6 +74,7 @@ class QQmlAccessors; class QMetaObjectBuilder; class QQmlPropertyCacheMethodArguments; class QQmlVMEMetaObject; +class QQmlPropertyCacheCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData @@ -339,6 +340,8 @@ protected: private: friend class QQmlEnginePrivate; friend class QQmlCompiler; + friend class QQmlPropertyCacheCreator; + friend class QQmlComponentAndAliasResolver; inline QQmlPropertyCache *copy(int reserve); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index a705e00886..27230cd669 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -49,6 +49,7 @@ #include <private/qqmlcomponent_p.h> #include <private/qqmlprofilerservice_p.h> #include <private/qqmlmemoryprofiler_p.h> +#include <private/qqmlcodegenerator_p.h> #include <QtCore/qdir.h> #include <QtCore/qfile.h> @@ -1474,17 +1475,6 @@ QQmlImportDatabase *QQmlTypeLoader::importDatabase() } /*! -\enum QQmlTypeLoader::Option - -This enum defines the options that control the way type data is handled. - -\value None The default value, indicating that no other options - are enabled. -\value PreserveParser The parser used to handle the type data is preserved - after the data has been parsed. -*/ - -/*! Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached. */ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) @@ -1498,7 +1488,7 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) QQmlTypeData *typeData = m_typeCache.value(url); if (!typeData) { - typeData = new QQmlTypeData(url, None, this); + typeData = new QQmlTypeData(url, this); // TODO: if (compiledData == 0), is it safe to omit this insertion? m_typeCache.insert(url, typeData); QQmlDataLoader::load(typeData, mode); @@ -1512,14 +1502,12 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) /*! Returns a QQmlTypeData for the given \a data with the provided base \a url. The QQmlTypeData will not be cached. - -The specified \a options control how the loader handles type data. */ -QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Options options) +QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url) { LockHolder<QQmlTypeLoader> holder(this); - QQmlTypeData *typeData = new QQmlTypeData(url, options, this); + QQmlTypeData *typeData = new QQmlTypeData(url, this); QQmlDataLoader::loadWithStaticData(typeData, data); return typeData; @@ -1915,11 +1903,11 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback() { } -QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader::Options options, - QQmlTypeLoader *manager) -: QQmlTypeLoader::Blob(url, QmlFile, manager), m_options(options), +QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) +: QQmlTypeLoader::Blob(url, QmlFile, manager), m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false) { + m_useNewCompiler = QQmlEnginePrivate::get(manager->engine())->useNewCompiler; } QQmlTypeData::~QQmlTypeData() @@ -1990,6 +1978,7 @@ void QQmlTypeData::done() } // Check all type dependencies for errors + // --- old compiler: for (int ii = 0; !isError() && ii < m_types.count(); ++ii) { const TypeReference &type = m_types.at(ii); Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); @@ -2006,13 +1995,32 @@ void QQmlTypeData::done() setError(errors); } } + // --- new compiler: + for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); + !isError() && it != end; ++it) { + const TypeReference &type = *it; + Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError()); + if (type.typeData && type.typeData->isError()) { + QString typeName = parsedQML->jsGenerator.strings.at(it.key()); + + QList<QQmlError> errors = type.typeData->errors(); + QQmlError error; + error.setUrl(finalUrl()); + error.setLine(type.location.line); + error.setColumn(type.location.column); + error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); + errors.prepend(error); + setError(errors); + } + } + // --- // Compile component if (!isError()) compile(); - if (!(m_options & QQmlTypeLoader::PreserveParser)) - scriptParser.clear(); + scriptParser.clear(); + parsedQML.reset(); } void QQmlTypeData::completed() @@ -2052,9 +2060,18 @@ void QQmlTypeData::dataReceived(const Data &data) if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse")); - if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) { - setError(scriptParser.errors()); - return; + if (m_useNewCompiler) { + parsedQML.reset(new QtQml::ParsedQML); + QQmlCodeGenerator compiler; + if (!compiler.generateFromQml(code, finalUrl(), finalUrlString(), parsedQML.data())) { + setError(compiler.errors); + return; + } + } else { + if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) { + setError(scriptParser.errors()); + return; + } } m_imports.setBaseUrl(finalUrl(), finalUrlString()); @@ -2084,7 +2101,34 @@ void QQmlTypeData::dataReceived(const Data &data) QList<QQmlError> errors; - foreach (const QQmlScript::Import &import, scriptParser.imports()) { + // ### convert to use new data structure once old compiler is gone. + QList<QQmlScript::Import> imports; + if (m_useNewCompiler) { + imports.reserve(parsedQML->imports.size()); + foreach (QV4::CompiledData::Import *i, parsedQML->imports) { + QQmlScript::Import import; + import.uri = parsedQML->stringAt(i->uriIndex); + import.qualifier = parsedQML->stringAt(i->qualifierIndex); + import.majorVersion = i->majorVersion; + import.minorVersion = i->minorVersion; + import.location.start.line = i->location.line; + import.location.start.column = i->location.column; + + switch (i->type) { + case QV4::CompiledData::Import::ImportFile: import.type = QQmlScript::Import::File; break; + case QV4::CompiledData::Import::ImportLibrary: import.type = QQmlScript::Import::Library; break; + case QV4::CompiledData::Import::ImportScript: import.type = QQmlScript::Import::Script; break; + default: break; + } + + + imports << import; + } + } else { + imports = scriptParser.imports(); + } + + foreach (const QQmlScript::Import &import, imports) { if (!addImport(import, &errors)) { Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); @@ -2145,11 +2189,118 @@ void QQmlTypeData::compile() QQmlCompilingProfiler prof(m_compiledData->name); - QQmlCompiler compiler(&scriptParser._pool); - if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { - setError(compiler.errors()); - m_compiledData->release(); - m_compiledData = 0; + if (m_useNewCompiler) { + m_compiledData->importCache = new QQmlTypeNameCache; + m_imports.populateCache(m_compiledData->importCache); + m_compiledData->importCache->addref(); + + QQmlEngine *engine = typeLoader()->engine(); + + for (QHash<int, TypeReference>::ConstIterator resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); + resolvedType != end; ++resolvedType) { + QQmlCompiledData::TypeReference ref; + if (resolvedType->typeData) { + ref.component = resolvedType->typeData->compiledData(); + ref.component->addref(); + } else { + ref.type = resolvedType->type; + Q_ASSERT(ref.type); + } + m_compiledData->resolvedTypes.insert(resolvedType.key(), ref); + } + + { + SignalHandlerConverter converter(QQmlEnginePrivate::get(engine), + parsedQML.data(), + m_compiledData); + if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations()) { + setError(converter.errors); + m_compiledData->release(); + m_compiledData = 0; + return; + } + } + + JSCodeGen jsCodeGen; + jsCodeGen.generateJSCodeForFunctionsAndBindings(finalUrlString(), parsedQML.data()); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); + + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator)); + isel->setUseFastLookups(false); + QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/false); + + QmlUnitGenerator qmlGenerator; + QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(*parsedQML.data()); + + if (jsUnit) { + Q_ASSERT(!jsUnit->data); + jsUnit->ownsData = false; + jsUnit->data = &qmlUnit->header; + } + + m_compiledData->compilationUnit = jsUnit; + if (m_compiledData->compilationUnit) + m_compiledData->compilationUnit->ref(); + m_compiledData->qmlUnit = qmlUnit; // ownership transferred to m_compiledData + + QList<QQmlError> errors; + + m_compiledData->datas.reserve(qmlUnit->nObjects); + m_compiledData->propertyCaches.reserve(qmlUnit->nObjects); + + QQmlPropertyCacheCreator propertyCacheBuilder(QQmlEnginePrivate::get(m_typeLoader->engine()), + qmlUnit, m_compiledData->url, + &m_imports, &m_compiledData->resolvedTypes); + + for (quint32 i = 0; i < qmlUnit->nObjects; ++i) { + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i); + + QByteArray vmeMetaObjectData; + QQmlPropertyCache *propertyCache = 0; + + // If the object has no type, then it's probably a nested object definition as part + // of a group property. + const bool objectHasType = !parsedQML->jsGenerator.strings.at(obj->inheritedTypeNameIndex).isEmpty(); + if (objectHasType) { + if (!propertyCacheBuilder.create(obj, &propertyCache, &vmeMetaObjectData)) { + errors << propertyCacheBuilder.errors; + break; + } + } + + m_compiledData->datas << vmeMetaObjectData; + if (propertyCache) + propertyCache->addref(); + m_compiledData->propertyCaches << propertyCache; + + if (i == qmlUnit->indexOfRootObject) { + Q_ASSERT(propertyCache); + m_compiledData->rootPropertyCache = propertyCache; + propertyCache->addref(); + } + } + + if (errors.isEmpty()) { + // Scan for components, determine their scopes and resolve aliases within the scope. + QQmlComponentAndAliasResolver resolver(m_compiledData->url, m_compiledData->qmlUnit, m_compiledData->resolvedTypes, m_compiledData->propertyCaches, + &m_compiledData->datas, &m_compiledData->objectIndexToIdForRoot, &m_compiledData->objectIndexToIdPerComponent); + if (!resolver.resolve()) + errors << resolver.errors; + } + + if (!errors.isEmpty()) { + setError(errors); + m_compiledData->release(); + m_compiledData = 0; + } + } else { + QQmlCompiler compiler(&scriptParser._pool); + if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { + setError(compiler.errors()); + m_compiledData->release(); + m_compiledData = 0; + } } } @@ -2176,10 +2327,10 @@ void QQmlTypeData::resolveTypes() m_scripts << ref; } + // --- old compiler: foreach (QQmlScript::TypeReference *parserRef, scriptParser.referencedTypes()) { TypeReference ref; - QString url; int majorVersion = -1; int minorVersion = -1; QQmlImportNamespace *typeNamespace = 0; @@ -2239,6 +2390,80 @@ void QQmlTypeData::resolveTypes() m_types << ref; } + + // --- new compiler: + QV4::CompiledData::TypeReferenceMap typeReferences; + QStringList names; + if (parsedQML) { + typeReferences = parsedQML->typeReferences; + names = parsedQML->jsGenerator.strings; + } else { + // ### collect from available QV4::CompiledData::QmlUnit + } + for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = typeReferences.constBegin(), end = typeReferences.constEnd(); + unresolvedRef != end; ++unresolvedRef) { + + TypeReference ref; // resolved reference + + int majorVersion = -1; + int minorVersion = -1; + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + + const QString name = names.at(unresolvedRef.key()); + bool typeFound = m_imports.resolveType(name, &ref.type, + &majorVersion, &minorVersion, &typeNamespace, &errors); + if (!typeNamespace && !typeFound && !m_implicitImportLoaded) { + // Lazy loading of implicit import + if (loadImplicitImport()) { + // Try again to find the type + errors.clear(); + typeFound = m_imports.resolveType(name, &ref.type, + &majorVersion, &minorVersion, &typeNamespace, &errors); + } else { + return; //loadImplicitImport() hit an error, and called setError already + } + } + + if (!typeFound || typeNamespace) { + // Known to not be a type: + // - known to be a namespace (Namespace {}) + // - type with unknown namespace (UnknownNamespace.SomeType {}) + QQmlError error; + if (typeNamespace) { + error.setDescription(QQmlTypeLoader::tr("Namespace %1 cannot be used as a type").arg(name)); + } else { + if (errors.size()) { + error = errors.takeFirst(); + } else { + // this should not be possible! + // Description should come from error provided by addImport() function. + error.setDescription(QQmlTypeLoader::tr("Unreported error adding script import to import database")); + } + error.setUrl(m_imports.baseUrl()); + error.setDescription(QQmlTypeLoader::tr("%1 %2").arg(name).arg(error.description())); + } + + error.setLine(unresolvedRef->line); + error.setColumn(unresolvedRef->column); + + errors.prepend(error); + setError(errors); + return; + } + + if (ref.type->isComposite()) { + ref.typeData = typeLoader()->getType(ref.type->sourceUrl()); + addDependency(ref.typeData); + } + ref.majorVersion = majorVersion; + ref.minorVersion = minorVersion; + + ref.location.line = unresolvedRef->line; + ref.location.column = unresolvedRef->column; + + m_resolvedTypes.insert(unresolvedRef.key(), ref); + } } void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &/*nameSpace*/) diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 9ff7ddc0eb..fee09d3bdb 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -84,6 +84,10 @@ class QQmlTypeData; class QQmlDataLoader; class QQmlExtensionInterface; +namespace QtQml { +struct ParsedQML; +} + class Q_QML_PRIVATE_EXPORT QQmlDataBlob : public QQmlRefCount { public: @@ -323,16 +327,10 @@ public: QQmlTypeLoader(QQmlEngine *); ~QQmlTypeLoader(); - enum Option { - None, - PreserveParser - }; - Q_DECLARE_FLAGS(Options, Option) - QQmlImportDatabase *importDatabase(); QQmlTypeData *getType(const QUrl &url, Mode mode = PreferSynchronous); - QQmlTypeData *getType(const QByteArray &, const QUrl &url, Options = None); + QQmlTypeData *getType(const QByteArray &, const QUrl &url); QQmlScriptBlob *getScript(const QUrl &); QQmlQmldirData *getQmldir(const QUrl &); @@ -391,8 +389,6 @@ private: QmldirBundleIdCache m_qmldirBundleIdCache; }; -Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlTypeLoader::Options) - class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob { public: @@ -419,7 +415,7 @@ public: private: friend class QQmlTypeLoader; - QQmlTypeData(const QUrl &, QQmlTypeLoader::Options, QQmlTypeLoader *); + QQmlTypeData(const QUrl &, QQmlTypeLoader *); public: ~QQmlTypeData(); @@ -454,16 +450,24 @@ private: virtual void scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace); - QQmlTypeLoader::Options m_options; - + // --- old compiler QQmlScript::Parser scriptParser; + // --- new compiler + QScopedPointer<QtQml::ParsedQML> parsedQML; + // --- QList<ScriptReference> m_scripts; QSet<QString> m_namespaces; + // --- old compiler QList<TypeReference> m_types; + // --- new compiler + // map from name index to resolved type + QHash<int, TypeReference> m_resolvedTypes; + // --- bool m_typesResolved:1; + bool m_useNewCompiler:1; QQmlCompiledData *m_compiledData; diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 244e0af4ac..fcf89afb9f 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -483,6 +483,10 @@ int main(int argc, char ** argv) } QObject *topLevel = component->create(); + if (!topLevel && component->isError()) { + qWarning("%s", qPrintable(component->errorString())); + return -1; + } QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(topLevel)); if (window) { engine.setIncubationController(window->incubationController()); |