aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-08-06 16:41:28 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-08 09:00:05 +0200
commit5b456c8da4462f9e11fa4da78a9e6ea86423a1e8 (patch)
treec8a24054a711d0ca4aed52ddcc480fcd25b32c91 /src/qml/compiler
parentdea991ec5b6e1e8546dfd3695c12425ee27d2ef3 (diff)
Beginning of a new qml parser
The goal is to parse QML and JavaScript binding expressions/functions in one go and generate data structures that allow for the parsing to happen in a thread and the instantiation of the object tree in another thread, just reading from the generated data structures. This will replace qqmlcompiler and the VME. This new way of loading QML is currently hidden behind the QML_NEW_COMPILER=1 environment variable. There's lots of work left to fill in the gaps in object construction, Component support, Component.onComplete, error messages, etc. etc. Change-Id: I5e40643cff169f469f0b6ce151584ffee5ca5e90 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmlcodegenerator.cpp776
-rw-r--r--src/qml/compiler/qqmlcodegenerator_p.h262
-rw-r--r--src/qml/compiler/qv4codegen.cpp10
-rw-r--r--src/qml/compiler/qv4codegen_p.h4
-rw-r--r--src/qml/compiler/qv4compileddata_p.h141
-rw-r--r--src/qml/compiler/qv4compiler.cpp17
-rw-r--r--src/qml/compiler/qv4compiler_p.h11
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp2
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp2
-rw-r--r--src/qml/compiler/qv4isel_p.cpp9
-rw-r--r--src/qml/compiler/qv4isel_p.h2
-rw-r--r--src/qml/compiler/qv4jsir.cpp4
13 files changed, 1195 insertions, 51 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)