diff options
29 files changed, 2171 insertions, 116 deletions
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..c2ad55dda3 --- /dev/null +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -0,0 +1,776 @@ +/**************************************************************************** +** +** 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 <QCoreApplication> + +QT_USE_NAMESPACE + +using namespace QtQml; + +void QmlObject::dump(DebugStream &out) +{ + out << inheritedTypeNameIndex << " {" << endl; + out.indent++; + + out.indent--; + out << "}" << endl; +} + +QQmlCodeGenerator::QQmlCodeGenerator() + : _object(0) + , jsGenerator(0) +{ +} + +bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output) +{ + 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; + + qSwap(_imports, output->imports); + qSwap(_objects, output->objects); + qSwap(_functions, output->functions); + this->pool = output->jsParserEngine.pool(); + this->jsGenerator = &output->jsGenerator; + + 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; + } + AST::UiObjectDefinition *rootObject = AST::cast<AST::UiObjectDefinition*>(program->members->member); + Q_ASSERT(rootObject); + output->indexOfRootObject = defineQMLObject(rootObject); + qSwap(_imports, output->imports); + qSwap(_objects, output->objects); + qSwap(_functions, output->functions); + return true; +} + +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) +{ + int idx = defineQMLObject(node); + appendBinding(registerString(QString()), idx); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node) +{ + int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer); + appendBinding(registerString(asString(node->qualifiedId)), idx); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiScriptBinding *node) +{ + appendBinding(registerString(asString(node->qualifiedId)), node->statement); + return false; +} + +bool QQmlCodeGenerator::visit(AST::UiArrayBinding*) +{ + return true; +} + +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); +} + +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)); + _object->idIndex = registerString(QString()); + _object->indexOfDefaultProperty = -1; + _object->properties = New<PoolList<QmlProperty> >(); + _object->qmlSignals = New<PoolList<Signal> >(); + _object->bindings = New<PoolList<Binding> >(); + _object->functions = New<PoolList<Function> >(); + + accept(initializer); + + 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 = registerString(QString()); + + // 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; +} + +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>(); + signal->nameIndex = registerString(node->name.toString()); + 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 = registerString(QString()); + } + + param->nameIndex = registerString(p->name.toString()); + signal->parameters->append(param); + p = p->next; + } + + _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 = registerString(QString()); + + property->nameIndex = registerString(name.toString()); + + if (node->statement) + appendBinding(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 {}) + // ### check if this is correct? + 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->value.type = QV4::CompiledData::Value::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->value.type = QV4::CompiledData::Value::Type_String; + binding->value.stringIndex = registerString(lit->value.toString()); + } else if (expr->kind == AST::Node::Kind_TrueLiteral) { + binding->value.type = QV4::CompiledData::Value::Type_Boolean; + binding->value.b = true; + } else if (expr->kind == AST::Node::Kind_FalseLiteral) { + binding->value.type = QV4::CompiledData::Value::Type_Boolean; + binding->value.b = false; + } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) { + binding->value.type = QV4::CompiledData::Value::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->value.type = QV4::CompiledData::Value::Type_Number; + binding->value.d = -lit->value; + } + } + } + } + + // Do binding instead + if (binding->value.type == QV4::CompiledData::Value::Type_Invalid) { + binding->value.type = QV4::CompiledData::Value::Type_Script; + _functions << statement; + binding->value.compiledScriptIndex = _functions.size() - 1; + binding->value.stringIndex = registerString(asStringRef(statement).toString()); + } +} + +void QQmlCodeGenerator::appendBinding(int propertyNameIndex, AST::Statement *value) +{ + Binding *binding = New<Binding>(); + binding->propertyNameIndex = propertyNameIndex; + setBindingValue(binding, value); + _object->bindings->append(binding); +} + +void QQmlCodeGenerator::appendBinding(int propertyNameIndex, int objectIndex) +{ + Binding *binding = New<Binding>(); + binding->propertyNameIndex = propertyNameIndex; + binding->value.type = QV4::CompiledData::Value::Type_Object; + binding->value.objectIndex = objectIndex; + _object->bindings->append(binding); +} + +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; + + 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->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); + + foreach (AST::Node *node, output->functions) { + _env = 0; + + AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node); + + ScanFunctions scan(this, output->code); + scan.enterEnvironment(node); + scan(function ? function->body : node); + scan.leaveEnvironment(); + + QString name; + if (function) + name = function->name.toString(); + else + name = QStringLiteral("%qml-expression-entry"); + + defineFunction(name, node, + function ? function->formals : 0, + function ? function->body->elements : node, QmlBinding); + + } + + qDeleteAll(_envMap); + _envMap.clear(); +} diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h new file mode 100644 index 0000000000..35a40f5bc4 --- /dev/null +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** 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> + +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; + PoolList<SignalParameter> *parameters; + 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; + + 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; + int indexOfRootObject; + QList<QmlObject*> objects; + QList<AST::Node*> functions; // FunctionDeclaration, Statement or Expression + QV4::Compiler::JSUnitGenerator jsGenerator; + + 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 +{ + QQmlCodeGenerator(); + bool generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output); + + 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(int propertyNameIndex, AST::Statement *value); + void appendBinding(int propertyNameIndex, int objectIndex); + + 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(); } + + QList<QQmlError> errors; + + QList<QV4::CompiledData::Import*> _imports; + QList<QmlObject*> _objects; + QList<AST::Node*> _functions; + + QmlObject *_object; + + QQmlJS::MemoryPool *pool; + QString sourceCode; + QV4::Compiler::JSUnitGenerator *jsGenerator; +}; + +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 Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen +{ + JSCodeGen() + : QQmlJS::Codegen(/*strict mode*/false) + {} + + void generateJSCodeForFunctionsAndBindings(const QString &fileName, ParsedQML *output); + +private: + 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 293086378e..f4822fe26e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1749,7 +1749,7 @@ bool Codegen::visit(FunctionDeclaration * ast) V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, Mode mode, + AST::Node *body, Mode mode, const QStringList &inheritedLocals) { qSwap(_mode, mode); // enter function code. @@ -1851,7 +1851,13 @@ V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); } - sourceElements(body); + if (AST::SourceElements *elements = AST::cast<AST::SourceElements*>(body)) + sourceElements(elements); + else if (body) { + Q_ASSERT(_mode == QmlBinding); + _block->nextLocation = ast->firstSourceLocation(); + accept(body); + } _function->insertBasicBlock(_exitBlock); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 2d2ca55f8d..385cddefe3 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -282,7 +282,7 @@ protected: V4IR::Function *defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, - AST::SourceElements *body, + AST::Node *body, Mode mode = FunctionCode, const QStringList &inheritedLocals = QStringList()); @@ -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_p.h b/src/qml/compiler/qv4compileddata_p.h index 237cdf7d62..50ed39f0d8 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -178,9 +178,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() @@ -225,77 +225,168 @@ struct Function struct Value { - quint32 type; // Invalid, Boolean, Number, String, Function, Object, ListOfObjects + enum ValueType { + Type_Invalid, + Type_Boolean, + Type_Number, + Type_String, + Type_Script, + Type_Object + }; + + quint32 type; union { bool b; - int i; double d; - quint32 offsetToString; - quint32 offsetToFunction; - quint32 offsetToObject; + quint32 compiledScriptIndex; // used when Type_Script + quint32 objectIndex; }; + quint32 stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) }; struct Binding { - quint32 offsetToPropertyName; + quint32 propertyNameIndex; Value value; }; struct Parameter { - quint32 offsetToName; + quint32 nameIndex; quint32 type; - quint32 offsetToCustomTypeName; + quint32 customTypeNameIndex; quint32 reserved; }; struct Signal { - quint32 offsetToName; + quint32 nameIndex; quint32 nParameters; - Parameter parameters[1]; + // 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; + quint32 customTypeNameIndex; + quint32 flags; // readonly }; struct Object { - quint32 offsetToInheritedTypeName; - quint32 offsetToId; - quint32 offsetToDefaultProperty; + 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; // 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 Location { + int line; + int column; +}; + +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,7 +395,7 @@ struct QmlUnit // CompilationUnit * (for functions that need to clean up) // CompiledData::Function *compiledFunction -struct CompilationUnit +struct Q_QML_EXPORT CompilationUnit { CompilationUnit() : refCount(0) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 322a39932a..6781fdfe72 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(); 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 1d277270ca..cefc053ac4 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -685,8 +685,6 @@ void *InstructionSelection::addConstantTable(QVector<Value> *values) QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep() { - compilationUnit->data = jsGenerator->generateUnit(); - compilationUnit->ownsData = true; compilationUnit->codeRefs.resize(irModule->functions.size()); int i = 0; foreach (V4IR::Function *irFunction, irModule->functions) diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index bef2f64c3e..d4dc5460b7 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -300,8 +300,6 @@ void InstructionSelection::run(V4IR::Function *function) QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep() { - compilationUnit->data = jsGenerator->generateUnit(); - compilationUnit->ownsData = true; compilationUnit->codeRefs.resize(irModule->functions.size()); int i = 0; foreach (V4IR::Function *irFunction, irModule->functions) diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 4d475a1bd0..5a1a6f5341 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -78,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) @@ -86,7 +86,12 @@ QV4::CompiledData::CompilationUnit *EvalInstructionSelection::compile() foreach (V4IR::Function *f, irModule->functions) run(f); - 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 ac6ea64c9f..8824b677af 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -65,7 +65,7 @@ public: 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; } diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index e70cf100ce..81aa727eda 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -626,9 +626,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/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 595955a8ec..735dc558e1 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> diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 9d0fed34fe..aef8ac8838 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -59,39 +59,44 @@ 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(); - } +Value QmlBindingWrapper::call(Managed *that, const CallData &) +{ + ExecutionEngine *engine = that->engine(); + QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); - static Value call(Managed *that, const CallData &); - static void markObjects(Managed *m) - { - QmlBindingWrapper *wrapper = static_cast<QmlBindingWrapper*>(m); - if (wrapper->qml) - wrapper->qml->mark(); - FunctionObject::markObjects(m); - wrapper->qmlContext->mark(); - } + CallContext *ctx = This->qmlContext; + std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue()); + engine->pushContext(ctx); + Value result = This->function->code(ctx, This->function->codeData); + engine->popContext(); -private: - Object *qml; - CallContext *qmlContext; -}; + return result; + +} + +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); @@ -121,22 +126,6 @@ struct CompilationUnitHolder : public QV4::Object DEFINE_MANAGED_VTABLE(CompilationUnitHolder); -Value QmlBindingWrapper::call(Managed *that, const CallData &) -{ - ExecutionEngine *engine = that->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); - Value result = This->function->code(ctx, This->function->codeData); - engine->popContext(); - - return result; - -} - - Script::~Script() { } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 250fd73704..b3bce6b427 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,19 @@ namespace QV4 { struct ExecutionContext; +struct QmlBindingWrapper : FunctionObject { + Q_MANAGED + + QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml); + + static Value call(Managed *that, const CallData &); + static void markObjects(Managed *m); + +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 dae76ede32..77b0334c7e 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -54,7 +54,8 @@ SOURCES += \ $$PWD/qqmllistwrapper.cpp \ $$PWD/qqmlcontextwrapper.cpp \ $$PWD/qqmlvaluetypewrapper.cpp \ - $$PWD/qqmltypewrapper.cpp + $$PWD/qqmltypewrapper.cpp \ + $$PWD/qqmlobjectcreator.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -130,7 +131,8 @@ HEADERS += \ $$PWD/qqmllistwrapper_p.h \ $$PWD/qqmlcontextwrapper_p.h \ $$PWD/qqmlvaluetypewrapper_p.h \ - $$PWD/qqmltypewrapper_p.h + $$PWD/qqmltypewrapper_p.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..8b04d823e2 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -60,6 +60,10 @@ QT_BEGIN_NAMESPACE +namespace QtQml { +class QmlObjectCreator; +} + class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding { public: @@ -150,6 +154,7 @@ private: friend class QQmlVME; friend class QtSharedPointer::ExternalRefCount<QQmlAbstractBinding>; friend class QV4Bindings; + friend class QtQml::QmlObjectCreator; typedef QSharedPointer<QQmlAbstractBinding> SharedPointer; // To save memory, we also store the rarely used weakPointer() instance in here diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index abe278f570..a2e323a129 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); @@ -127,6 +127,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..ca825a923d 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; @@ -125,6 +132,11 @@ public: QList<QQmlScriptData *> scripts; QList<QUrl> urls; + // --- new compiler + QV4::CompiledData::CompilationUnit *compilationUnit; + QV4::CompiledData::QmlUnit *qmlUnit; + // --- + 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 5f3a2e971c..ff961c7822 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -893,8 +893,16 @@ 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) { + state.creator = new QtQml::QmlObjectCreator(engine, context, cc); + rv = state.creator->create(); + 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) { @@ -934,14 +942,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) { + // ### + } else { + state->vme.complete(); + } state->completePending = false; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 439f1fcdd9..3bd0a194df 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 + QtQml::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 39c3399917..971836f783 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -510,6 +510,7 @@ 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), @@ -520,6 +521,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) { + useNewCompiler = qmlUseNewCompiler(); } QQmlEnginePrivate::~QQmlEnginePrivate() diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index c57c70fa40..b57c84ceae 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -144,6 +144,7 @@ public: QQmlContext *rootContext; bool isDebugging; + bool useNewCompiler; bool outputWarningsToStdErr; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp new file mode 100644 index 0000000000..8fdfe8e4f1 --- /dev/null +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -0,0 +1,686 @@ +/**************************************************************************** +** +** 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> + +QT_USE_NAMESPACE + +using namespace QtQml; + +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(QQmlEngine *engine, QQmlContextData *contextData, QQmlCompiledData *runtimeData) + : engine(engine) + , unit(runtimeData->qmlUnit) + , jsUnit(runtimeData->compilationUnit) + , context(contextData) + , typeNameCache(0) + , runtimeData(runtimeData) + , imports(&QQmlEnginePrivate::get(engine)->typeLoader) + , _object(0) + , _ddata(0) + , _propertyCache(0) +{ + typeNameCache = new QQmlTypeNameCache; + + QQmlImportDatabase *idb = &QQmlEnginePrivate::get(engine)->importDatabase; + + for (uint i = 0; i < unit->nImports; ++i) { + const QV4::CompiledData::Import *import = unit->importAt(i); + if (import->type == QV4::CompiledData::Import::ImportLibrary) { + imports.addLibraryImport(idb, stringAt(import->uriIndex), stringAt(import->qualifierIndex), + import->majorVersion, import->minorVersion, /*qmldir identifier*/ QString(), + /*qmldir url*/ QString(), /*incomplete*/ false, &errors); + } else { + // ### + } + } + + imports.populateCache(typeNameCache); + context->imports = typeNameCache; + context->imports->addref(); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (runtimeData->compilationUnit && !runtimeData->compilationUnit->engine) + runtimeData->compilationUnit->linkToEngine(v4); +} + +QVector<QQmlAbstractBinding*> QmlObjectCreator::setupBindings(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + int propertyWriteStatus = -1; + QVariant fallbackVariantValue; + + QVector<QQmlAbstractBinding*> createdDynamicBindings(obj->nBindings, 0); + + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + QString name = stringAt(binding->propertyNameIndex); + + if (name.isEmpty() && binding->value.type == QV4::CompiledData::Value::Type_Object) { + create(binding->value.objectIndex, _object); + continue; + } + + QQmlPropertyData *property = _propertyCache->property(name, _object, context); + + if (_ddata->hasBindingBit(property->coreIndex)) + removeBindingOnProperty(_object, property->coreIndex); + + if (binding->value.type == QV4::CompiledData::Value::Type_Script) { + QV4::Function *runtimeFunction = jsUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QV4::FunctionObject *function = new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, runtimeFunction, qmlGlobal); + QQmlBinding *binding = new QQmlBinding(QV4::Value::fromObject(function), _object, context, + QString(), 0, 0); // ### + + binding->setTarget(_object, *property, context); + binding->addToObject(); + + createdDynamicBindings[i] = binding; + binding->m_mePtr = &createdDynamicBindings[i]; + continue; + } + + void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; + + // shortcuts +#if 0 + if (property->propType == QMetaType::Double && binding->value.type == QV4::CompiledData::Value::Type_Number) { + argv[0] = const_cast<double*>(&binding->value.d); + } else if (property->propType == QMetaType::Bool && binding->value.type == QV4::CompiledData::Value::Type_Boolean) { + argv[0] = const_cast<bool*>(&binding->value.b); + } else +#endif + { + // fallback + fallbackVariantValue = variantForBinding(property->propType, binding); + + if (property->propType == QMetaType::QVariant) + argv[0] = &fallbackVariantValue; + else + argv[0] = fallbackVariantValue.data(); + } + + QMetaObject::metacall(_object, QMetaObject::WriteProperty, property->coreIndex, argv); + } + + return createdDynamicBindings; +} + +void QmlObjectCreator::setupFunctions(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal) +{ + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(_object); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + const quint32 *functionIdx = obj->functionOffsetTable(); + for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIdx) { + QV4::Function *function = jsUnit->runtimeFunctions[*functionIdx]; + const QString name = function->name->toQString(); + + QQmlPropertyData *property = _propertyCache->property(name, _object, context); + if (!property->isVMEFunction()) + continue; + + QV4::FunctionObject *v4Function = new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, function, qmlGlobal); + vme->setVmeMethod(property->coreIndex, QV4::Value::fromObject(v4Function)); + } +} + +QObject *QmlObjectCreator::create(int index, QObject *parent) +{ + const QV4::CompiledData::Object *obj = unit->objectAt(index); + + QQmlTypeNameCache::Result res = typeNameCache->query(stringAt(obj->inheritedTypeNameIndex)); + if (!res.isValid()) + return 0; + + QObject *result = res.type->create(); + // ### use no-event variant + if (parent) + result->setParent(parent); + + QQmlData *declarativeData = QQmlData::get(result, /*create*/true); + + qSwap(_object, result); + qSwap(_ddata, declarativeData); + + context->addObject(_object); + + // ### avoid _object->metaObject + QQmlPropertyCache *baseTypeCache = QQmlEnginePrivate::get(engine)->cache(_object->metaObject()); + baseTypeCache->addref(); + + QQmlPropertyCache *cache = 0; + QByteArray vmeMetaData; + bool needCustomMetaObject = createVMEMetaObjectAndPropertyCache(obj, baseTypeCache, &cache, &vmeMetaData); + cache->addref(); + + baseTypeCache->release(); + baseTypeCache = 0; + + qSwap(_propertyCache, cache); + + if (needCustomMetaObject) { + runtimeData->datas.append(vmeMetaData); + // install on _object + (void)new QQmlVMEMetaObject(_object, _propertyCache, reinterpret_cast<const QQmlVMEMetaData*>(runtimeData->datas.last().constData())); + if (_ddata->propertyCache) + _ddata->propertyCache->release(); + _ddata->propertyCache = _propertyCache; + _ddata->propertyCache->addref(); + } + + QV4::Value scopeObject = QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _object); + + QVector<QQmlAbstractBinding*> dynamicBindings = setupBindings(obj, scopeObject.asObject()); + setupFunctions(obj, scopeObject.asObject()); + + // ### do this later when requested + for (int i = 0; i < dynamicBindings.count(); ++i) { + QQmlAbstractBinding *b = dynamicBindings.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); + } + + qSwap(_propertyCache, cache); + qSwap(_ddata, declarativeData); + qSwap(_object, result); + + cache->release(); + + return result; +} + +// ### +#define COMPILE_EXCEPTION(token, desc) {} + +static QAtomicInt classIndexCounter(0); + +bool QmlObjectCreator::createVMEMetaObjectAndPropertyCache(const QV4::CompiledData::Object *obj, QQmlPropertyCache *baseTypeCache, QQmlPropertyCache **outputCache, QByteArray *vmeMetaObjectData) const +{ + if (!obj->nProperties) { + *outputCache = baseTypeCache; + return false; + } + + QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(engine, obj->nProperties, /*methodCount*/0, /*signalCount*/0); + *outputCache = 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); + QQmlType *qmltype = 0; + if (!imports.resolveType(stringAt(param->customTypeNameIndex), &qmltype, 0, 0, 0)) + COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString())); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = QQmlEnginePrivate::get(engine)->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)) { +#if 0 // ### + const QQmlScript::Object::DynamicSignal &currSig = *s; + COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); +#endif + } + 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 = unit->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)) { +#if 0 // ### + const QQmlScript::Object::DynamicSlot &currSlot = *s; + COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal")); +#endif + } + // 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 = QQmlEnginePrivate::get(engine)->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 = unit->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; +} + +QVariant QmlObjectCreator::variantForBinding(int expectedMetaType, const QV4::CompiledData::Binding *binding) const +{ + QVariant result; + + switch (expectedMetaType) { + case QMetaType::QString: + result = valueAsString(&binding->value); + break; + case QMetaType::Bool: + result = valueAsBoolean(&binding->value); + break; + case QMetaType::Double: + result = valueAsNumber(&binding->value); + break; + case QMetaType::Int: + result = (int)valueAsNumber(&binding->value); + break; + case QVariant::Color: { + bool ok = false; + result = QQmlStringConverters::colorFromString(valueAsString(&binding->value), &ok); + if (!ok) { + // ### compile error + } + break; + } + default: + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(expectedMetaType); + if (converter) { + result = converter(valueAsString(&binding->value)); + } else { + if (expectedMetaType == QMetaType::QVariant) + result = QVariant(); + else + result = QVariant(expectedMetaType, (void*)0); + } + break; + } + return result; +} + +QString QmlObjectCreator::valueAsString(const QV4::CompiledData::Value *value) const +{ + switch (value->type) { + case QV4::CompiledData::Value::Type_Script: + case QV4::CompiledData::Value::Type_String: + return stringAt(value->stringIndex); + case QV4::CompiledData::Value::Type_Boolean: + return value->b ? QStringLiteral("true") : QStringLiteral("false"); + case QV4::CompiledData::Value::Type_Number: + return QString::number(value->d); + case QV4::CompiledData::Value::Type_Invalid: + return QString(); + default: + break; + } + return QString(); +} + +double QmlObjectCreator::valueAsNumber(const QV4::CompiledData::Value *value) +{ + if (value->type == QV4::CompiledData::Value::Type_Number) + return value->d; + return 0.0; +} + +bool QmlObjectCreator::valueAsBoolean(const QV4::CompiledData::Value *value) +{ + if (value->type == QV4::CompiledData::Value::Type_Boolean) + return value->b; + return false; +} + diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h new file mode 100644 index 0000000000..6a2b28c35d --- /dev/null +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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> + +QT_BEGIN_NAMESPACE + +class QQmlAbstractBinding; + +namespace QtQml { + +struct Q_QML_EXPORT QmlObjectCreator +{ + QmlObjectCreator(QQmlEngine *engine, + // extra data/output stored in these two + QQmlContextData *contextData, + QQmlCompiledData *runtimeData); + + QObject *create(QObject *parent = 0) + { return create(unit->indexOfRootObject); } + QObject *create(int index, QObject *parent = 0); + + QList<QQmlError> errors; + + bool createVMEMetaObjectAndPropertyCache(const QV4::CompiledData::Object *obj, QQmlPropertyCache *baseTypeCache, QQmlPropertyCache **cache, QByteArray *vmeMetaObjectData) const; +private: + QString stringAt(int idx) const { return unit->header.stringAt(idx); } + + QVector<QQmlAbstractBinding *> setupBindings(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal); + void setupFunctions(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal); + + QVariant variantForBinding(int expectedMetaType, const QV4::CompiledData::Binding *binding) const; + + QString valueAsString(const QV4::CompiledData::Value *value) const; + static double valueAsNumber(const QV4::CompiledData::Value *value); + static bool valueAsBoolean(const QV4::CompiledData::Value *value); + + QQmlEngine *engine; + const QV4::CompiledData::QmlUnit *unit; + const QV4::CompiledData::CompilationUnit *jsUnit; + QQmlContextData *context; + QQmlTypeNameCache *typeNameCache; + QQmlCompiledData *runtimeData; + QQmlImports imports; + + QObject *_object; + QQmlData *_ddata; + QQmlPropertyCache *_propertyCache; +}; + +} // end namespace QtQml + +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..0fd9252754 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -66,6 +66,10 @@ QT_BEGIN_NAMESPACE +namespace QtQml { +struct QmlObjectCreator; +} + class QV8Engine; class QMetaProperty; class QQmlEngine; @@ -339,6 +343,7 @@ protected: private: friend class QQmlEnginePrivate; friend class QQmlCompiler; + friend struct QtQml::QmlObjectCreator; inline QQmlPropertyCache *copy(int reserve); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 3531e89779..cd57704771 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1900,6 +1900,7 @@ 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() @@ -2031,9 +2032,17 @@ 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) { + QQmlCodeGenerator compiler; + if (!compiler.generateFromQml(code, finalUrl(), finalUrlString(), &parsedQML)) { + setError(compiler.errors); + return; + } + } else { + if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) { + setError(scriptParser.errors()); + return; + } } m_imports.setBaseUrl(finalUrl(), finalUrlString()); @@ -2063,7 +2072,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()); @@ -2124,11 +2160,36 @@ 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) { + JSCodeGen jsCodeGen; + jsCodeGen.generateJSCodeForFunctionsAndBindings(finalUrlString(), &parsedQML); + + 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); + + 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 + } else { + QQmlCompiler compiler(&scriptParser._pool); + if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { + setError(compiler.errors()); + m_compiledData->release(); + m_compiledData = 0; + } } } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index abe545fc8f..9e71bab7b0 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -68,6 +68,7 @@ #include <private/qqmlbundle_p.h> #include <private/qflagpointer_p.h> #include <private/qqmlabstracturlinterceptor_p.h> +#include <private/qqmlcodegenerator_p.h> #include <private/qv4value_p.h> #include <private/qv4script_p.h> @@ -446,7 +447,11 @@ private: virtual void scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace); + // --- old compiler QQmlScript::Parser scriptParser; + // --- new compiler + QtQml::ParsedQML parsedQML; + // --- QList<ScriptReference> m_scripts; @@ -454,6 +459,7 @@ private: QList<TypeReference> m_types; bool m_typesResolved:1; + bool m_useNewCompiler:1; QQmlCompiledData *m_compiledData; |